/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

//  Methods for the MacroConverter classes
//
//
#include "inc_iostream.h"
#include <MvRequestUtil.hpp>
#include <MvPath.hpp>
#include "MacroConverter.h"
#include "ObjectInfo.h"
#include "ObjectList.h"

// All the instances
DefaultMacroConverter defaultConverterInstance("Default");
ReadMacroConverter readConverterInstance("READ");

// Family converter
FamilyMacroConverter familyConverterInstance("FAMILY");
CurveFamilyMacroConverter curvefamilyConverterInstance("CURVES_FAMILY");
FamilyMacroConverter divrotfamilyConverterInstance("DIVROT_FAMILY");
FamilyMacroConverter vectorsfamilyConverterInstance("VECTORS_FAMILY");
FamilyMacroConverter spectrafamilyConverterInstance("SPECTRA_FAMILY");
FamilyMacroConverter pottfamilyConverterInstance("POTT_FAMILY");
FamilyMacroConverter velstrfamilyConverterInstance("VELSTR_FAMILY");
FamilyMacroConverter tephigramfamilyConverterInstance("TEPHIGRAM_FAMILY");
FamilyMacroConverter tephidatafamilyConverterInstance("TEPHIDATA_FAMILY");
FamilyMacroConverter odbManagerFamilyConverterInstance("ODBVISUALISER_FAMILY");
FamilyMacroConverter netcdfManagerFamilyConverterInstance("NETCDFPLUS_FAMILY");

// Simple Formula Family converter
FormulaFamilyMacroConverter simpleformulafamilyConverterInstance("SIMPLE_FORMULA_FAMILY");
FormulaFamilyMacroConverter simpleformulafamilyConverterInstance1("SAMPLE_FORMULA_DOD");
FormulaFamilyMacroConverter simpleformulafamilyConverterInstance2("SAMPLE_FORMULA_DON");
FormulaFamilyMacroConverter simpleformulafamilyConverterInstance3("SAMPLE_FORMULA_NOD");
FormulaFamilyMacroConverter simpleformulafamilyConverterInstance4("SAMPLE_FORMULA_NON");
FormulaFamilyMacroConverter simpleformulafamilyConverterInstance5("SAMPLE_FORMULA_FD");
FormulaFamilyMacroConverter simpleformulafamilyConverterInstance6("SAMPLE_FORMULA_FN");
FormulaFamilyMacroConverter simpleformulafamilyConverterInstance7("SAMPLE_FORMULA_FDD");
FormulaFamilyMacroConverter simpleformulafamilyConverterInstance8("SAMPLE_FORMULA_FDN");
FormulaFamilyMacroConverter simpleformulafamilyConverterInstance9("SAMPLE_FORMULA_FND");
FormulaFamilyMacroConverter simpleformulafamilyConverterInstance10("SAMPLE_FORMULA_FNN");

ComputeMacroConverter computeConverterInstance("COMPUTE");

// Data converter
DataMacroConverter gribConverterInstance("GRIB");
DataMacroConverter bufrConverterInstance("BUFR");
DataMacroConverter cdfConverterInstance("NETCDF");
DataMacroConverter geoConverterInstance("GEOPOINTS");
DataMacroConverter odbConverterInstance("ODB_DB");

MacroConverter&
ConverterTraits::DefaultObject()
{
  return defaultConverterInstance;
}

/////////////// DefaultMacroConverter ///////////////
Cached DefaultMacroConverter::Convert(MvRequest &req,ObjectInfo *oi )
{
  // Convert the request and return the object name.
  Cached duName = ObjectInfo::GenerateName(req);

  Cached macroName = ObjectList::MacroName(req.getVerb());

  oi->ConvertRequestToMacro ( req, PUT_END,duName,macroName );
  return duName;
}

Cached ReadMacroConverter::Convert(MvRequest &req,ObjectInfo *oi )
{
 	Cached duName = ObjectInfo::GenerateName(req);
	oi->ConvertRequestToMacro ( req, PUT_END,duName,"read" );

	return duName;
}

Cached FamilyMacroConverter::Convert(MvRequest &req,ObjectInfo *oi )
{
	Cached returnName;
	MvRequest tmpReq = req;

	const char *name = tmpReq("_NAME");
	Cached duName = ObjectInfo::GenerateName(req);

	// First request contains just the family part, so skip it
	tmpReq.advance();

	if ( name && ! (const char *)tmpReq("_NAME") )
		tmpReq("_NAME") = name;

	// Get the macroname to call and convert it to lowercase.
	Cached familyType = ObjectList::MacroName(tmpReq.getVerb() );

	returnName = ObjectInfo::SpaceToUnderscore ( duName );

	oi->ConvertRequestToMacro ( tmpReq, PUT_END, returnName,familyType);

	return returnName;
}

Cached FormulaFamilyMacroConverter::Convert(MvRequest &req,ObjectInfo *oi )
{
    // The new algorithm uses the mvimportDesktop module, which is used
    // by the MacroEditor (dropping icon).
    // The old algorithm uses its own code.
    // We need to review the whole MacroConverter function trying to
    // use a single algorithm for both MacroConverter and MacroEditor
#if 1
    // Get initial information
    const char *name = req("_NAME");
    string duName = (const char*)ObjectInfo::GenerateName(req);

    // First request contains just the family part, get the name and skip it
    const char* familyName = req.getVerb();

    // Call an external function
    string tmpName = marstmp();
    char buf[1024];
    sprintf(buf,"$METVIEW_BIN/mvimportDesktop \"%s\" %s \"%s\" 4", name, tmpName.c_str(), familyName);
    system(buf);

    // Decode commands
    ifstream in(tmpName.c_str());
    if ( ! in )  // Fall back on the incomplete way
    {
        oi->PutNewLine("# ERROR: Could not convert the Simple Formula Family icon");
    }
    else
    {
        // Copy command lines
        while ( in.getline(buf,1023) )
        {
            // Strip of #Importing ... lines
            if ( strncmp("# Importing :",buf, 13))
            oi->PutNewLine(buf);
        }
        in.close();

        // Convert duName to lowercase, as function mvimport uses lowercase
        std::transform(duName.begin(), duName.end(), duName.begin(), ::tolower);
    }

    unlink(tmpName.c_str());

    return duName.c_str();
#else

	MvRequest tmpReq = req;
	const char *name = req("_NAME");
	Cached duName = ObjectInfo::GenerateName(req);
	Cached returnName = ObjectInfo::SpaceToUnderscore ( duName );

	// First request contains just the family part, so skip it
	tmpReq.advance();
	tmpReq = ObjectList::ExpandRequest(tmpReq,EXPAND_DEFAULTS);

	if ( name && ! (const char *)tmpReq("_NAME") )
		tmpReq("_NAME") = name;

	MvRequest defReq = ObjectList::CreateDefaultRequest(tmpReq.getVerb());

	const char *f, *p1,*p2,*oper;
	bool twoParams = false;
	string formula = " = ";
	string cp1, cp2;
	if ( IsParameterSet(tmpReq,"PARAMETER_2") )
	{
		p1 = tmpReq("PARAMETER_1");
		p2 = tmpReq("PARAMETER_2");
		if ( ObjectInfo::IsAName(p2 ) )
			cp2 = ObjectInfo::SpaceToUnderscore(mbasename(p2));
		else
			cp2 = p2;

		twoParams = true;
	}
	else
	{
		p1 = tmpReq("PARAMETER");
		if ( !p1 )
			p1 = tmpReq("PARAMETER_1");

		if ( !p1 )
			return "ERROR CONVERTING FAMILY FORMULA";
	}

	if ( ObjectInfo::IsAName(p1 ) )
		cp1 = ObjectInfo::SpaceToUnderscore(mbasename(p1));
	else
		cp1 = p1;

	if ( IsParameterSet(defReq,"FUNCTION") )
	{
		if ( IsParameterSet(tmpReq,"FUNCTION") )
			f = tmpReq("FUNCTION");
		else
			f = defReq("FUNCTION");

		if ( twoParams )
			formula = formula + f + "( " + cp1 + ", " + cp2 + ")";
		else
			formula = formula + f + "( " + cp1  + ")";
	}
	else
	{
		if ( IsParameterSet(tmpReq,"OPERATOR") ) 
			oper = tmpReq("OPERATOR");
		else
			oper = defReq("OPERATOR");

		formula = formula + cp1 + " " + oper + " " + cp2;
	}

	Cached familyType = ObjectList::MacroName(tmpReq.getVerb() );
	set<Cached> skipSet;
	bool onlySub = true; // Tell ConvertRequestToMacro to only expand subrequests, not the current one

	oi->ConvertRequestToMacro ( tmpReq, PUT_END, returnName,familyType,skipSet,onlySub);

	oi->PutNewLine(returnName + formula.c_str() + Cached("\n"));
    return returnName;
#endif
}


Cached CurveFamilyMacroConverter::Convert(MvRequest &req,ObjectInfo *oi )
{
  Cached returnName;

  Cached duName = ObjectInfo::GenerateName(req);
  Cached path = ObjectInfo::ObjectPath(req);

  // First request contains just the family part, so skip it
  req.advance();

  // Get the macroname to call and convert it to lowercase.
  const char *curveType = req.getVerb();

  returnName = ObjectInfo::SpaceToUnderscore ( duName );

  if ( ! strcmp(curveType,"GRAPH") )
    {
      // Get the names for the curves and try to exand them.
      vector<Cached> curveNames;
      const char *name;
      Cached oneName;
      for ( int i = 0; i < req.countValues("CURVES"); i++ )
	{
	  req.getValue(name,"CURVES",i);
	  Cached fileName = path + Cached("/") + Cached(name);
	  if ( FileCanBeOpened(fileName, "r" ) )
	    {
	      MvRequest iconRequest;
	      iconRequest.read(fileName);
	      oneName = oi->Convert(iconRequest);
	      if ( returnName )
		curveNames.push_back(oneName);
	    }
	}

      set<Cached> skipSet;
      skipSet.insert("CURVES");
      oi->ConvertRequestToMacro ( req, PUT_LAST_COMMA, returnName,curveType, skipSet);
      if ( curveNames.size() > 0 ) 
	{
	  Cached nameList = "[";
	  for (unsigned int j  = 0; j < curveNames.size();j++ )
	    {
	      if ( j > 0 )
		nameList = nameList + Cached(", ");
	      nameList = nameList + curveNames[j];
	    }
	  nameList = nameList + Cached(" ] ");
	  oi->FormatLine("CURVES", nameList,"" );
	  
	}
      oi->PutNewLine(Cached("   ) ") );
    }
  else
    oi->ConvertRequestToMacro ( req, PUT_END, returnName,curveType);

  return returnName;
}

// Let mvimport do the work here. It means the formatting will be slightly
// different.
Cached ComputeMacroConverter::Convert(MvRequest & req,ObjectInfo *oi )
{
  string tmpName = marstmp();

  char buf[1024];
  sprintf(buf,"$METVIEW_BIN/mvimport \"%s\" %s",
	  (const char *)req("_NAME"),
	  tmpName.c_str());
  system(buf);

  Cached duName = ObjectInfo::GenerateName(req);

  ifstream in(tmpName.c_str());

  if ( ! in )  // Fall back on the incomplete way
    {
	const char *formula = req("FORMULA");
	oi->PutNewLine("# Remember to import any icons used in formula");
	oi->PutNewLine("# Just click in macro after this line, and drop the icons");
	oi->PutNewLine ( duName + Cached("  = ")  + Cached(formula)  );
    }
  else
    {
      while ( in.getline(buf,1023) )
	{
	  // Strip of #Importing ... lines
	  if ( strncmp("# Importing :",buf, 13))
	    oi->PutNewLine(buf);
	}
      in.close();

      // Convert duName to lowercase, as mvimport uses lowercase.
      char *dulower = new char[ strlen(duName) + 1];
      strcpy(dulower,duName);

      for (unsigned int i=0;i<strlen(dulower); i++ )
	dulower[i] = tolower(dulower[i]);

      duName = dulower;
      delete [] dulower;
    }
  unlink(tmpName.c_str());

  return duName;
}


Cached DataMacroConverter::Convert(MvRequest &req,ObjectInfo *oi)
{
   // Obtain the object name
   Cached duName = ObjectInfo::GenerateName(req);

   // Obtain the fully qualified path to the object
   const char* mvPath = req( "_NAME" );
   if ( mvPath )
   {
      // Get the full path and check if it can be opened
      Cached filename = MakeUserPath(mvPath);
      if ( !FileCanBeOpened(filename,"r") )
         filename = Cached("Please provide here an input filename");

      oi->PutNewLine ( "" );
      oi->PutNewLine ( Cached ( "# Importing ") + duName );
      oi->PutNewLine ( duName + Cached("  = read  ( \"")  + filename  + Cached("\")") );
   }
   else
      duName = "";

   return duName;
}
