#include "machdefs.h"
#include <stdlib.h>
#include <typeinfo>
#include <iostream>
#include <string>
#include <complex>

#include "datatype.h"

#include "tvector.h"
#include "objfitter.h"
#include "nomtmatvecadapter.h"
#include "piyfxdrw.h"
#include "pitvmaad.h"

#include "fioarr.h"
#include "fitstarray.h"


//----------------------------------------------------------------
// Class Adaptateur d'objet (Pour NamedObjMgr) d'objet TMatrix<T> 
//----------------------------------------------------------------


/* --Methode-- */
template <class T> 
NOMAdapter_TMatrix<T>::NOMAdapter_TMatrix(TMatrix<T>* o)
  : NObjMgrAdapter(o)
{
mMtx = o;
}

/* --Methode-- */
template <class T> 
NOMAdapter_TMatrix<T>::~NOMAdapter_TMatrix()
{
}

/* --Methode-- */
template <class T> 
NObjMgrAdapter* NOMAdapter_TMatrix<T>::Clone(AnyDataObj* o)
{
TMatrix<T>* m = dynamic_cast<TMatrix<T> *>(o);
if (m) return ( new NOMAdapter_TMatrix<T>(m) );
return ( new NObjMgrAdapter(o) );
}

/* --Methode-- */
template <class T> 
string NOMAdapter_TMatrix<T>::GetDataObjType()
{
string type = "TMatrix< ";

TVector<T>* v = dynamic_cast<TVector<T> *>(mMtx);
if (v != NULL) type = "TVector< ";

// type +=  DecodeTypeIdName(typeid(T).name());
type += DataTypeInfo<T>::getTypeName();
type +=  " > ";
return(type);
}

/* --Methode-- */
template <class T> 
AnyDataObj* NOMAdapter_TMatrix<T>::CloneDataObj(bool share)
{
if (mMtx == NULL)  return(NULL);
TVector<T>* v = dynamic_cast<TVector<T> *>(mMtx);
if (v != NULL) return( new TVector<T>(*v, share) );
else return ( new TMatrix<T>(*mMtx, share) );
}

/* --Methode-- */
template <class T> 
void NOMAdapter_TMatrix<T>::ReadFits(string const & flnm)
{
  FitsInFile fis(flnm);
  fis >> (*mMtx);
}

/* --Methode-- */
template <class T> 
void NOMAdapter_TMatrix<T>::SaveFits(string const & flnm)
{
  FitsOutFile fos(flnm);
  fos << (*mMtx);
}
// ---- Specialisation pour complexes -----
DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
void NOMAdapter_TMatrix< complex<r_4> >::ReadFits(string const & flnm)
{
cout << " NOMAdapter_TMatrix< complex<r_4> >::ReadFits() - Error " 
     << " Not supported (complex data type)" << endl;
}
DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
void NOMAdapter_TMatrix< complex<r_4> >::SaveFits(string const & flnm)
{
cout << " NOMAdapter_TMatrix< complex<r_4> >::SaveFits() - Error " 
     << " Not supported (complex data type)" << endl;
}

DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
void NOMAdapter_TMatrix< complex<r_8> >::ReadFits(string const & flnm)
{
cout << " NOMAdapter_TMatrix< complex<r_8> >::ReadFits() - Error " 
     << " Not supported (complex data type)" << endl;
}
DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
void NOMAdapter_TMatrix< complex<r_8> >::SaveFits(string const & flnm)
{
cout << " NOMAdapter_TMatrix< complex<r_8> >::SaveFits() - Error " 
     << " Not supported (complex data type)" << endl;
}

/* --Methode-- */
template <class T> 
void NOMAdapter_TMatrix<T>::SavePPF(POutPersist& pos, string const & nom)
{
if (mMtx == NULL)  return;
TVector<T>* v = dynamic_cast<TVector<T> *>(mMtx);
if (v != NULL) {
  FIO_TArray<T> fio(v);
  fio.Write(pos, nom);
  }
else {
  FIO_TArray<T> fio(mMtx);
  fio.Write(pos, nom);
  }
}

/* --Methode-- */
template <class T> 
void NOMAdapter_TMatrix<T>::Print(ostream& os)
{
TVector<T>* v = dynamic_cast<TVector<T> *>(mMtx);
if (v != NULL)  os << (*v);
else os << (*mMtx);
}

/* --Methode-- */
template <class T> 
PIDrawer* NOMAdapter_TMatrix<T>::GetDrawer(string & dopt)
{
TVector<T>* v = dynamic_cast<TVector<T> *>(mMtx);
if (v == NULL) return(NULL);
else {
  dopt = "thinline " + dopt;
  return( new PIYfXDrawer( new POTVectorAdapter<T>(v, false), NULL, true) );
  }
}

/* --Methode-- */
template <class T> 
P2DArrayAdapter* NOMAdapter_TMatrix<T>::Get2DArray(string &)
{
return ( new POTMatrixAdapter<T>(mMtx, false) );
}

/* --Methode-- */
template <class T> 
NTupleInterface* NOMAdapter_TMatrix<T>::GetNTupleInterface(bool& adel)
{
adel = true;
return( new NTupInt_TMatrix<T>(mMtx) );
}


/* --Methode-- */
template <class T> 
GeneralFitData* NOMAdapter_TMatrix<T>::GetGeneralFitData(bool& adel
 ,GeneralFitData::FitErrType errtype,double errscale,double errmin
 ,int i1,int i2,int j1,int j2)
{
adel = false;
if(!mMtx) return(NULL);

int nx,ny;
TVector<T>* vec = dynamic_cast<TVector<T> *>(mMtx);

if(vec) {  // C'est un TVector
  nx = vec->NElts();
  ny = 1;
} else {
  nx = mMtx->NRows();
  ny = mMtx->NCol();
  if(nx<=0 || ny<=0) return(NULL);
}

i1 = (i1<0||i1>=nx)? 0: i1;
i2 = (i2<0||i2>=nx||i2<i1)? nx-1: i2;
j1 = (j1<0||j1>=ny)? 0: j1;
j2 = (j2<0||j2>=ny||j2<j1)? ny-1: j2;

GeneralFitData* mGData = NULL;

if(vec) {  // C'est un TVector
  mGData = new GeneralFitData(1,(i2-i1+1),0);
  adel = true;
  for(int i=i1;i<=i2;i++) {
    double x = (double) i;
    double f = (*vec)(i);
    double e = 1.;
    e = GeneralFitData::ComputeError(f,e,errtype,errscale,errmin);
    mGData->AddData1(x,f,e);
  }
} else {
  mGData = new GeneralFitData(2,(i2-i1+1)*(j2-j1+1),0);
  adel = true;
  for(int i=i1;i<=i2;i++) for(int j=j1;j<=j2;j++) {
    double x = (double) i;
    double y = (double) j;
    double f = (*mMtx)(i,j);
    double e = 1.;
    e = GeneralFitData::ComputeError(f,e,errtype,errscale,errmin);
    mGData->AddData2(x,y,f,e);
  }
}

return mGData;
}

template <class T> 
AnyDataObj* NOMAdapter_TMatrix<T>::FitResidusObj(GeneralFit& mfit)
{
TVector<T>* vec = dynamic_cast<TVector<T> *>(mMtx);
if(vec) {
  TVector<T>* v = new TVector<T>(ObjectFitter::FitResidus(*vec,mfit),true);
  return v;
} else {
  TMatrix<T>* m = new TMatrix<T>(ObjectFitter::FitResidus(*mMtx,mfit),true);
  return m;
}
}

template <class T> 
AnyDataObj* NOMAdapter_TMatrix<T>::FitFunctionObj(GeneralFit& mfit)
{
TVector<T>* vec = dynamic_cast<TVector<T> *>(mMtx);
if(vec) {
  TVector<T>* v = new TVector<T>(ObjectFitter::FitFunction(*vec,mfit),true);
  return v;
} else {
  TMatrix<T>* m = new TMatrix<T>(ObjectFitter::FitFunction(*mMtx,mfit),true);
  return m;
}
}

// ---- Specialisation pour complexes -----
DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
GeneralFitData* NOMAdapter_TMatrix< complex<r_4> >::GetGeneralFitData(bool& adel
 ,GeneralFitData::FitErrType errtype,double errscale,double errmin
 ,int i1,int i2,int j1,int j2)
{
  return(NULL);
}

DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
AnyDataObj* NOMAdapter_TMatrix< complex<r_4> >::FitResidusObj(GeneralFit& mfit)
{
  return(NULL);
}

DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
AnyDataObj* NOMAdapter_TMatrix< complex<r_4> >::FitFunctionObj(GeneralFit& mfit)
{
  return(NULL);
}

DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
GeneralFitData* NOMAdapter_TMatrix< complex<r_8> >::GetGeneralFitData(bool& adel
 ,GeneralFitData::FitErrType errtype,double errscale,double errmin
 ,int i1,int i2,int j1,int j2)
{
  return(NULL);
}

DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
AnyDataObj* NOMAdapter_TMatrix< complex<r_8> >::FitResidusObj(GeneralFit& mfit)
{
  return(NULL);
}

DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
AnyDataObj* NOMAdapter_TMatrix< complex<r_8> >::FitFunctionObj(GeneralFit& mfit)
{
  return(NULL);
}

// -------------------------------------------------------------

/* --Methode-- */
template <class T> 
NTupInt_TMatrix<T>::NTupInt_TMatrix(TMatrix<T>* m)
{
mMtx = m;
}

/* --Methode-- */
template <class T> 
NTupInt_TMatrix<T>::~NTupInt_TMatrix()
{
}

/* --Methode-- */
template <class T> 
uint_4 NTupInt_TMatrix<T>::NbLines() const
{
return( mMtx->NRows()*mMtx->NCols() );
}

/* --Methode-- */
template <class T> 
uint_4 NTupInt_TMatrix<T>::NbColumns() const 
{
return(8);
}

/* --Methode-- */
template <class T> 
r_8* NTupInt_TMatrix<T>::GetLineD(int n) const 
{
int i,j;
if ((n < 0) || (n >= (int)(mMtx->NRows()*mMtx->NCols()) )) {
  mRet[0] = n;
  for(i=1; i<8; i++)  mRet[i] = 0.;
}
else { 
  i = n/mMtx->NCols(); j = n%mMtx->NCols(); 
  mRet[0] = n;  mRet[1] = i;  mRet[2] = j;  
  mRet[3] = (*mMtx)(i,j);
  mRet[4] = mRet[2];  mRet[5] = 0.;
  mRet[6] = mRet[2];  mRet[7] = 0.;
  }
return(mRet);
}

/* --Methode-- */
template <class T> 
string NTupInt_TMatrix<T>::VarList_C(const char* nx) const 
{
string nomx;
if (nx) nomx = nx;
else nomx = "_xh_";
string vardec = "double n,r,c,val,real,imag,mod,phas; \n";
vardec += "n = " + nomx + "[0];  r = " + nomx + "[1]; c = " + nomx + "[2]; \n"; 
vardec += "val = " + nomx + "[3]; \n";
vardec += "real = " + nomx + "[4];  imag = " + nomx + "[5]; \n";
vardec += "mod  = " + nomx + "[6];  phas = " + nomx + "[7]; \n";
return(vardec);
}

/* --Methode-- */
DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
r_8* NTupInt_TMatrix< complex<r_4> >::GetLineD(int n) const 
{
int i,j;
if ((n < 0) || (n >= (int)(mMtx->NRows()*mMtx->NCols()) )) {
  mRet[0] = n;
  for(i=1; i<8; i++)  mRet[i] = 0.;
}
else { 
  i = n/mMtx->NCols(); j = n%mMtx->NCols(); 
  mRet[0] = n;   mRet[1] = i;  mRet[2] = j;  
  mRet[4] = (*mMtx)(i,j).real();  mRet[5] = (*mMtx)(i,j).imag();
  mRet[3] = mRet[6] = sqrt(mRet[4]*mRet[4]+mRet[5]*mRet[5]);
  mRet[7] = atan2(mRet[5], mRet[4]);
}
return(mRet);
}

DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
r_8* NTupInt_TMatrix< complex<r_8> >::GetLineD(int n) const 
{
int i,j;
if ((n < 0) || (n >= (int)(mMtx->NRows()*mMtx->NCols()) )) {
  mRet[0] = n;
  for(i=1; i<8; i++)  mRet[i] = 0.;
}
else { 
  i = n/mMtx->NCols(); j = n%mMtx->NCols(); 
  mRet[0] = n;   mRet[1] = i;  mRet[2] = j;  
  mRet[4] = (*mMtx)(i,j).real();  mRet[5] = (*mMtx)(i,j).imag();
  mRet[3] = mRet[6] = sqrt(mRet[4]*mRet[4]+mRet[5]*mRet[5]);
  mRet[7] = atan2(mRet[5], mRet[4]);
}
return(mRet);
}


#ifdef __CXX_PRAGMA_TEMPLATES__
//#pragma define_template NOMAdapter_TMatrix<uint_2>
//#pragma define_template NOMAdapter_TMatrix<int_2>
#pragma define_template NOMAdapter_TMatrix<int_4>
#pragma define_template NOMAdapter_TMatrix<r_4>
#pragma define_template NOMAdapter_TMatrix<r_8>
#pragma define_template NOMAdapter_TMatrix< complex<r_4> >
#pragma define_template NOMAdapter_TMatrix< complex<r_8> >
//#pragma define_template NTupInt_TMatrix<uint_2>
//#pragma define_template NTupInt_TMatrix<int_2>
#pragma define_template NTupInt_TMatrix<int_4>
#pragma define_template NTupInt_TMatrix<r_4>
#pragma define_template NTupInt_TMatrix<r_8>
#pragma define_template NTupInt_TMatrix< complex<r_4> >
#pragma define_template NTupInt_TMatrix< complex<r_8> >
#endif

#if defined(ANSI_TEMPLATES) 
//template class NOMAdapter_TMatrix<uint_2>;
//template class NOMAdapter_TMatrix<int_2>;
template class NOMAdapter_TMatrix<int_4>;
template class NOMAdapter_TMatrix<r_4>;
template class NOMAdapter_TMatrix<r_8>;
template class NOMAdapter_TMatrix< complex<r_4> >;
template class NOMAdapter_TMatrix< complex<r_8> >;
// template class NTupInt_TMatrix<uint_2>;
// template class NTupInt_TMatrix<int_2>;
template class NTupInt_TMatrix<int_4>;
template class NTupInt_TMatrix<r_4>;
template class NTupInt_TMatrix<r_8>;
template class NTupInt_TMatrix< complex<r_4> >;
template class NTupInt_TMatrix< complex<r_8> >;
#endif
