/***************************** 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 *************************************/


//#define Box braz_box

#include <inc_iostream.h>
#include <stdlib.h>
#include <Metview.h>
#include <MvPath.hpp>


using namespace std;


//
//_____________________________________________________________________

class MetPlus : public MvService
{
public:
    MetPlus();
    void serve(MvRequest&, MvRequest&);

protected:
    map<string, string> databases_;
};

//_____________________________________________________________________

MetPlus::MetPlus() :
    MvService("MetPlus")
{
    MvRequest setup = mars.setup;
    setup.rewind();
    saveToPool(false);

    while (setup) {
        if (setup.getVerb() == Cached("MetPlus")) {
            databases_["10_days_epsgram"]              = "10_days";
            databases_["15_days_epsgram"]              = "15_days";
            databases_["10_days_wave_epsgram"]         = "10_days_wave";
            databases_["10_days_plumes"]               = "10_days_plumes";
            databases_["15_days_epsgram_with_climate"] = "15_days_with_clim";

            break;
        }
        setup.advance();
    }
}

/*
static 
void substitute(string& xml, const string& var, const string& val)
{
	string name("$");
	name.append(var);
	unsigned int index = xml.find(name);
	int last = 0;
	
	while ( index != string::npos) {
		xml.replace(index, name.length(), val);
		last = index + val.length();
		std::cout << "[" << name << "]" << "\n";
		index = xml.find(name, last);
        }

//	std::cout << "[" << xml << "]" << "\n";
}
*/
static string toString(double val)
{
    ostringstream tool;
    //tool.width(7);
    tool.precision(5);
    tool << val;
    return tool.str();
}


static string UpperCase(const string& s)
{
    string out;
    for (string::const_iterator l = s.begin(); l != s.end(); ++l)
        out.push_back(toupper(*l));
    return out;
}


/*
static string format_spec (const string &format_to_check, const string &format_we_want,
                           string &outfile, string &outclass)
{
    if (format_to_check == format_we_want)  // is this the one we want?
    {
        string extra_params = "";


        // set the output class (the VERB in the output request)

        if (format_to_check == "ps") outclass = "PSFILE";
        else                         outclass = UpperCase (format_to_check);


        // for single-page formats, turn off the first page numbering

        if (format_to_check != "ps" && format_to_check != "pdf")
        {
            extra_params = string(" output_name_first_page_number=\'off\'");
        }


        // add the correct extension to the (temporary) output file

        outfile.append(".");
        outfile.append(format_to_check);


        // create the MagML driver tag, e.g. "ps fullname = '/a/b/c'"

        string spec(format_we_want);
        spec.append(" fullname=\'");
        spec.append(outfile);
        spec.append("\'");
        spec.append(extra_params);

        return spec;
    }
    
    else
    {
        // we do not want this format

        string spec("no");              // e.g. "nosvg"
        spec.append(format_to_check);
        return spec;
    }
}
*/

void MetPlus::serve(MvRequest& in, MvRequest& out)
{
    cout << "--------------MetPlus::serve() in--------------" << endl;
    in.print();

    vector<string> tempFileNames;  // will store a list of all the temporary filenames used

    string outclass       = "BAD";
    string outputFileName = marstmp();


    // ----------------------------------------------------
    // --- construct the string that will be written to the
    // --- xml metgram request file
    // ----------------------------------------------------

    string format;
    string mgrequest;
    string mainTag;


    // an example request looks like this:
    // <eps template='10_days' date='latest' format='a4'>
    //     <station name='Reading' latitude='51.4' longitude='-1'  height='48' psfile='/xyz/Reading.ps' giffile='Reading.gif'/>
    // </eps>


    // get the data selection type (local, date or latest)

    string dataSelectionType = (const char*)in("DATA_SELECTION_TYPE");


    // start creating our xml metgram request. We will write this to the file 'mgrequestfile'


    if (dataSelectionType == "LOCAL")
        mainTag = "epslocal";
    else
        mainTag = "eps";


    mgrequest = "<" + mainTag + " ";


    // get the template we'll use (e.g. 10 days eps)
    // first, convert the name to what the metgram client expects

    string type = (const char*)in("type");
    type        = databases_[type];
    mgrequest += "template='" + type + "' ";


    if (dataSelectionType == "DATE") {
        // get the date as a full string

        MvDate date = in("DATE");
        ostringstream date_option;
        date_option << date.YyyyMmDd();
        mgrequest += "date='" + date_option.str() + "' ";


        // get the time as a full string

        int time = in("FORECAST_RUN_TIME");
        ostringstream time_option;
        time_option << setw(4) << setfill('0') << time * 100;
        mgrequest += "time='" + time_option.str() + "' ";
    }

    else if (dataSelectionType == "LATEST") {
        mgrequest += "date='latest' ";
    }

    else if (dataSelectionType == "LOCAL") {
        string database = (const char*)in("database");

        mgrequest += "database='" + database + "' ";
    }


    // get the experiment number

    string experiment = (const char*)in("EXPERIMENT");

    if (!experiment.empty())
        mgrequest += "expver='" + experiment + "' ";


    // get the size (A4, A3, etc)
    // - also make a note of where we are in the string because we might want
    //   to hardcode this to A3 if the user wants a raster file generated.

    string size = "a4";
    mgrequest += "format='" + size + "' ";
    size_t a4_char_position = mgrequest.size() - 3;


    // close the eps tag

    mgrequest += ">\n    ";


    // --- loop through the stations

    MvRequest station = in("station");

    while (station) {
        // start the embedded station tag

        mgrequest += "<station ";


        // get the station name for the title

        string title_option = (const char*)station("NAME");
        mgrequest += "name='" + title_option + "' ";


        // get the station coordinates

        string latitude_option  = toString(station("LATITUDE"));
        string longitude_option = toString(station("LONGITUDE"));
        mgrequest += "latitude='" + latitude_option + "' ";
        mgrequest += "longitude='" + longitude_option + "' ";


        // get the station height

        if (station.countValues("HEIGHT") != 0) {
            string height_option = toString(station("HEIGHT"));

            if (!height_option.empty())
                mgrequest += "height='" + height_option + "' ";
        }


        // get the file format option

        format = (const char*)in("format");
        string format_option;

        if (format == "ps")
            format_option = "psfile";
        else if (format == "png")
            format_option = "pngfile";
        else if (format == "gif")
            format_option = "giffile";
        else if (format == "jpeg")
            format_option = "jpgfile";
        else if (format == "pdf")
            format_option = "pdffile";
        else
            format_option = "pdffile";
        // SVG????????????


        string tmpOutputFileName = marstmp();
        mgrequest += format_option + "='" + tmpOutputFileName + "' ";

        tempFileNames.push_back(tmpOutputFileName);  // remember which filename we used


        // if we are creating a raster image, then set the format to a3 so
        // that the conversion looks better (the web metgram server actually
        // generates a ps file and then converts that to raster).

        if (format == "png" || format == "gif" || format == "jpeg") {
            mgrequest.replace(a4_char_position, 1, "3");
        }


        // close the station tag

        mgrequest += "/>\n";

        station.advance();
    }


    // finally add the closing eps tag

    mgrequest += "</" + mainTag + ">\n";


    // we only support multiple stations with PostScript output
    // (for backwards compatibility with the previous metgram module)

    if (tempFileNames.size() > 1 && (format != "ps") && (format != "pdf")) {
        setError(1, "MetPlus-> Multiple stations only supported with PDF or PostScript output; use 'pdf' output or use multiple calls.");
        return;
    }

    // write the request to a text file

    string mgrequestfile = marstmp();  // the name of the temporary file we'll write our xml request to


    ofstream xmlrequestfile;
    xmlrequestfile.open(mgrequestfile.c_str());

    if (xmlrequestfile.is_open()) {
        xmlrequestfile << mgrequest;
        xmlrequestfile.close();
    }
    else {
        setError(1, "MetPlus-> Failed to write xml metgram request file: %s", mgrequestfile.c_str());
        return;
    }


    // run the metgram command
    // which command to run to get the metgram - the 'classic' metgram requires a different script

    string command;
    command = getenv("METVIEW_BIN");
    command += "/mv_metgram " + mgrequestfile;


    sendProgress("MetPlus-> Running command: %s", command.c_str());
    int return_code = system(command.c_str());
    sendProgress("MetPlus-> Returned: %d", return_code);


    // successful execution?

    if (return_code == 0) {
        // check if any of the returned files are either non-existent or of zero-length

        bool allFilesOk = true;

        for (size_t i = 0; i < tempFileNames.size(); i++) {
            if (!FileHasValidSize(tempFileNames[i].c_str())) {
                allFilesOk = false;
                break;
            }
        }

        if (allFilesOk) {
            // copy the temporary files to their proper destinations

            if (tempFileNames.size() > 1) {
                // multiple metgrams - join them into one; format-dependent code here

                std::string mpRunning("MetPlus-> Running: ");

                if (format == "ps") {
                    string psJoinCommand = "psjoin -p ";

                    for (size_t i = 0; i < tempFileNames.size(); i++) {
                        psJoinCommand += tempFileNames[i] + " ";
                    }

                    psJoinCommand += " > " + outputFileName;

                    sendProgress(mpRunning + psJoinCommand);
                    system(psJoinCommand.c_str());
                }

                else if (format == "pdf") {
                    string pdfJoinCommand = "gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -sOutputFile=";
                    pdfJoinCommand += outputFileName + " ";

                    for (size_t i = 0; i < tempFileNames.size(); i++) {
                        pdfJoinCommand += tempFileNames[i] + " ";
                    }

                    sendProgress(mpRunning + pdfJoinCommand);
                    system(pdfJoinCommand.c_str());
                }
                else {
                    // otherwise, just one file so copy it to the dest filename

                    mars_copyfile(tempFileNames[0].c_str(), outputFileName.c_str());
                }
            }

            else {
                // otherwise, just one file so copy it to the dest filename

                mars_copyfile(tempFileNames[0].c_str(), outputFileName.c_str());
            }


            // return the path to the output file
            // - this will allow the user to save or visualise the result

            string outclass;

            if (format == "ps")
                outclass = "PSFILE";
            else
                outclass = UpperCase(format);

            MvRequest data(outclass.c_str());

            data("PATH") = outputFileName.c_str();
            //data("TEMPORARY") = 1;
            out = data;
        }

        else  // not all the files are ok
        {
            // the script appeared to connect to the server ok, but it seems
            // that we did not get a valid file back from it
            // this will cause the program to fail and abort

            setError(1, "MetPlus-> Invalid file returned from server (non-existant or zero size)");
            return;
        }
    }

    else {
        // execution failed
        setError(1, "MetPlus-> Failed to connect to the metgram server");
        return;
    }

    cout << "--------------MetPlus::serve() out--------------" << endl;
    out.print();

    //////////////////////////////////////////////////////////////////////////////////
    /*

    if ( database == "latest") 
        database = databases_[type];

  
    ostringstream maxint;
    maxint << MAGINT_MAX;


    MvRequest station = in("station");
    string latitude   = toString(double(station("LATITUDE")));
    string longitude  = toString(double(station("LONGITUDE")));
    string name       = (const char*) station("NAME");
    double h = station("HEIGHT");
    string height     = h ? toString(h) : maxint.str();
  
  
    string xml      = marstmp();
    string outfile  = marstmp();

    string psinfo  = format_spec ("ps",  format, outfile, outclass);
    string svginfo = format_spec ("svg", format, outfile, outclass);
    string gifinfo = format_spec ("gif", format, outfile, outclass);
    string pnginfo = format_spec ("png", format, outfile, outclass);
    string pdfinfo = format_spec ("pdf", format, outfile, outclass);
  

    ostringstream magml;
    string magplushome = getenv("MAGPLUS_HOME");
    string template_file = magplushome;
    template_file.append("/share/templates/");
    template_file.append(type);
    template_file.append(".xml");
    magml << template_file;

    cout << "Using template: " << template_file << endl;
  
  

  try {
        ifstream instream(magml.str().c_str());
        ofstream outstream(xml.c_str());
        if (!instream)
        {            
            cout << "Could not read template file '" << template_file << "'" << endl;
            return;
        }

        string s; 
        s = "";
        
        char c;
        while(instream.get(c))
        {
            s += c;
        }
    
        substitute(s, "latitude", latitude);
        substitute(s, "longitude", longitude);
        substitute(s, "station", name);
        substitute(s, "title", name);
        substitute(s, "height", height);
        substitute(s, "database", database);
        substitute(s, "meta", "nometa");
        substitute(s, "format", "a4");
        substitute(s, "date", "");
        substitute(s, "time", "");
        substitute(s, "ps",  psinfo);
        substitute(s, "pdf", pdfinfo);
        substitute(s, "svg", svginfo);
        substitute(s, "gif", gifinfo);
        substitute(s, "png", pnginfo);
        
        outstream << s;
        cout << s;
    

        instream.close();
        outstream.close();

        manager_.execute(xml);
               
    }
    catch (exception e)
    {
        cout << "Exception caught after manager_.execute()" << endl;
    }



    MvRequest data(outclass.c_str());
    data("PATH") = outfile.c_str();
    //data("TEMPORARY") = 1;
    data.print();
    out = data;
*/
}
//_____________________________________________________________________

int main(int argc, char** argv)
{
    MvApplication theApp(argc, argv);
    MetPlus tool;
    theApp.run();
}
