#include <stdio.h>
#include <ctype.h>
#include <string.h>

enum DataStructures { Tree, Matrix };
enum DataStructuresTypes { Complete, MemorySaving };

enum UnderlyingTypes { binom, binom2, timeHalvingTrinom };
enum DerivativeTypes { EuropeanCall, EuropeanPut, AmericanPut, AverageStrikeEuropeanCall };

int main(void);
void doMakefile(FILE *out, char *fname, int graphics);
void doUnderlyingIP(FILE *out, enum UnderlyingTypes utype);

static const char *DataStructuresStr[] = { "Tree", "Matrix" };
static const char *DataStructuresTypesStr[] = { "Complete", "MemorySaving"};
static const char *UnderlyingTypesStr[] = {"Binom", "Binom2", "TimeHalvingTrinom"};
static const char *DerivativeTypesStr[] = {"EuropeanCall", "EuropeanPut", "AmericanPut", "AverageStrikeEuropeanCall", "AverageStrikeAmericanPut"};

int main(void)
{
  FILE *out;
  char buf[256];
  char tmp[256];
  char fname[256];
  char ch = 0;
  int graphics;
  enum DataStructures dstruct;
  enum DataStructuresTypes dstructType;
  enum UnderlyingTypes utype;
  enum DerivativeTypes dtype;
  
  printf("Enter name of output cpp file (WITHOUT the .cpp): ");
  fgets(buf, 255, stdin);
  buf[strlen(buf)-1] = 0;
  
  sprintf(fname, "%s", buf);
  sprintf(tmp, "%s.cpp", buf);

  if( (out = fopen(tmp, "w")) == NULL)
    perror("could not open file for writing");
  
  while(ch != 'y' && ch != 'n')
    {
      printf("Use graphics [y/n]? ");
  
      fgets(buf, 255, stdin);
      ch = buf[0];
      ch = tolower(ch);
    }
  
  if(ch == 'y')
    graphics = 1;
  else
    graphics = 0;
  
  ch = 0;
  
  while(ch != '0' && ch != '1')
    {
      printf("Select Data Structure:\n  0) Tree\n  1) Matrix\n> ");
      fgets(buf, 255, stdin);
      ch = buf[0];
    }

  dstruct = ch-48;

  ch = 0;
  
  while(ch != '0' && ch != '1')
    {
      printf("Select Data Structure Type:\n  0) Complete\n  1) MemorySaving\n> ");
      fgets(buf, 255, stdin);
      ch = buf[0];
    }

  dstructType = ch-48;
  
  ch = 0;

  while(ch != '0' && ch != '1' && ch != '2')
    {
      printf("Select Underlying:\n  0) binom\n  1) binom2\n  2) timeHalvingTrinom\n> ");
      fgets(buf, 255, stdin);
      ch = buf[0];
    }
  
  utype = ch-48;

  ch = 0;

  while(ch != '0' && ch != '1' && ch != '2' && ch != '3' && ch != '4')
    {
      printf("Select Derivative:\n  0) EuropeanCall\n  1) EuropeanPut\n  2) AmericanPut\n  3) AverageStrikeEuropeanCall\n  4) AverageStrikeAmericanPut\n> ");
      fgets(buf, 255, stdin);
      ch = buf[0];
    }
  
  dtype = ch-48;
  
  fprintf(out, "#include <stdio.h>\n");
  fprintf(out, "#include <vector.h>\n");
  fprintf(out, "#include <stack.h>\n");
  fprintf(out, "#include <math.h>\n\n");
  fprintf(out, "#include \"treenode.h\"\n");
  fprintf(out, "#include \"visitor.h\"\n");
  fprintf(out, "#include \"iterators.h\"\n");
  fprintf(out, "#include \"dstruct.h\"\n");
  fprintf(out, "#include \"multiplicative.h\"\n");

  fprintf(out, "#include \"underlying/%c%s.h\"\n", 
	  tolower(*UnderlyingTypesStr[utype]), UnderlyingTypesStr[utype]+1);

  fprintf(out, "#include \"derivative/%c%s.h\"\n", 
	  tolower(*DerivativeTypesStr[dtype]), DerivativeTypesStr[dtype]+1);

  fprintf(out, "#include \"pricing.h\"\n\n");
  fprintf(out, "int main(void);\n\n");
  fprintf(out, "int main(void)\n{\n");

  doUnderlyingIP(out, utype);

  printf("Enter derivative parameter K: ");
  fgets(buf, 255, stdin);
  
  buf[strlen(buf) - 1] = 0;

  fprintf(out, "\tPricing<UNDERLYING(%s), %s<%s, Multiplicative>, %s > *derivative = new Pricing<UNDERLYING(%s), %s<%s, Multiplicative>, %s >(param, %s);\n\n", UnderlyingTypesStr[utype], DataStructuresStr[dstruct], DataStructuresTypesStr[dstructType], DerivativeTypesStr[dtype], UnderlyingTypesStr[utype], DataStructuresStr[dstruct], DataStructuresTypesStr[dstructType], DerivativeTypesStr[dtype], buf);

  fprintf(out, "\t/* The following methods are available in derivative:\n\t *\n");

  fprintf(out, "\t * double getDerivativeRootValue()\n");
  fprintf(out, "\t *\n");

  if(dstructType == Complete)
    switch(dstruct)
      {
      case Tree:
	  
	fprintf(out, "\t * PostOrderIterator<double>& getStockPostOrderIterator()\n");
	fprintf(out, "\t * BreadthFirstIterator<double>& getStockBreadthFirstIterator()\n");
	fprintf(out, "\t * \n");
	fprintf(out, "\t * PostOrderIterator<double>& getDerivativePostOrderIterator()\n");
	fprintf(out, "\t * BreadthFirstIterator<double>& getDerivativeBreadthFirstIterator()\n");
	fprintf(out, "\t * \n");

	if(graphics)
	  {
	    fprintf(out, "\t * void graphStock(char *fname, unsigned int width, unsigned int height)\n");
	    fprintf(out, "\t * void graphDerivative(char *fname, unsigned int width, unsigned int height)\n");
	  }

	break;
      case Matrix:
	{
	  fprintf(out, "\t * double getStockValue(unsigned int col, unsigned int row)\n");
	  fprintf(out, "\t * double getDerivativeValue(unsigned int col, unsigned int row)\n");

	  if(graphics)
	    {
	      fprintf(out, "\t * void graphStock(char *fname, unsigned int width, unsigned int height)\n");
	      fprintf(out, "\t * void graphDerivative(char *fname, unsigned int width, unsigned int height)\n");
	      fprintf(out, "\t * \n");
	    }
	    
	  break;
	}
      }

  fprintf(out, "\t */\n");
  fprintf(out, "\n\t printf(\"root derivative value is %%f\\n\", derivative->getDerivativeRootValue());\n");
  fprintf(out, "}");
  fclose(out);
  
  /* now create the Makefile
   * first, name the makefile "Makefile.filename"
   * where filename is the user supplied filename - the extension
   */

  sprintf(buf, "Makefile.%s", fname);
  
  if( (out = fopen(buf, "w")) == NULL)
    perror("could not open Makefile for writing");
  
  doMakefile(out, fname, graphics);

  fclose(out);
  
  printf("\n2 files were generated: %s.cpp and %s\n", fname, buf);
  printf("\nTo compile:\n\n\tmake -f %s\n", buf);
  return 0;
}

void doMakefile(FILE *out, char *fname, int graphics)
{
  fprintf(out, "# set GRAPHICS to either YES or NO\n");
  fprintf(out, "# set INCLUDE to either the relative or absolute location of the header files\n\n");
  fprintf(out, "GRAPHICS= %s\nINCLUDE= ../Source\n\n#don't touch anything below here\n\n", (graphics ? "YES" : "NO"));
  fprintf(out, "INC= $(INCLUDE)/dstruct.h $(INCLUDE)/iterators.h $(INCLUDE)/multiplicative.h $(INCLUDE)/pricing.h $(INCLUDE)/treenode.h $(INCLUDE)/underlying.h $(INCLUDE)/visitor.h $(INCLUDE)/derivative/americanPut.h $(INCLUDE)/derivative/averageStrikeEuropeanCall.h $(INCLUDE)/derivative/averageStrikeAmericanPut.h $(INCLUDE)/derivative/europeanCall.h $(INCLUDE)/derivative/europeanPut.h $(INCLUDE)/underlying/binom.h $(INCLUDE)/underlying/binom2.h $(INCLUDE)/underlying/timeHalvingTrinom.h\n\n");
  fprintf(out, "ifeq (YES, $(GRAPHICS))\n\tLIBS := -lm -lgd -lpng\n\tENABLE := -DGRAPHICS=1\n");
  fprintf(out, "else\n\tLIBS := -lm\n\tENABLE :=\nendif\n\n");
  
  fprintf(out, "all: %s\n\n", fname);
  
  fprintf(out, "%s: %s.o\n\tg++ -I$(INCLUDE) -g -o %s %s.o $(LIBS)\n\n", fname, fname, fname, fname);
  
  fprintf(out, "%s.o: %s.cpp $(INC)\n\tg++ -I$(INCLUDE) $(ENABLE) -Wall -g -c %s.cpp\n\n", fname, fname, fname);
}
  
  
void doUnderlyingIP(FILE *out, enum UnderlyingTypes utypes)
{
  int max = 0, i;
  char *params[10];
  char buf[256];
  
  switch(utypes)
    {
    case binom:
      fprintf(out, "\tIP_Binom param;\n\n");
      params[0] = "S0";
      params[1] = "N";
      params[2] = "dt[0]";
      params[3] = "U";
      params[4] = "D";
      params[5] = "r";

      max = 6;
      
      break;
    case binom2:
      fprintf(out, "\tIP_Binom2 param;\n\n");
      params[0] = "S0";
      params[1] = "N";
      params[2] = "dt[0]";
      params[3] = "sigma";
      params[4] = "r";

      max = 5;

      break;
    case timeHalvingTrinom:
      fprintf(out, "\tIP_TimeHalvingTrinom param;\n\n");
      params[0] = "S0";
      params[1] = "N";
      params[2] = "dt[0]";
      params[3] = "sigma";
      params[4] = "r";

      max = 5;

      break;
    default:

      printf("unknown underlying type %d\n", utypes);
      return;
    }

  fprintf(out, "\tparam.dt = vector<double>(1);\n\n");
  
  for(i = 0; i < max; i++)
    {
      printf("Enter underlying input paramater %s: ", params[i]);
      fgets(buf, 255, stdin);
      buf[strlen(buf)-1] = 0;

      fprintf(out, "\tparam.%s = %s;\n", params[i], buf);
    }
  
  fprintf(out, "\n");
}
