/* Fesslix - Stochastic Analysis
 * Copyright (C) 2010-2016 Wolfgang Betz
 *
 * Fesslix is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Fesslix is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Fesslix.  If not, see <http://www.gnu.org/licenses/>. 
 */

#include "flxobjrf.h"
#include "flxobjcommon.h"
#include "flxobjmtx.h"
#include "flxobjfem.h"



void FlxObjRndRfCreate::task()
{
  tuint ui = index->cast2tuintW0(false);
  RFbox_get().insert(ui,rG->generate_RF(ui,false));
}

FlxObjBase* FlxObjReadRndRfCreate::read()
{
  FlxFunction* index = new FlxFunction(funReader,false);
  try {
    std::string rfStr = reader->getWord(true,false);
    FlxRF_Generator_base* rndG = FlxRF_Generator_base::createRF(FlxRF_Generator_base::get_rft(rfStr,false));
    read_optionalPara(false);
    return new FlxObjRndRfCreate(get_doLog(),index, rndG);
  } catch ( FlxException &e) {
    FLXMSG("FlxObjReadRndRfCreate::read",1);
    delete index;
    throw;
  }
}

void FlxObjRndRfElSet::task()
{
  const tuint rfindex = index->cast2tuintW0(false);
  const tuint elindexI = elindex->cast2tuintW0(false);
  flxDomain_base* db = NULL;
  flxElement_KL_base* kb = NULL;
  std::ostringstream ssV;
  switch (typeEL) {
    case flxDomain_base::geoel_edge:
    {
      flxEdge* et = FEMbox_get()->EdgeList.get(elindexI,true);
      FlxKLelementProp_1D* KLelProp_1D = dynamic_cast<FlxKLelementProp_1D*>(KLelProp);
      #if FLX_DEBUG
	if (KLelProp_1D==NULL) throw FlxException_Crude("FlxObjRndRfElSet::task_1");
      #endif
      kb = et->create_KL_element(KLelProp_1D);
      db = et;
      ssV << "edge";
      break;
    }
    case flxDomain_base::geoel_face:
    {
      flxFace* et = FEMbox_get()->FaceList.get(elindexI,true);
      FlxKLelementProp_2D* KLelProp_2D = dynamic_cast<FlxKLelementProp_2D*>(KLelProp);
      #if FLX_DEBUG
	if (KLelProp_2D==NULL) throw FlxException_Crude("FlxObjRndRfElSet::task_2");
      #endif
      kb = et->create_KL_element(KLelProp_2D);
      db = et;
      ssV << "face";
      break;
    }
    case flxDomain_base::geoel_box:
    {
      flxBox* et = FEMbox_get()->BoxList.get(elindexI,true);
      FlxKLelementProp_3D* KLelProp_3D = dynamic_cast<FlxKLelementProp_3D*>(KLelProp);
      #if FLX_DEBUG
	if (KLelProp_3D==NULL) throw FlxException_Crude("FlxObjRndRfElSet::task_3");
      #endif
      kb = et->create_KL_element(KLelProp_3D);
      db = et;
      ssV << "box";
      break;
    }
    default:
      throw FlxException_Crude("FlxObjRndRfElSet::task_4");
  }
  try {
    ssV << " " << elindexI;
    FlxRandomField_base* rfb = RFbox_get().get(rfindex,false);
    rfb->set_element(db,kb,true,rfindex,ssV.str());
  } catch (FlxException& e) {
    FLXMSG("FlxObjRndRfElSet::task_5",1);
    if (kb) delete kb;
    throw;
  }
}

FlxObjBase* FlxObjReadRndRfElSet::read()
{
  FlxFunction* index = new FlxFunction(funReader,false);
  FlxFunction* elindex = NULL;
  FlxKLelementProp_base* KLelProp = NULL;
  try {
    const flxDomain_base::geoel_type gelt = flxDomain_base::get_geoel_type(reader->getWord(true,false),false);
    elindex = new FlxFunction(funReader,false);
    switch (gelt) {
      case flxDomain_base::geoel_edge:
	KLelProp = new FlxKLelementProp_1D(false);
	break;
      case flxDomain_base::geoel_face:
	KLelProp = new FlxKLelementProp_2D(false);
	break;
      case flxDomain_base::geoel_box:
	KLelProp = new FlxKLelementProp_3D(false);
	break;
      default:
	throw FlxException_NotImplemented("FlxObjReadRnd::read_8");
    }
    read_optionalPara(false);
    return new FlxObjRndRfElSet(get_doLog(),index, gelt,elindex, KLelProp);
  } catch ( FlxException &e) {
    FLXMSG("FlxObjReadRndRfElSet::read",1);
    delete index;
    if (elindex!=NULL) delete elindex;
    if (KLelProp!=NULL) delete KLelProp;
    throw;
  }
}

void FlxObjRndRfSolve::task()
{
  const tuint idx = index->cast2tuintW0(false);
  FlxRandomField_base* rf = RFbox_get().get(idx,false);
  GlobalVar.slogcout(3) << "Random field: solve field with index '" << idx << "'." << std::endl;
  const tuint DOF = rf->solve(RFsolveProp,sout());
  GlobalVar.slogcout(3) << "Random field: assembled random field with index '" << idx << "'. (" << DOF << " DOFs)" << std::endl;
  data->ConstantBox.insert("g_solver_ndof",tdouble(DOF));
  if (RFsolveProp->get_calcerr()) GlobalVar.slogcout(3) << "  mean-error(1)=" << GlobalVar.Double2String(rf->get_domain_error(1)) << std::endl;
}

FlxObjBase* FlxObjReadRndRfSolve::read()
{
  FlxFunction* index = new FlxFunction(funReader,false);
  FlxRFsolveProp* RFsolveProp = NULL;
  try {
    RFsolveProp = new FlxRFsolveProp(false);
    read_optionalPara(false);
    return new FlxObjRndRfSolve(get_doLog(),index,RFsolveProp,get_stream(),get_verbose());
  } catch ( FlxException &e) {
    FLXMSG("FlxObjReadRndRfSolve::read",1);
    delete index;
    if (RFsolveProp!=NULL) delete RFsolveProp;
    throw;
  }
}

void FlxObjRndRfErr::task()
{
  const tuint idx = index->cast2tuintW0(false);
  const int erM = errMeasure->cast2int();
  FlxRandomField_base* rf = RFbox_get().get(idx,false);
  sout() << "RF-error(field=" << GlobalVar.Double2String(idx) << ",measure=" << GlobalVar.Double2String(erM) << ") = " ;
  sout() << GlobalVar.Double2String(rf->get_domain_error(erM)) << std::endl;
}

FlxObjReadRndRfErr::FlxObjReadRndRfErr(): FlxObjReadOutputBase(true)
{
  // errmeasure
    AllDefParaBox->insert(new FlxOptionalParaFun(ONE,"rfplot::errmeasure"));
    ParaBox.insert("errmeasure", "rfplot::errmeasure" );  
}

FlxObjBase* FlxObjReadRndRfErr::read()
{
  FlxFunction* index = new FlxFunction(funReader,false);
  try {
    read_optionalPara(false);
    return new FlxObjRndRfErr(get_doLog(),index,get_optPara_FlxFunction("errmeasure"),get_stream(),get_verbose());
  } catch (FlxException &e) {
    FLXMSG("FlxObjReadRndRfErr::read",1);
    delete index;
    throw;
  }
}

void FlxObjRndRfPlot::task()
{
  const tuint idx = index->cast2tuintW0(false);
  FlxRandomField_base* rf = RFbox_get().get(idx,false);
  const int erM = errMeasure->cast2int();
  // write the header
    FlxObjFunPlot_header::write_entry("x",sout(),prec,fixW,true);
    FlxObjFunPlot_header::write_entry("y",sout(),prec,fixW,false);
    FlxObjFunPlot_header::write_entry("z",sout(),prec,fixW,false);
    FlxObjFunPlot_header::write_entry("err",sout(),prec,fixW,false);
    for (tuint i=0;i<rf->get_DIM();++i) {
      std::ostringstream ssV;
      ssV << "EM_" << i;
      FlxObjFunPlot_header::write_entry(ssV.str(),sout(),prec,fixW,false);
    }
    sout() << std::endl;
  rf->plot(sout(),ppdim,erM,prec,fixW,checkTOL);
}

FlxObjReadRndRfPlot::FlxObjReadRndRfPlot(): FlxObjReadOutputBase(true)
{
  // errmeasure
    AllDefParaBox->insert(new FlxOptionalParaFun(ONE,"rfplot::errmeasure"));
    ParaBox.insert("errmeasure", "rfplot::errmeasure" );  
  // ppdim
    ParaBox.insert("ppdim", "flxfem::elplot::pointsperdim" );
}

FlxObjBase* FlxObjReadRndRfPlot::read()
{
  FlxFunction* index = new FlxFunction(funReader,false);
  try {
    read_optionalPara(false);
    return new FlxObjRndRfPlot(get_doLog(),index,get_optPara_FlxMtxFun("ppdim"),get_optPara_FlxFunction("errmeasure"),get_stream(),get_verbose(),get_prec(),get_fixW(),get_checkTOL());
  } catch (FlxException &e) {
    FLXMSG("FlxObjReadRndRfPlot::read",1);
    delete index;
    throw;
  }
}

void FlxObjRndRfPlotCov::task()
{
  const tuint idx = index->cast2tuintW0(false);
  FlxRandomField_base* rf = RFbox_get().get(idx,false);
  flxPoint pp =  p->calc();
  // write the header
    FlxObjFunPlot_header::write_entry("x",sout(),prec,fixW,true);
    FlxObjFunPlot_header::write_entry("y",sout(),prec,fixW,false);
    FlxObjFunPlot_header::write_entry("z",sout(),prec,fixW,false);
    FlxObjFunPlot_header::write_entry("cov",sout(),prec,fixW,false);
    FlxObjFunPlot_header::write_entry("cov_analytical",sout(),prec,fixW,false);
    FlxObjFunPlot_header::write_entry("diff:cov_anal-cov",sout(),prec,fixW,false);
    sout() << std::endl;
  rf->plotcov(sout(),pp,ppdim,prec,fixW);
}

FlxObjReadRndRfPlotCov::FlxObjReadRndRfPlotCov(): FlxObjReadOutputBase(true)
{
  // ppdim
    ParaBox.insert("ppdim", "flxfem::elplot::pointsperdim" );
}

FlxObjBase* FlxObjReadRndRfPlotCov::read()
{
  FlxFunction* index = new FlxFunction(funReader,false);
  FlxFunPoint* p = NULL;
  try {
    p = new FlxFunPoint(reader, funReader,false);
    read_optionalPara(false);
    return new FlxObjRndRfPlotCov(get_doLog(),index,p,get_optPara_FlxMtxFun("ppdim"),get_stream(),get_verbose(),get_prec(),get_fixW());
  } catch (FlxException &e) {
    FLXMSG("FlxObjReadRndRfPlotCov::read",1);
    delete index;
    if (p!=NULL) delete p;
    throw;
  }
}

const std::string FunRF::write()
{
  std::ostringstream ssV;
  ssV << "rf(" << RFindex << ")";
  return ssV.str();
}

FunBase* FunReadFunRF::read(bool errSerious)
{
  FunBase* fb = FunctionList->read(errSerious);
  try {
    const tuint RFindex = tuint_from(fb->calc(),"Index of random field",errSerious,true,fb);
    delete fb; fb = NULL;
    FlxRandomField_base* rfb = RFbox_get().get(RFindex,errSerious);
    if (rfb->is_automatic()) {
      FlxError(errSerious,"FunReadFunRF::read_1", "Random field is an automatic random field and must not be accessed via 'rf'." );
    }
    return new FunRF( RFindex, rfb );
  } catch (FlxException& e) {
    FLXMSG("FunReadFunRF::read_2",1);
    if (fb) delete fb;
    throw;
  }
}

const tdouble FunRFerr::calc()
{
  const tuint idx = tuint_from(ParaListP[0]->calc(),"Index of random field",true,true,ParaListP[0]);
  const int erM = int(round_flx(ParaListP[1]->calc()));
  FlxRandomField_base* rf = RFbox_get().get(idx);
  return rf->get_domain_error(erM);
}

void FlxObjMtxConstRfElIdVec::task()
{
  const tuint id = rfID->cast2tuintW0(false);
  FlxRandomField_base* rf = RFbox_get().get(id,true);
  flxVec* mvec = NULL;
  rf->assignElementIDs(mvec,typeEL,FEMbox_get());
  std::sort(mvec->get_tmp_vptr(),mvec->get_tmp_vptr()+mvec->get_N());
  tuint N = mvec->get_N();
  flxVec rhs(data->ConstMtxBox.get_Vec(mcn_target->eval(),N),N);
  rhs = *mvec;
  delete mvec;
}

FlxObjBase* FlxObjReadMtxConstRfElIdVec::read()
{
  FlxMtxConstFun* mcn_target = new FlxMtxConstFun(false);
  FlxFunction* rfID = NULL;
  try {
    reader->getChar('(');
    rfID = new FlxFunction(funReader,false);
    reader->getChar(',');
    const flxDomain_base::geoel_type gelt = flxDomain_base::get_geoel_type(reader->getWord(true,false),false);
    reader->getChar(')');
    read_optionalPara(false);
    return new FlxObjMtxConstRfElIdVec(get_doLog(),mcn_target,rfID,gelt);
  } catch (FlxException& e) {
    FLXMSG("FlxObjReadMtxConstRfElIdVec::read",1);
    delete mcn_target;
    if (rfID) delete rfID;
    throw;
  }
}

const tdouble FunRFeigenvalue::calc()
{
  const tuint i = tuint_from(ParaListP[0]->calc(),"The index of the corresponding random field",true,true,ParaListP[0]);
  const tuint j = tuint_from(ParaListP[1]->calc(),"The index of the corresponding eigenvalue",true,true,ParaListP[1]);
  return RFbox_get().get(i,false)->get_Eigenvalue(j-1,false);
}


