#!/usr/bin/perl 

#
# $Id$
#
#  This file is part of the Virtual Leaf.
#
#  The Virtual Leaf is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  The Virtual Leaf 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 the Virtual Leaf.  If not, see <http://www.gnu.org/licenses/>.
#
#  Copyright 2010 Roeland Merks.
#

# input: parameter file + types
# format: par_name = default_value/type

# output: C++ source code of class Parameter
# and sample parameter file

%funname = (
	    "double" => fgetpar,
	    "float" => fgetpar,
	    "int" => igetpar,
	    "bool" => bgetpar,
	    "char *" => sgetpar,
	    "string" => sgetpar,
	    "double *" => dgetparlist,
	   );

%typetrans = (
	      "double" => "double",
	      "float" => "double",
	      "int" => "int",
	      "bool" => "bool",
	      "char *" => "char *",
	      "string" => "char *",
	      "directory" => "char *",
	      "doublelist" => "double *",
	      "label" => "label",
	      "title" => "title",
);

open parfile,"<$ARGV[0]";
open cppfile,">parameter.cpp";
$i=0;
while (<parfile>) {
#ignore comments
  if (/^#/) {
    next;
  }
  @line=split(/=/);
#ignore empty lines
  if ($#line<1) {
    next;
  }
  $param[$i]=$line[0];
  $value_type=$line[1];

  @typel=split(/ \/ /,$value_type);
  $value[$i] = $typel[0];
  $type[$i] = $typel[1];
  
#get rid of spaces
  $param[$i] =~ s/ //g;
  $value[$i] =~ s/ //g;
  $type[$i] =~ s/ //g;
  $type[$i] =~s/\n//g;
  $convtype[$i]=$typetrans{$type[$i]};

  if ($convtype[$i] eq "char *") {
    $value[$i] = "\"$value[$i]\"";
  }
  #print cppfile "param = $param, value = $value, type = $type\n";

  $i++;
}

$lines=$i;

print cppfile <<END_HEADER;
// WARNING: This file is automatically generated by make_parameter_source.pl. Do not edit.
// All edits will be discarded.

#include "parameter.h"
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cerrno>
#include <iostream>
#include <sstream>
#include "output.h"
#include "parse.h"
#include "xmlwrite.h"

using namespace std;


Parameter::Parameter() {
END_HEADER

for ($i=0;$i<$lines;$i++) {
  if ($convtype[$i] ne "label" && $convtype[$i] ne "title") {
    if ($convtype[$i] eq "char *") {
      print cppfile "  $param[$i] = strdup($value[$i]);\n";
    } else {
      if ($convtype[$i] eq "double *") {
	#comma separated list expected
	@paramlist = split(/,/, $value[$i]);
	$length = $#paramlist+1;
	print cppfile "  $param[$i] = new double\[$length\];\n";
	for ($j=0;$j<=$#paramlist;$j++) {
	  print cppfile "  $param[$i]\[$j\] = $paramlist[$j];\n";
	}
      } else {
	print cppfile "  $param[$i] = $value[$i];\n";
      }
    }
 }
}


print cppfile <<END_HEADER3;
}

Parameter::~Parameter() {
  
  // destruct parameter object

  // free string parameter

  CleanUp();

}

void Parameter::CleanUp(void) {
END_HEADER3

for ($i=0;$i<$lines;$i++) {
  if ($convtype[$i] eq "char *" || $convtype[$i] eq "double *") {
    print cppfile "  if ($param[$i]) \n";
    print cppfile "     free($param[$i]);\n";
  }
}
print cppfile <<END_HEADER4;

}

void Parameter::Read(const char *filename) {
  
  static bool ReadP=false;

  if (ReadP) {

    //throw "Run Time Error in parameter.cpp: Please Read parameter file only once!!";
    CleanUp();
    
  } else
    ReadP=true;

  FILE *fp=OpenReadFile(filename);


END_HEADER4

for ($i=0;$i<$lines;$i++) {

  if ($convtype[$i] eq "label" || $convtype[$i] eq "title") {
    next;
  }
  if ($convtype[$i] eq "double *") {
    @paramlist = split(/,/,$value[$i]);
    $length = $#paramlist+1;
    print cppfile "  $param[$i] = $funname{$convtype[$i]}(fp, \"$param[$i]\", $length, true);\n";
  } else {
    print cppfile "  $param[$i] = $funname{$convtype[$i]}(fp, \"$param[$i]\", $value[$i], true);\n";
  }
  if ($type[$i] eq "directory") {

    print cppfile "  if (strcmp($param[$i], \".\"))\n";
    print cppfile "    MakeDir($param[$i]);\n";
  }
}



print cppfile <<END_MIDPART;

}

const char *sbool(const bool &p) {

  const char *true_str="true";
  const char *false_str="false";
  if (p)
    return true_str;
  else
    return false_str;
}

void Parameter::Write(ostream &os) const {

END_MIDPART

for ($i=0;$i<$lines;$i++) {
 
  if ($convtype[$i] eq "label"  || $convtype[$i] eq "title") {
    next;
  }
  if ($convtype[$i] eq "double *") {
    print cppfile "  os << \" $param[$i] = \"";
    @paramlist = split(/,/,$value[$i]);
    for ($j=0;$j<$#paramlist;$j++) {
      print cppfile "<< $param[$i]\[$j\] << \", \" ";
    }
    print cppfile "<< $param[$i]\[$#paramlist\] << endl;\n";
  } else {
    if ($convtype[$i] eq "bool") {
      print cppfile "  os << \" $param[$i] = \" << sbool($param[$i]) << endl;\n";
    } else {
      if ($convtype[$i] eq "char *") {
	print cppfile "\n  if ($param[$i]) \n";
	print cppfile "  ";
      }
      print cppfile "  os << \" $param[$i] = \" << $param[$i] << endl;\n";
    }
  }
}

print cppfile "}\n";

print cppfile <<END_TRAIL2;
void Parameter::XMLAdd(xmlNode *root) const {
  xmlNode *xmlparameter = xmlNewChild(root, NULL, BAD_CAST "parameter", NULL);
END_TRAIL2

for ($i=0;$i<$lines;$i++) {
  if ($convtype[$i] eq "label" || $convtype[$i] eq "title") {
    next;
  }
  print cppfile "{\n";
  print cppfile "  xmlNode *xmlpar = xmlNewChild(xmlparameter, NULL, BAD_CAST \"par\", NULL);\n";
  print cppfile "  xmlNewProp(xmlpar, BAD_CAST \"name\", BAD_CAST \"$param[$i]\" );\n";
  if ($convtype[$i] eq "double *") {
    @paramlist = split(/,/,$value[$i]);
    print cppfile "  xmlNode *xmlvalarray = xmlNewChild(xmlpar, NULL, BAD_CAST \"valarray\", NULL);\n";
    for ($j=0;$j<=$#paramlist;$j++) {
      print cppfile "  {\n";
      print cppfile "    ostringstream text;\n";
      print cppfile "    text << $param[$i]\[$j\];\n";
      print cppfile "    xmlNode *xmlval = xmlNewChild(xmlvalarray, NULL, BAD_CAST \"val\", NULL);\n";
      print cppfile "    xmlNewProp(xmlval, BAD_CAST \"v\", BAD_CAST text.str().c_str());\n";
      print cppfile "  }\n";
    }
    print cppfile "}\n";
    next;
} else {
    print cppfile "  ostringstream text;\n";
    if ($convtype[$i] eq "bool") {
      print cppfile "text << sbool($param[$i]);\n";
    } else {
      if ($convtype[$i] eq "char *") {
	print cppfile "\n  if ($param[$i]) \n";
	print cppfile "  ";
      }
      print cppfile "  text << $param[$i];\n";
    }
  }
  print cppfile "xmlNewProp(xmlpar, BAD_CAST \"val\", BAD_CAST text.str().c_str());\n";
  print cppfile "}\n";
}
print cppfile "}\n";

print cppfile "void Parameter::AssignValToPar(const char *namec, const char *valc) {\n";
print cppfile;

for ($i=0;$i<$lines;$i++) {

  if ($convtype[$i] eq "label" || $convtype[$i] eq "title") {
    next;
  }
  if ($convtype[$i] eq "double *") {
    next;
  } else {
    print cppfile "if (!strcmp(namec, \"$param[$i]\")) {\n";
    if ($convtype[$i] eq "bool") {
      print cppfile "$param[$i] = strtobool(valc);\n";
    } else {
      if ($convtype[$i] eq "char *") {
	print cppfile "  if ($param[$i]) { free($param[$i]); }\n";
	print cppfile "  $param[$i]=strdup(valc);\n";
      } else {
	if ($convtype[$i] eq "int") {
	  print cppfile "  $param[$i] = (int)strtol(valc, 0, 10);\n";
	} else {
	  print cppfile "  $param[$i] = strtod(valc, 0);\n";
	}
      }
    }
  }
  print cppfile "}\n";
}
print cppfile "}\n";

print cppfile "void Parameter::AssignValArrayToPar(const char *namec, vector<double> valarray) {\n";
print cppfile;

for ($i=0;$i<$lines;$i++) {

  if ($convtype[$i] eq "double *") {
    @paramlist = split(/,/,$value[$i]);
    print cppfile "if (!strcmp(namec, \"$param[$i]\")) {\n";
    print cppfile "  int i=0;\n";
    print cppfile "  vector<double>::const_iterator v=valarray.begin();\n";
    print cppfile "  while (v!=valarray.end() && i <= $#paramlist ) {\n";
    print cppfile "     $param[$i]\[i++\]=*(v++);\n";
    print cppfile "  }\n";
    print cppfile "}\n";
  }
}
print cppfile "}\n";


print cppfile <<END_TRAILER;

/* void Parameter::XMLRead(xmlNode *root) {
  
  xmlNode *cur = root->xmlChildrenNode;
  while (cur!=NULL) {
    if ((!xmlStrcmp(cur->name, (const xmlChar *)"parameter"))){
      xmlNode *par_node = cur->xmlChildrenNode;
      while (par_node!=NULL) {
	{
	  if (!xmlStrcmp(par_node->name, (const xmlChar *)"par")) {
	    xmlChar *namec = xmlGetProp(par_node, BAD_CAST "name");
	    xmlChar *valc = xmlGetProp(par_node, BAD_CAST "val");
	    if (valc) {
	      AssignValToPar((const char*)namec,(const char*)valc);
	    } else {
	      // Probably a valarray
	      xmlNode *sub_par_node = par_node->xmlChildrenNode;
	      vector<double> valarray; 
	      while (sub_par_node != NULL) {
		if (!xmlStrcmp(sub_par_node->name, (const xmlChar *)"valarray")) {
		  valarray = XMLIO::XMLReadValArray(sub_par_node);
		}
		sub_par_node = sub_par_node->next;
	      }
	      AssignValArrayToPar((const char*)namec, valarray);
	    }
	  }
	}	  
	par_node = par_node->next;
      }

    }
    cur=cur->next;
  }
    
}*/

ostream &operator<<(ostream &os, Parameter &p) {
  p.Write(os);
  return os;
}

END_TRAILER



# parameter.h

open hfile, ">parameter.h";
print hfile <<END_HEADER2;
// WARNING: This file is automatically generated by make_parameter_source.pl. Do not edit.
// All edits will be discarded.

#ifndef _PARAMETER_H_
#define _PARAMETER_H_
#include "vector.h"
#include <vector>

#include <libxml/parser.h>
#include <libxml/tree.h>

class Parameter {
  
 public: 
  Parameter();
  ~Parameter();
  void CleanUp(void);
  void Read(const char *filename);
  void Write(ostream &os) const;
  void XMLAdd(xmlNode *root) const;
  void XMLRead(xmlNode *root);
  void AssignValToPar(const char *namec, const char *valc);
  void AssignValArrayToPar(const char *namec, vector<double> valarray);
END_HEADER2

  for ($i=0;$i<$lines;$i++) {
    if ($convtype[$i] ne "label" && $convtype[$i] ne "title") {
      print hfile "  $convtype[$i] $param[$i];\n";
    }
  }

print hfile <<END_TRAILER2;
 private:
};

ostream &operator<<(ostream &os, Parameter &p);
const char *sbool(const bool &p);


#endif
END_TRAILER2

# finally, a parameter file with the default values, based on the template

open parfile,">default.par";

for ($i=0;$i<$lines;$i++) {
  if ($type[$i] ne "title" && $type[$i] ne "label") {
    $value[$i] =~ s/\"//g;
    print parfile "  $param[$i] = $value[$i]\n";
  }
}
