#!/usr/bin/env perl


use XML::Parser;
use Data::Dumper;
use Date::Calc;

my $file = shift;
my $Author="MagicsTeam";
my $ecmwf="http://wms.ecmwf.int";
my $info = {};
my $element = {};


my %basetype = (
	"int" => 1,
	"magint" => 1,
	"double" => 1,
	"float" => 1,
	"magfloat" => 1,
	"long" => 1,
	"bool" => 1,
    "string" => 1,
    "magstring" => 1,    
    "FloatArray" =>1,
    "magfloatarray" =>1,
    "StringArray" => 1,
    "magstringarray" => 1,
    "IntArray" => 1,
    "magintarray" => 1, 
    "LineStyle" =>1,
    "Hemisphere" =>1,
    "ArrowPosition" => 1,
    "Justification" => 1,
    "OpenGLDriverObserverPtr" => 1,
    "Widget" =>1, 
    "Matrix" => 1,
    "GribHandlePtr" =>1,

);

my %magtype = (
	"int" => "magint",
	"long" => "magint",
	"float" => "magfloat",
	"double" => "magfloat",
    "string" => "magstring",
    "FloatArray" => "magfloatarray",
    "StringArray" => "magstringarray",
    "IntArray" => "magintarray",
);

my %arraytype = (
	"magfloatarray" => "atof(data)",
	"magstringarray" => "data",
	"magintarray" => "atoi(data)",
);

my %quote = (

    "string" => 1,
    "magstring" => 1,
    
);

sub parse
{
    my $def = shift;
    my $node = shift;   
   
    while ( defined ( $element = shift @{ $node } ) )
    {
       
        my $child = shift @{ $node };
        if ( ref $child )
        {
           my $attr = \%{ shift @{ $child } };
           my $name = $attr->{name};
           
           if ($name ne "") 
           {
               
               $def->{$element}->{$name} = {};
               my $list = $element . "_list";
               push( @{$def->{$list}}, $name);
               foreach my $a (keys %{$attr}) 
               {
                   $def->{$element}->{$name}->{attributes}->{$a} = $attr->{$a}; 
                  
               }
               parse($def->{$element}->{$name}, $child); 
               
           }
           else
           {
                   $def->{$element} = {};
                   foreach my $a (keys %{$attr}) 
                   {
                        $def->{$element}->{attributes}->{$a} = $attr->{$a};
                       
                   }
                   parse($def->{$element}, $child); 
           }
        }
        else 
        {
         
          $def->{data} = $child;
        }
    }   
}





my $xml= new XML::Parser(Style=>"Tree");
  
parse ($info, $xml->parsefile($file));

foreach my $static (keys %{$info->{magics}->{static}}) 
{
	my $name = $info->{magics}->{static}->{$static}->{attributes}->{value};
	if ( $name eq '') {
		$name = $info->{magics}->{static}->{$static}->{attributes}->{name};
	}
	my $class = $info->{magics}->{static}->{$static}->{attributes}->{class};
	my $base = $info->{magics}->{static}->{$static}->{attributes}->{base};
	my $tpl_class = $info->{magics}->{static}->{$static}->{attributes}->{template_class};
	my $tpl_base = $info->{magics}->{static}->{$static}->{attributes}->{template_base};
	print "#include \"$base.h\"\n" unless  $info->{magics}->{static}->{$static}->{attributes}->{include_base} eq "";
	print "#include \"$class.h\"\n" unless  $info->{magics}->{static}->{$static}->{attributes}->{include_class} eq "";

	
	
	
	
}
foreach my $static (keys %{$info->{magics}->{static}}) 
{
	my $name = $info->{magics}->{static}->{$static}->{attributes}->{value};
	if ( $name eq '') {
		$name = $info->{magics}->{static}->{$static}->{attributes}->{name};
	}
	my $class = $info->{magics}->{static}->{$static}->{attributes}->{class};
	my $base = $info->{magics}->{static}->{$static}->{attributes}->{base};
	my $tpl_class = $info->{magics}->{static}->{$static}->{attributes}->{template_class};
	my $tpl_base = $info->{magics}->{static}->{$static}->{attributes}->{template_base};
	my $dclass = $class;
	$dclass = "$class<$tpl_class>" unless $tpl_class eq "";
	$class = "$class\_$tpl_class" unless $tpl_class eq "";
	$base = "$base<$tpl_base> " unless $tpl_base eq "";
	if ($base ne "") { 
		print "static SimpleObjectMaker<$dclass, $base> $class\_$name(\"$name\");\n";
	}
	else {
		$dclass = "$dclass " unless $tpl_class eq "";
		print "static SimpleObjectMaker<$dclass> $class\_$name(\"$name\");\n";
	}
	
	
	
}

print "\n";

foreach my $object (keys %{$info->{magics}->{class}}) 
{
   
    $current = $info->{magics}->{class}->{$object};
    $directory = $info->{magics}->{class}->{$object}->{attributes}->{directory};
    

######################################################
#####                Include file

    open STDOUT, ">tools/src/$directory/$object\Attributes.h";
    
    (my $year,my $month,my $day) = Date::Calc::Today();
    my $string = Date::Calc::Date_to_Text($year, $month, $day);
    print  <<EOF;

/*! \\file $object\Attributes.h
    \\brief Definition of $object Attributes class.
    Automatically generated on $string
    Do Not Edit!
    
    Magics Team - ECMWF 2004
   
    Created: $string
    
*/
   

#ifndef $object\Attributes_H
#define $object\Attributes_H

#include "magics.h"
#include "ParameterManager.h"
#include "Factory.h"


EOF
my $includes = {};
    my $impl=$current->{attributes}->{implements};
    print "#include \"$impl.h\"\n" if $impl ne ''; 
    my $parent ='';
    $parent=": public $impl" if $impl ne ''; 
    foreach my $param (@{$current->{parameter_list}}) 
    {   
       my $todo = $current->{parameter}->{$param}->{attributes}->{implemented};
       next if $todo eq 'no';
       my $to = $current->{parameter}->{$param}->{attributes}->{to};
     
       $includes->{$to} = "find" unless $basetype{$to};
    }
    foreach my $include (keys %{$includes} ) 
    {
          print "#include \"$include.h\"\n"; 
    }
    
    print "\n";
  
    
    my $template = $current->{attributes}->{template};
    my $class = "$object\Attributes";
    my $line = "";
    if ( $template ne "" ) {
        $line = "template <class $template>";
        $class = "$object\Attributes<$template>";
        
    }
    
    print <<EOF;
namespace magics {

class XmlNode;


$line
class $object\Attributes $parent
{
public:
//  --  constructor
    $object\Attributes();
    
//  --  destructor
    virtual ~$object\Attributes();
    
    virtual void set(const map<magstring, magstring>&);
    virtual void set(const XmlNode&);
    virtual void copy(const $object\Attributes&);
    
    void setTag(const magstring& tag) { tag_ = tag; }
    
   
    
EOF
    my $xml_data = $current->{attributes}->{xml_data};
    if ($xml_data eq '' ) {
        print <<EOF;
    void setXmlData(const magstring&)  {}
EOF
    }
    else {
    	my $to = $current->{parameter}->{$xml_data}->{attributes}->{to};
    	my $member = $current->{parameter}->{$xml_data}->{attributes}->{member};
    	if ( $arraytype{$to} ) {    		
    		print("\tvoid setXmlData(const magstring& data)  { $member\_.push_back($arraytype{$to}); } \n");
    	}
    	else {
    		print("\tvoid setXmlData(const magstring& data)  { $member\_ = data; }\n");
    	}
    }
    
   
    
    foreach  my $param (@{$current->{parameter_list}}) 
    {      
        my $todo = $current->{parameter}->{$param}->{attributes}->{implemented};
        next if $todo eq 'no';
        my $name = $current->{parameter}->{$param}->{attributes}->{name};
        my $default = $current->{parameter}->{$param}->{attributes}->{default};
        my $member = $current->{parameter}->{$param}->{attributes}->{member};
        my $from = $current->{parameter}->{$param}->{attributes}->{from};
        my $to = $current->{parameter}->{$param}->{attributes}->{to};
        my $tpl = $current->{parameter}->{$param}->{attributes}->{template};
        $to = "$to<$tpl> " unless $tpl eq "";
       
        my $value = lc $member;
        my $method = ucfirst $member;
        
        print <<EOF;
/*!
 *   Magics Information:
 *     - Parameter name: $name 
 *     - Default: $default
 */
EOF
    my $magto = $to;
    $magto = $magtype{$magto} if $magtype{$magto};
    

    if ( $basetype{$to} ) 
    { 
        print <<EOF;   
	void set$method($magto $value) 
		{ $member\_ =  $value; } 
	$magto get$method() const 
		{ return $member\_; } 
        
EOF
    }
    else
    {
        print <<EOF;   
	void set$method($to* $value) 
		{ unique_ptr<$to> tmp($value); $member\_ = tmp; } 
	const $to&  get$method() const 
		{ return *$member\_; }
     
EOF
    }
    
    }

    print <<EOF;
protected:
//  --  method
	virtual void print(ostream&) const;
	virtual void toxml(ostream&, magint = 0) const;
	
//  --  members:
EOF
     print "\tmagstring $xml_data\_;\n" unless $xml_data eq ''; 
     foreach  my $param (@{$current->{parameter_list}}) 
    {      
        my $todo = $current->{parameter}->{$param}->{attributes}->{implemented};
        next if $todo eq 'no';
        my $member = $current->{parameter}->{$param}->{attributes}->{member};
        my $to = $current->{parameter}->{$param}->{attributes}->{to};
        my $tpl = $current->{parameter}->{$param}->{attributes}->{template};
        $to = "$to<$tpl> " unless $tpl eq "";
        
        if ( $magtype{$to} )
        { 
            print "\t$magtype{$to} $member\_;\n";
        }
        elsif ( $basetype{$to} ) 
        { 
            print "\t$to $member\_;\n";
        }
        else 
        {
            print "\tunique_ptr<$to> $member\_;\n";  
        }
    }

print <<EOF;   

private:
  	magstring tag_;
	friend ostream& operator<<(ostream& s,const $class& p)
	{ p.print(s); return s; }
};

} // namespace magics
EOF

    if ( $template ne "" ) {
        print "#include \"$object\Attributes.cc\" \n";
    }
    
    print "\n#endif\n";
 
    close STDOUT;
    
#####                Include file
######################################################

######################################################
#####                Source file

    open STDOUT, ">tools/src/$directory/$object\Attributes.cc";
    
 
    print  <<EOF;
/*! \\file $object\Attributes.h
    \\brief Implementation of $object Attributes class.
    Automatically generated on $string
    Do Not Edit!

    Created: $string
*/    

    
#include "$object\Attributes.h"
#include "MagicsParameter.h"
#include "Factory.h"
#include "Translator.h"
#include "XmlNode.h"

using namespace magics;

EOF
   $header = "";
    $p = "";
   if ( $template ne "" ) {
        $header = "template <class $template>";
        $p = "<P>";
    }
    
    my $count = 0;
    
    foreach  my $param (@{$current->{parameter_list}}) 
    {      
        my $todo = $current->{parameter}->{$param}->{attributes}->{implemented};
        my $tpl = $current->{parameter}->{$param}->{attributes}->{template};
        next if $todo eq 'no';
        next if $tpl  ne "";
        $count++;
    }
    
    my $subclass = ":";
    $subclass = "" if $count eq 0; 
    
    print "$header\n$object\Attributes$p\::$object\Attributes()$subclass"; 
    my $sep = ""; 
    foreach  my $param (@{$current->{parameter_list}}) 
    {      
        my $todo = $current->{parameter}->{$param}->{attributes}->{implemented};
        next if $todo eq 'no';
        
        my $name = $current->{parameter}->{$param}->{attributes}->{name};
        my $default = $current->{parameter}->{$param}->{attributes}->{default};
        my $member = $current->{parameter}->{$param}->{attributes}->{member};
        my $from = $current->{parameter}->{$param}->{attributes}->{from};
        my $to = $current->{parameter}->{$param}->{attributes}->{to};
        my $tpl = $current->{parameter}->{$param}->{attributes}->{template};
        my $constant= $current->{parameter}->{$param}->{attributes}->{constant};
        next if $tpl ne "";
        $to = "$to<$tpl> " unless $tpl eq "";
        my $magto = $to;
        $magto = $magtype{$to} if $magtype{$to};
        $from = $magtype{$from} if $magtype{$from};
        if ($constant eq "yes" ) 
        {
        	print "$sep\n\t$member\_(Translator<$from, $magto>()(\"$default\"))"; 
        }
        else {
        	print "$sep\n\t$member\_(Translator<$from, $magto>().magics(\"$name\"))"; 
        }
		$sep = ",";
        
        
    }
	print "\n";
    print <<EOF;
{
EOF
   foreach  my $param (@{$current->{parameter_list}}) 
    {   
    	my $todo = $current->{parameter}->{$param}->{attributes}->{implemented};
        next if $todo eq 'no';
        my $tpl = $current->{parameter}->{$param}->{attributes}->{template};
        next if $tpl eq "";
        my $name = $current->{parameter}->{$param}->{attributes}->{name};
        my $default = $current->{parameter}->{$param}->{attributes}->{default};
        my $member = $current->{parameter}->{$param}->{attributes}->{member};
        my $from = $current->{parameter}->{$param}->{attributes}->{from};
        my $to = $current->{parameter}->{$param}->{attributes}->{to};
        my $method = ucfirst $member;
        
        $to = "$to<$tpl> " unless $tpl eq "";
        my $magto = $to;
        $magto = $magtype{$to} if $magtype{$to};
        $from = $magtype{$from} if $magtype{$from};
        print "\n\tTranslator<magstring, $magto> $member;\n";
        print "\tset$method($member.magics(\"$name\"));\n";
       
		
   }
   print <<EOF;
} 


$header
$object\Attributes$p\::~$object\Attributes()
{
}

$header    
EOF
	$val = 0;
    foreach  my $param (@{$current->{parameter_list}}) 
    {
        my $todo = $current->{parameter}->{$param}->{attributes}->{implemented};
        next if $todo eq 'no';
        my $name = $current->{parameter}->{$param}->{attributes}->{name};
        my $default = $current->{parameter}->{$param}->{attributes}->{default};
        my $member = $current->{parameter}->{$param}->{attributes}->{member};
        my $from = $current->{parameter}->{$param}->{attributes}->{from};
        my $to = $current->{parameter}->{$param}->{attributes}->{to};
        my $tpl = $current->{parameter}->{$param}->{attributes}->{template};
        my $xml = $current->{parameter}->{$param}->{attributes}->{xml};
        
        $to = "$to<$tpl> " unless $tpl eq "";
        my $magto = $to;
        my $method = ucfirst $member;
        $magto = $magtype{$to} if $magtype{$to};
        $from = $magtype{$from} if $magtype{$from};
        
        if ( $basetype{$to} ) 
        { 
        	if ($val == 0) {
        		$val =1;
        		
				print "void $object\Attributes$p\::set(const map<magstring, magstring>& params)\n{\n";
        		print"\tmap<magstring, magstring>::const_iterator val;\n\n";
        	}
        	
        	print "\tval = params.find(\"$name\");\n";
        	print "\tif ( val != params.end() ) { \n";
        	print "\t\tMagLog::info() << \"Parameter $name set to \" << val->second << \"\\n\"; \n";        
            print "\t\tset$method(Translator<magstring, $magto>()(val->second));\n"; 
            print "\t}\n";
        }
       
       
      
        if ( $xml ne "" && $xml ne $name) {
        	if ( $basetype{$to} ) 
            { 
            	print "\tval = params.find(\"$xml\");\n";
            	print "\tif ( val != params.end() ) { \n";
            	print "\t\tMagLog::info() << \"Parameter $name set to \" << val->second << \"\\n\"; \n";
          
                 print "\t\tset$method(Translator<magstring, $magto>()(val->second));\n"; 
                 print "\t}\n";
            }
            
           
            
        }
       
       
        
    }
     if ($val==0) {
        		print "void $object\Attributes$p\::set(const map<magstring, magstring>&) \n{\n";

        	}
     print  <<EOF;
} 

$header
void $object\Attributes$p\::copy(const $object\Attributes& other)
{
EOF

    foreach  my $param (@{$current->{parameter_list}}) 
    {
        my $todo = $current->{parameter}->{$param}->{attributes}->{implemented};
        next if $todo eq 'no';
        my $name = $current->{parameter}->{$param}->{attributes}->{name};
        my $default = $current->{parameter}->{$param}->{attributes}->{default};
        my $member = $current->{parameter}->{$param}->{attributes}->{member};
        my $from = $current->{parameter}->{$param}->{attributes}->{from};
        my $to = $current->{parameter}->{$param}->{attributes}->{to};
        my $tpl = $current->{parameter}->{$param}->{attributes}->{template};
        my $xml = $current->{parameter}->{$param}->{attributes}->{xml};
         my $xml_data = $current->{attributes}->{xml_data};
        $to = "$to<$tpl> " unless $tpl eq "";
        my $magto = $to;
        my $method = ucfirst $member;
        $magto = $magtype{$to} if $magtype{$to};
        $from = $magtype{$from} if $magtype{$from};
        
        if ( $basetype{$to} ) 
        {         	
             print "\t$member\_ = other.$member\_;\n"; 
        }
        else 
        {
           print "\tset$method(other.$member\_->clone());\n";          
        }
       
        print "";
       
        
    }
     print  <<EOF;
} 

$header
void $object\Attributes$p\::set(const XmlNode& node)
{
	set(node.attributes());
EOF
	if ($xml_data ne '') {
		print "\n\tfor (XmlNode::DataIterator data = node.firstData(); data != node.lastData(); ++data)\n";
		print "\t\tsetXmlData(*data);\n";	
	}
	print "\n";
	print "\tfor (XmlNode::ElementIterator elt = node.firstElement(); elt != node.lastElement(); ++elt) {\n";
    		foreach  my $param (@{$current->{parameter_list}}) {
    			 my $todo = $current->{parameter}->{$param}->{attributes}->{implemented};
    			next if $todo eq 'no';
    			my $to = $current->{parameter}->{$param}->{attributes}->{to};
    			next if $basetype{$to};
    			my $member = $current->{parameter}->{$param}->{attributes}->{member};
    			my $tpl = $current->{parameter}->{$param}->{attributes}->{template};
    			$to = "$to<$tpl> " unless $tpl eq "";
    			my $magto = $to;
        		my $method = ucfirst $member;
        		$magto = $magtype{$to} if $magtype{$to};
    			
    			print "\t\ttry {\n";
    			print "\t\t\t$magto* $member = Translator<magstring, $magto>()((*elt)->name());\n";
    			print "\t\t\t$member->set(*(*elt));\n";
                print "\t\t\tset$method($member);\n";
    			print "\t\t}\n";
    			print "\t\tcatch (NoFactoryException& e) {}\n";
       
    	}
    
	

print <<EOF; 		
	}
}
$header
void $object\Attributes$p\::print(ostream& out)  const
{
	out << "$object\Attributes[";
EOF
    $sep = "";
    foreach  my $param (@{$current->{parameter_list}}) 
    {      
         my $todo = $current->{parameter}->{$param}->{attributes}->{implemented};
       next if $todo eq 'no';
        my $name = $current->{parameter}->{$param}->{attributes}->{name};
        my $member = $current->{parameter}->{$param}->{attributes}->{member};
        my $to = $current->{parameter}->{$param}->{attributes}->{to};
        my $tpl = $current->{parameter}->{$param}->{attributes}->{template};
       
       
        $to = "$to<$tpl> " unless $tpl eq "";
        $print = "$member\_";
        $print = "*$member\_" unless  $basetype{$to} ;
        
        print "\tout << \"$sep$member = \" << $print;\n"; 
        
        $sep = ", ";
        
    }
        
    print "\tout << \"]\" << \"\\n\";\n";
    print "}\n\n";
print <<EOF; 
$header
void $object\Attributes$p\::toxml(ostream& out, magint tabs)  const
{
	string tab;
	for ( magint t = 0; t < tabs; tabs++) tab = tab + "\t";
	
	out << tab << "<" << tag_;
EOF
    $sep = " ";
     my $calls = "";
    
    foreach  my $param (@{$current->{parameter_list}}) 
    {
        my $todo = $current->{parameter}->{$param}->{attributes}->{implemented};
        next if $todo eq 'no';
        my $name = $current->{parameter}->{$param}->{attributes}->{name};
        my $default = $current->{parameter}->{$param}->{attributes}->{default};
        my $member = $current->{parameter}->{$param}->{attributes}->{member};
        my $from = $current->{parameter}->{$param}->{attributes}->{from};
        my $to = $current->{parameter}->{$param}->{attributes}->{to};
        my $tpl = $current->{parameter}->{$param}->{attributes}->{template};
        my $xml = $current->{parameter}->{$param}->{attributes}->{xml};
         my $xml_data = $current->{attributes}->{xml_data};
        $to = "$to<$tpl> " unless $tpl eq "";
        my $magto = $to;
        my $method = ucfirst $member;
        $magto = $magtype{$to} if $magtype{$to};
        $from = $magtype{$from} if $magtype{$from};
       
        if ( $basetype{$to} ) 
        {         	
             print "\tout << tab << \"\\t$xml = \\\'\" << $member\_ << \"\\\'\\n\";\n"; 
        }
        else {
        	 $calls = "$calls \t$member.toxml(out, tabs+1);\n";
        	
        }
       
        
      
        print "";

    }
    print "\tout << tab << \">\";\n\n";
    print $calls;
    
    print "\n\tout << tab << \"</\" << tag << \">\\n\";\n";

  
        
    
    print "}\n\n";
    
 

    foreach  my $param (@{$current->{parameter_list}}) 
    {     
         my $todo = $current->{parameter}->{$param}->{attributes}->{implemented};
       next if $todo eq 'no';
        my $name = $current->{parameter}->{$param}->{attributes}->{name};
        my $member = $current->{parameter}->{$param}->{attributes}->{member};
        my $from = $current->{parameter}->{$param}->{attributes}->{from};
        my $default = $current->{parameter}->{$param}->{attributes}->{default};
        my $migration = $current->{parameter}->{$param}->{migration}->{data};
        $from = $magtype{$from} if $magtype{$from};
        
        
        $default = "\"$default\"" if  $quote{$from} ;
     
        print <<EOF
static MagicsParameter<$from> $name("$name", $default, "$migration");
EOF
        
    }
        
   
}


