#include <stdio.h>
#include <iostream>
#include <string.h>
#include <assert.h>
#include "snopt.hh"
#include "cuterProblem.hh"
#include "snoptProblem.hh"
#include "snfilewrapper.hh"

using namespace std;

static integer *glbliGfun = 0;
static integer *glbljGvar = 0;

int cuterusrfg_( integer    *Status, integer *n,    doublereal x[],
		 integer    *needF,  integer *neF,  doublereal F[],
		 integer    *needG,  integer *neG,  doublereal G[],
		 char       *cu,     integer *lencu,
		 integer    iu[],    integer *leniu,
		 doublereal ru[],    integer *lenru )
{
  integer mc = *neF-1, nx = *n;
  if (*needF ) {
    cfn_( &nx, &mc, x, F, &mc, F+1 );
  }
  if (*needG){
    integer grlagf  = 0, nnz;
    cdimsj_( &nnz );
    assert( nnz == *neG );
    doublereal y[1] = { 1e20 }; // Not used, since we are requesting the gradient
    // of the objective, but we still need at least one element
    // (why? I don't know. I put a huge value in there to screw up the gradient
    // just in case it does get used.)
    integer ly = 0;
    //  int ldJ = J.leadingDimension();
    csgr_( &nx, &mc, &grlagf, &ly, y, x, &nnz,
	   neG, G, glbliGfun, glbljGvar );
  }
}

int cuterusrf_( integer    *Status, integer *n,    doublereal x[],
		integer    *needF,  integer *neF,  doublereal F[],
		integer    *needG,  integer *neG,  doublereal G[],
		char       *cu,     integer *lencu,
		integer    iu[],    integer *leniu,
		doublereal ru[],    integer *lenru )
{
  integer mc = *neF-1, nx = *n;
  cfn_( &nx, &mc, x, F, &mc, F+1 );
}

cuterProblem::cuterProblem()
{
  xlow = 0;  xupp = 0;  x = 0;  xmul = 0;

  equatn = 0;  linear = 0;

  Flow = 0;  Fupp = 0;  F = 0;  Fmul = 0;

  xstate = 0;  Fstate = 0;

  iAfun  = 0; jAvar = 0; A = 0;
  iGfun  = 0; jGvar = 0;

  xnames = 0;
  Fnames = 0;
}

cuterProblem::~cuterProblem()
{
  if (xlow != 0 ) delete []xlow;
  if (xupp != 0 ) delete []xupp;
  if (x    != 0 ) delete []x;
  if (xmul != 0 ) delete []xmul;

  if (Flow != 0 ) delete []Flow;
  if (Fupp != 0 ) delete []Fupp;
  if (F    != 0 ) delete []F;
  if (Fmul != 0 ) delete []Fmul;

  if (xstate != 0 ) delete []xstate;
  if (Fstate != 0 ) delete []Fstate;

  if (equatn != 0 ) delete []equatn;
  if (linear != 0 ) delete []linear;

  if (iAfun != 0 ) delete []iAfun;
  if (jAvar != 0 ) delete []jAvar;
  if (A     != 0 ) delete []A;
  if (iGfun != 0 ) delete []iGfun;
  if (jGvar != 0 ) delete []jGvar;


  if (xnames != 0 ) delete []xnames;
  if (Fnames != 0 ) delete []Fnames;

  if (glbliGfun != 0 ) delete []glbliGfun;
  if (glbljGvar != 0 ) delete []glbljGvar;
}

void cuterProblem::alloc( integer n, integer neF)
{
  xlow = new doublereal[n];
  xupp = new doublereal[n];
  x    = new doublereal[n];
  xmul = new doublereal[n];

  equatn = new integer[neF-1];
  linear = new integer[neF-1];

  Flow   = new doublereal[neF];
  Fupp   = new doublereal[neF];
  Fmul   = new doublereal[neF];
  F      = new doublereal[neF];

  xstate = new integer[n];
  Fstate = new integer[neF];

//   lenA = 10;
//   iAfun = new integer[this->lenA];
//   jAvar = new integer[this->lenA];
//   A     = new doublereal[this->lenA];

//   lenG  = 10;
//   iGfun = new integer[lenG];
//   jGvar = new integer[lenG];

//   n      = 2;
//   x      = new doublereal[n];
//   xlow   = new doublereal[n];
//   xupp   = new doublereal[n];
//   xmul   = new doublereal[n];
//   xstate = new    integer[n];

//   neF    = 3;
//   F      = new doublereal[neF];
//   Flow   = new doublereal[neF];
//   Fupp   = new doublereal[neF];
//   Fmul   = new doublereal[neF];
//   Fstate = new integer[neF];

//   nxnames = 1;
//   nFnames = 1;
//   xnames = new char[nxnames*8];
//   Fnames = new char[nFnames*8];
}

void cuterProblem::initialize()
{
  /* Allocate and initialize data */
  integer fifty_five = 55;
  integer six        = 6;
  integer efirst = 0, lfirst = 0, nvfirst = 0;

  char fname[18] = "OUTSDIF.d";
  snopenread_( &fifty_five, fname, &inform, 9 );

  cdimen_( &fifty_five, &nx, &mc );
  neF    = mc + 1; //Add one for F[0] = objective.
  n      = nx;

  this->alloc( n, neF );

  if( 0 == inform ) {
    integer nmax = n;
    integer mmax = neF-1;
    csetup_( &fifty_five, &six, &nx, &mc, x, xlow, xupp, &nmax,
	     equatn, linear, Fmul+1, Flow+1, Fupp+1, &mmax,
	     &efirst, &lfirst, &nvfirst );
    snclose_( &fifty_five );
  }

  usrfun = cuterusrfg_;

  cdimsj_( &lenG );

  lenG = lenG + n; //Add on n for objective gradient.
  neG  = lenG;
  lenA = lenG;
  neA  = 0;
  iGfun = new integer[lenG];
  jGvar = new integer[lenG];
  iAfun = new integer[lenA];
  jAvar = new integer[lenA];
  A     = new doublereal[lenA];

  glbliGfun = new integer[lenG];
  glbljGvar = new integer[lenG];

  xnames = new char[10*nx];
  Fnames = new char[10*mc];

  /* Give the problem a name.  */
  cnames_( &nx, &mc, Prob, xnames, Fnames, 200, 8*nx, 8*mc);
  delete []xnames;
  delete []Fnames;
  nxnames = 1;
  nFnames = 1;
  xnames = new char[nxnames*8];
  Fnames = new char[nFnames*8];

  ObjRow = 0;
  ObjAdd = 0;

  {//Perhaps there is a better way to do this.  But we
    //need to compute iGfun and jGvar.  Don't really need G.
    doublereal *Gtemp = new doublereal[lenG];
    integer grlagf  = 0;
    doublereal y[1] = { 1e20 }; // Not used, since we are requesting the gradient
    // of the objective, but we still need at least one element
    // (why? I don't know. I put a huge value in there to screw up the gradient
    // just in case it does get used.)
    integer ly = 0;
    //  int ldJ = J.leadingDimension();
    csgr_( &nx, &mc, &grlagf, &ly, y, x, &neG,
	   &lenG, Gtemp, glbljGvar, glbliGfun);
    //Now convert iGfun and jGvar to be C++ type array starting at 0.
    for( int k = 0; k < neG; k++ ) jGvar[k] = glbljGvar[k] - 1;
    memset( iGfun + (neG-n), 0, n*sizeof(integer) );
    memcpy( iGfun, glbliGfun, (neG-n)*sizeof(integer) );
    delete []Gtemp;
  }

  /* Set xstate and Fstate to zero. */
  memset( xstate, 0, n*sizeof(integer) );
  memset( Fstate, 0, neF*sizeof(integer) );

  this->makePrintName();
}

void cuterProblem::makePrintName()
{
  char *endptr;
  strcpy( printname, Prob );
  endptr = strchr( printname, ' ' );
  *endptr = '\0';
  sprintf( Prob, "%8s", printname );
  strcat( printname, ".out" );
  this->setPrintFile( printname );
}
