// Copyright (C) 2010 Deltares
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

/**
 * @file
 * @brief xxx
 * @author Dirk Schwanenberg
 * @version 1.0
 * @date 2010, 2011
 */

#include "rtcToolsGAMS_OPT.h"
#include "piDiagInterface.h"

#include <cstdio>
#include <stdlib.h> 

// constructors
rtcToolsGAMS_OPT::rtcToolsGAMS_OPT(rtcToolsRuntime *tool, string workDir)
{
	this->tool = tool;
	this->workDir = workDir;
}

rtcToolsGAMS_OPT::rtcToolsGAMS_OPT(rtcToolsRuntime *tool, rtcRuntimeConfigSettings::GAMS par, string workDir)
{
	this->tool = tool;
	this->workDir = workDir;
	this->par = par;
}

int rtcToolsGAMS_OPT::optimize()
{
	ofstream gamsFile(string(utils::getAbsoluteFilename(workDir, "gamsFromRTCTools.gms")).c_str(), ios::out | ios::trunc);

	int n = tool->getN();
	int m = tool->getM();
	int nnz = tool->getNNZ();

	// variables and variable bounds
	gamsFile << "*vector with control variables, objective function term" << endl;
	if (m==0) {
		gamsFile << "set i / i1*i" << n << " /" << endl;
		gamsFile << "variables x[i], objX;" << endl << endl;
	} else {
		gamsFile << "set i / i1*i" << n << " /" << endl;
		gamsFile << "set j / j1*j" << m << " /" << endl;
		gamsFile << "variables x[i], objX, slack[j];" << endl << endl;
	}

	// define the external equations
	gamsFile << "Equations" << endl << "  Def_objX" << endl;
	for (int i=0; i<m; i++) {
		gamsFile << "  constr" << (i+1) << "X" << endl;
	}
	gamsFile << ";" << endl << endl;

	// define the variables in each external equation
	int* iRow = new int[nnz];
	int* jCol = new int[nnz];
	tool->eval_jac_g(m, nnz, iRow, jCol, (double*) 0);
	gamsFile << "Def_objX .. 1 =X= sum(i, ord(i)*x(i)) + (card(i)+1)* objX;" << endl;
	for (int i=0; i<m; i++) {
		gamsFile << "constr" << (i+1) << "X .. " << i+2 << " =X= ";
		int nCell = 0;
		for (int j=0; j<nnz; j++) {
			if (iRow[j]==i) {
				if (nCell == 0) {
					gamsFile << jCol[j]+1 << "*x('i" << jCol[j]+1 << "')" << endl;
					nCell = 1;
				} else {
					gamsFile << "  + " << jCol[j]+1 << "*x('i" << jCol[j]+1 << "')" << endl;
				}
			}
		}
		gamsFile << "  + " << n+i+2 << "*slack('j" << i+1 << "');" << endl;
	}
	gamsFile << endl;

	// get upper / lower bounds as well as initial condition
	double* x0 = new double[n];
	double* xl = new double[n];
	double* xu = new double[n];
	double* gl = new double[m];
	double* gu = new double[m];
	tool->getInput(n, x0);
	for (int i=0; i<n; i++) {
		if (x0[i]!=x0[i]) x0[i] = 0.0;
	}
	tool->get_bounds_info(n, xl, xu, m, gl, gu);

	// lower / upper bounds of variables, initial value
	for (int i=0; i<n; i++) {
		if (xl[i]>-1e+10) gamsFile << "x.lo('i" << (i+1) << "') = " << xl[i] << "; ";
		if (xu[i]< 1e+10) gamsFile << "x.up('i" << (i+1) << "') = " << xu[i] << "; ";
		gamsFile << "x.l('i"  << (i+1) << "') = " << x0[i] << "; ";
		gamsFile << endl;
	}
	gamsFile << endl;

	// lower upper bounds of constraints
	for (int j=0; j<m; j++) {
		if (gl[j]>-1e+10) gamsFile << "slack.lo('j" << (j+1) << "') = " << gl[j] << "; ";
		if (gu[j]< 1e+10) gamsFile << "slack.up('j" << (j+1) << "') = " << gu[j] << "; ";
		gamsFile << endl;
	}
	gamsFile << endl;

	delete [] x0;
	delete [] xl;
	delete [] xu;
	delete [] gl;
	delete [] gu;

	// initial condition for objective function value
	gamsFile << "objX.l = 0.0;" << endl << endl;

	// optimization
	gamsFile << "option nlp = ipopth;" << endl << endl;

	gamsFile << "Model RTCToolsGAMS /all/;" << endl;
	gamsFile << "Solve RTCToolsGAMS using nlp minimize objX;" << endl << endl;

	// display results
	gamsFile << "display    x.l ;" << endl;
	gamsFile << "display objX.l ;" << endl;

	gamsFile.close();

	return 0;
}
