#!/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>

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

# finis
