#!/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"
#include "warning.h"
#include <QLocale>
#include <QDir>
#include <QStringList>

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;
  // reassign datadir outside the loop
  QDir dataDir(datadir);
  if (dataDir.isRelative()){
    QStringList path;
    path << QDir::homePath() << dataDir.dirName();
    datadir = strdup((char *) path.join("/").toStdString().c_str());
  }
}

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;
print cppfile "  QLocale standardlocale(QLocale::C);\n";
print cppfile "  bool ok;\n";
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] = standardlocale.toInt(valc, &ok);\n";
		    print cppfile "  if (!ok) { MyWarning::error(\"Read error: cannot convert string \\\"%s\\\" to integer while reading parameter '$param[$i]' from XML file.\",valc); }\n";
		    # print cppfile "  $param[$i] = (int)strtol(valc, 0, 10);\n";
		} else {
		    # print cppfile "  $param[$i] = strtod(valc, 0);\n";
		    print cppfile "  $param[$i] = standardlocale.toDouble(valc, &ok);\n";
		    print cppfile "  if (!ok) { MyWarning::error(\"Read error: cannot convert string \\\"%s\\\" to double while reading parameter '$param[$i]' from XML file.\",valc); }\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 <<END_DATADIR;
// reassign datadir outside the loop
if (!strcmp(namec, "datadir")) {
  if (datadir) { free(datadir); }
  QDir dataDir(datadir);
  if (dataDir.isRelative()){
    QStringList path;
    path << QDir::homePath() << dataDir.dirName();
    datadir = strdup((char *) path.join("/").toStdString().c_str());
  }
 }
END_DATADIR

print cppfile "}\n";

print cppfile <<END_TRAILER;

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";
  }
}

# finis
