#ifndef MLIIO_H
#define MLIIO_H

#include "mli/mliConv.h"

/////////////////////////////////////////////////////////////////////////////////////
//
// This is a set of Matlab interface routines which allow data to
// be loaded into tempus variables. The most common use is to specify
// these routines as setting expressions to run variables and
// parameters in the Runset editor. These routines may also be used
// in input and parameter setting expressions for subsystems.
//
/////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////
//
// mliLoad is used to load an mxArray and convert it to
// almost any tempus data type except for Grids.
// See mliLoadG to load a Grid.
//
// Usage: mliLoad(const char* fn, char* vn, const T dummy)
//
// where
//
// fn is the name of the mat-file from which data is to be loaded.
//    The .mat extension should be included. Use forware slashes (/)
//    rather than back-slashes to indicate directory paths.
//
// vn is the name of the variable to be loaded.
//
// dummy is a NULL variable of the type to be returned. dummy can be
//       float(), double(), int(), Complex(),
//       Vector<float>(), Vector<double>(), Vector<int>(), Vector<Complex()>(),
//       Array<float>(), Array<double>(), Array<int>(),  Array<Complex()>()
//
// If the type is omitted, then a Matlab structure (mxArray*) is returned.
//
// Examples:
//
// To load the vector "v" from the file "data.mat" located
// in the root directory, use the following expression:
//
// mliLoad("c:/data.mat", "v", Vector<float>())
//
// To load the the scalar value "f" from the file "inputs.mat" located
// in the directory in which the simulation is run, use the following
// expression:
//
// mliLoad("./inputs.mat", "f", float())
//
// To load a complete Matlab data structure, "s" from "inputs.mat" use:
//
// mxArray* struct = mliLoad("./inputs.mat", "s")
//
// template<class T>
// T mliLoad(const char* fn, char* vn, const T dummy);
//
/////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////
//
// mliLoadG is used to load an mxArray and convert it to a Grid.
//
// Usage: mliLoadG(const char* fn, char* vn, const T dummy)
//
// where
//
// fn is the name of the mat-file from which data is to be loaded.
//    The .mat extension should be included. Use forware slashes (/)
//    rather than back-slashes to indicate directory paths.
//
// vn is the name of the variable to be loaded.
//
// dummy is a NULL variable of the base type to be returned.
//       dummy can be float(), double(), Complex().
//
// The specified mat-file variable can take on two forms:
//
// 1. A structure having at least three fields: x, y, and g.
//    x is 1-dimensional vector giving the x coordinates.
//    y is 1-dimensional vector giving the y coordinates.
//    g specifies the grid values. If x is length nx and y is
//    length ny, g must be dimensioned nx x ny.
// 2. A collection of seven variables, specifying the coordinate
//    grid and the grid values. Where var is the name of the variable
//    the collection is as follows:
//    varNX is the grid size in the x direction.
//    varNY is the grid size in the y direction.
//    varDX is the grid spacing in the x direction.
//    varDY is the grid spacing in the y direction.
//    varXC is the offset of the origin in x (usually zero).
//    varYC is the offset of the origin in y (usually zero).
//
// Examples:
//
// To load the structure "grd" from the file "grids.mat" located
// in the root directory, use the following expression:
//
// mliLoadG("c:/grids.mat", "grd", float())
//
// To load the structure "grdc" from the file "grids.mat" located
// in the root directory, use the following expression:
//
// mliLoadG("c:/grids.mat", "grdc", Complex())
//
// Alternatively, if a structure "grdc" did not exist, then the same
// call would attempt to load the variables "grdc", "grdcNX", "grdcNY",
// "grdcDX", "grdcDY", "grdcXC", and "grdcYC".
//
// template<class T>
// TempGrid<T> mliLoadG(const char* fn, char* vn, const T dummy, const float crs=1.0, const float zrs=1.0);
//
/////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////
//
// mliSelect is used to load a particular element of a vector.
//
// Usage: mliSelect(const char* fn, char* vn, int ind, const T dummy)
//
// where
//
// fn is the name of the mat-file from which data is to be loaded.
//    The .mat extension should be included. Use forware slashes (/)
//    rather than back-slashes to indicate directory paths.
//
// vn is the name of the variable to be loaded and indexed.
//
// ind specifies the element of the variable to be returned.
//
// dummy is a NULL variable of the base type to be returned.
//       dummy can be int(), float(), double(), Complex()
//
// crs is an optional scale factor to be applied to the x and y coordinates.
//
// zrs is an optional scale factor to be applied to the grid values.
//
// Examples:
//
// To load the fifth element of the variable Cn2 from the file turb.mat:
//
// mliSelect("turb.mat", "cn2", 5, float())
//
// To load Cn2(iloop) from the same file:
//
// mliSelect("turb.mat", "cn2", iloop, float())
//
// template<class T>
// T mliSelect(const char* fn, char* vn, int ind, const T dummy);
//
/////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////
//
// mliSelect is used to load a particular element of a vector.
//
// Usage: mliSelect(const char* fn, char* vn, int ind, const T dummy)
//
// where
//
// fn is the name of the mat-file from which data is to be loaded.
//    The .mat extension should be included. Use forware slashes (/)
//    rather than back-slashes to indicate directory paths.
//
// vn is the name of the string array variable to be loaded and indexed
//    to determine the name of the grid variable to be loaded. 
//
// ind specifies the index into the string array to be used to
//     determine the name of the grid variable to be loaded.
//     In Matlab notation, vn(ind,:) is used as the variable name.
//
// dummy is a NULL variable of the base type to be returned.
//       dummy can be int(), float(), double(), Complex()
//
// crs is an optional scale factor to be applied to the x and y coordinates.
//
// zrs is an optional scale factor to be applied to the grid values.
//
// Examples:
//
// To load the fifth element of the variable Cn2 from the file turb.mat:
//
// mliSelect("turb.mat", "cn2", 5, float())
//
// To load Cn2(iloop) from the same file:
//
// mliSelect("turb.mat", "cn2", iloop, float())
//
// template<class T>
// TempGrid<T> mliSelectG(const char* fn, char* vn, int ind, const T dummy, const float crs=1.0, const float zrs=1.0);
//
/////////////////////////////////////////////////////////////////////////////////////

//
// End of comments for the user. Only code follows.
//

/////////////////////////////////////////////////////////////////////////////////////


MLI_API void mliLoadGridError(const char* fn, const char* vn);

#ifndef NO_MATLAB

template<class T>
T mliLoad(const char* fn, const char* vn, const T dummy)
{
   MATFile* mfp = matOpen(fn, "r");
   if (mfp == NULL)
   {
      char msg[MLICONV_ERRSTR_LEN];
      sprintf(msg, "Cannot open MATFile [%s] for reading variable [%s]", fn, vn);
      mliConvErrMsg(msg);
   }
   mxArray* mxp = matGetVariable(mfp, vn);
   if (mxp == NULL)
   {
      char msg[MLICONV_ERRSTR_LEN];
      sprintf(msg, "Cannot read variable [%s] from MATFile [%s]", vn, fn);
      mliConvErrMsg(msg);
   }
   int ierr = matClose(mfp);
   if (ierr != 0)
   {
      char msg[MLICONV_ERRSTR_LEN];
      sprintf(msg, "Trouble closing MATFile [%s] after reading variable [%s]", fn, vn);
      mliConvWarnMsg(msg);
   }
   T t;
   ierr = fromMxArray(mxp, t);
   if (ierr != 0)
   {
      char msg[MLICONV_ERRSTR_LEN];
      sprintf(msg, "Cannot convert variable [%s] from MATFile [%s] to the proper type", vn, fn);
      mliConvErrMsg(msg);
   }
   mxDestroyArray(mxp);
   return t;
}

template<class T>
Grid<T> mliLoad(const char* fn, const char* vn, const Grid<T> dummy)
{
   mliLoadGridError(fn, vn);
   return Grid<T>();
}

MLI_API mxArray* mliLoad(const char* fn, const char* vn);

template<class T>
TempGrid<T> mliLoadG(const char* fn, const char* vn, const T dummy, const float crs=1.0f, const float zrs=1.0)
{
   MATFile* mfp = matOpen(fn, "r");
   if (mfp == NULL)
   {
      char msg[MLICONV_ERRSTR_LEN];
      sprintf(msg, "Cannot open MATFile [%s] for reading variable [%s]", fn, vn);
      mliConvErrMsg(msg);
   }
   mxArray* mxp = matGetVariable(mfp, vn);
   if (mxp == NULL)
   {
      char msg[MLICONV_ERRSTR_LEN];
      sprintf(msg, "Cannot read variable [%s] from MATFile [%s]", vn, fn);
      mliConvErrMsg(msg);
   }
   int ierr = matClose(mfp);
   if (ierr != 0)
   {
      char msg[MLICONV_ERRSTR_LEN];
      sprintf(msg, "Trouble closing MATFile [%s] after reading variable [%s]", fn, vn);
      mliConvWarnMsg(msg);
   }
   if (!mxIsStruct(mxp))
   {
      char vnp[MLICONV_ERRSTR_LEN];
      strcpy(vnp, vn); strcat(vnp, "DX");
      float dx = mliLoad(fn, vnp, float());
      strcpy(vnp, vn); strcat(vnp, "DY");
      float dy = mliLoad(fn, vnp, float());
      strcpy(vnp, vn); strcat(vnp, "XC");
      float xc = mliLoad(fn, vnp, float());
      strcpy(vnp, vn); strcat(vnp, "YC");
      float yc = mliLoad(fn, vnp, float());
      TempArray<T> ta;
      fromMxArray(mxp, ta);
      TempGrid<T> t(ta, dx*(ta.dim(0)-1), dy*(ta.dim(1)-1), xc, yc);
      if (crs != 1.0) t.rescale(crs);
      if (zrs != 1.0) t *= zrs;
      mxDestroyArray(mxp);
      return t;
   }
   else
   {
      TempGrid<T> t;
      ierr = fromMxArray(mxp, t);
      if (ierr != 0)
      {
         char msg[MLICONV_ERRSTR_LEN];
         sprintf(msg, "Cannot convert variable [%s] from MATFile [%s] to the proper type", vn, fn);
         mliConvErrMsg(msg);
      }
      if (crs != 1.0) t.rescale(crs);
      if (zrs != 1.0) t *= zrs;
      mxDestroyArray(mxp);
      return t;
   }
}

MLI_API double mliSelect(const char* fn, int ind, const char* vn);

MLI_API double mliSelect(const char* fn, int ind0, int ind1, const char* vn);

template<class T>
T mliSelect(const char* fn, const char* vn, int ind, const T dummy)
{  
   mxArray* mxdum;
   mxArray* mxary = mliLoad(fn, vn, mxdum);
   char* vni = NULL;
   fromMxArray(mxary, vni, ind);
   mxDestroyArray(mxary);
   T t(mliLoad(fn, vni, dummy));
   free(vni);
   return t;
}

template<class T>
TempGrid<T> mliSelectG(const char* fn, const char* vn, int ind, const T dummy, const float crs=1.0, const float zrs=1.0)
{
   mxArray* mxdum;
   mxArray* mxary = mliLoad(fn, vn, mxdum);
   char* vni = NULL;
   fromMxArray(mxary, vni, ind);
   mxDestroyArray(mxary);
   TempGrid<T> t(mliLoadG(fn, vni, dummy, crs, zrs));
   free(vni);
   return t;
}

template<class T>
T mliSelectC(const char* fn, const char* vn, int ind, const T dummy)
{
   if (ind < 0)
   {
      mliConvErrMsg("T mliSelectC(...) called with negative index");
   }
   mxArray* mxdum;
   mxArray* mxaryc = mliLoad(fn, vn, mxdum);
   if (!mxIsCell(mxaryc))
   {
      mliConvErrMsg("T mliSelectC(...) called with for non-cell array");
   }
   if (ind >= mxGetNumberOfElements(mxaryc))
   {
      mliConvErrMsg("T mliSelectC(...) called with index exceeding the number of elements");
   }
   mxArray* mxary = mxGetCell(mxaryc, ind+1);
   // T t(); XLY
	T t;
   fromMxArray(mxary, t);
   mxDestroyArray(mxaryc);
   return t;
}

template<class T>
TempGrid<T> mliSelectGC(const char* fn, const char* vn, int ind, const T dummy, const float crs=1.0, const float zrs=1.0)
{
   if (ind < 0)
   {
      mliConvErrMsg("T mliSelectGC(...) called with negative index");
   }
   mxArray* mxdum;
   mxArray* mxaryc = mliLoad(fn, vn, mxdum);
   if (!mxIsCell(mxaryc))
   {
      mliConvErrMsg("T mliSelectGC(...) called with for non-cell array");
   }
   if (ind >= mxGetNumberOfElements(mxaryc))
   {
      mliConvErrMsg("T mliSelectGC(...) called with index exceeding the number of elements");
   }
   mxArray* mxary = mxGetCell(mxaryc, ind+1);
   TempGrid<T> t;
   fromMxArray(mxary, t);
   mxDestroyArray(mxaryc);
   if (crs != 1.0) t.rescale(crs);
   if (zrs != 1.0) t *= zrs;
   return t;
}
#endif

#endif // MLIIO_H
