#!/usr/bin/perl

use warnings;

use lib qw(.);
use XML::LibXML;
use Getopt::Long;

use SSXMLUtils;

my $pgnam=$0;                # program name
my @VERSION=('major'=>1,
             'minor'=>3,
             'date'=>04172014);

#
# Process the command line
#
my $DEBUG=0;
my ($VERBOSE,$HELP,$VERSION);
my $XML='on';
my $SVM;
{ # process options
    GetOptions(
        "xml=s"     => \$XML,
        "svm=s"     => \$SVM,
        "verbose"   => \$VERBOSE,
        "debug"     => \$DEBUG,
        "version"   => \$VERSION,
        "help"      => \$HELP,
        );
}

(&help_print() && exit) if $HELP;;
if($VERSION){
    print STDERR "@VERSION\n";
    exit;
}

if(@ARGV < 2){
    die "Usage: $pgnam [options ...] tbmx_input_file batml_output_file
options:
  --xml=(on|off)
  --svm=file
  --verbose
  --debug
  --version
  --help\n";
}

($fin,$fout)=@ARGV;
open $IFILE, $fin or die $!;
$tbmxdoc = XML::LibXML->load_xml(IO => $IFILE);
close $IFILE;

if($SVM){
    open($SVMFILE, '>', $SVM) or die $!;
}

my $filename='tbmx2batmlSVM.map';
open $IFILE, $filename or die "Cannot open file $filename\n";

#
# BATML header
#
my $batmldoc = XML::LibXML::Document->new('1.0', 'utf-8');
my $root = $batmldoc->createElement("BatteryML_Doc");
$batmldoc->setDocumentElement($root);
my $aroot = $batmldoc->createAttributeNS( "BatteryML:2.0" , "name", $filename);
$root->setAttributeNodeNS( $aroot );

#
# Root documentation
#
{
    my $tag = $batmldoc->createElement('Documentation');
    $tag->appendTextNode("Converted from BDS XML file $filename");
    $root->appendChild($tag);
}

#
# Build default BatML tree
#
my @DBS=(
    'BuildingBlockDB',
    'CellSandwichDB',
    'CellDB',
    'ModuleDB',
    'PackDB',
    'PartDB',
    'DeviceDB',
    'ModelDB',
    'UnitsDB',
    'SimulationDB',
);
foreach my $dbname (@DBS) {
    my $tag = $batmldoc->createElement($dbname);
    $root->appendChild($tag);
}

# open xpath file
my $lcount=0;
 READ: while (<$IFILE>) {       # main read loop
     next if /^#/;
     s/^\s+//;
     s/\s+$//;              # remove leading and trailing blanks
     $lcount++;
     $iline=$lcount%4;
     if($iline==1){
#	 my $svm;
	 undef $scale;
	 ($svm, $unit, $dtype, $scale)=split;
	 print "--NEW svm: $svm unit: $unit data: $dtype\n"  if $VERBOSE;
	 next;
     }
     if($iline==2){
	 $tbmxpath=$_;
	 $tbmxpath=~s/'//g;
	 @nodes=$tbmxdoc->findnodes( $tbmxpath );
	 $nn=@nodes;
	 print "tbmxpath: [$nn] $tbmxpath\n"  if $VERBOSE;
	 next;
     }
     if($iline==3){
	 $batmlxpath=$_;
	 $batmlxpath=~s/'//g;
	 print "batmlxpath: $batmlxpath\n"  if $VERBOSE;
	 if(@nodes == 0){
	     print "$svm ----NOT FOUND\n"  if $VERBOSE;
	     print $SVMFILE "$svm = 0; \% NOT FOUND IN THE TBM FILE \[$unit\]\n" if $SVM;
	     next;
	 }
	 my $BBDB_params=elFromXPathWithAttr($root,$batmlxpath );   # autovivify node, return it
	 if($dtype eq 'scalar'){
	     $node=$nodes[0];
	     $content = $node->textContent;
	     $content=~s/\s+.*$//;
	     if(defined($scale)){
		 $content *= $scale;
	     }
	     print "content [scalar]: $content\n"  if $VERBOSE;
	     $BBDB_params->appendTextNode( $content );

	     print $SVMFILE "$svm = $content; \% \[$unit\]\n" if $SVM;

	 }
	 if($dtype eq 'array'){
	     my ($iref, $aref);
	     ($iref, $aref)=&reorder_valuearray(\@nodes, 'idx');
	     if(defined($scale)){
		 foreach $ia ( @{$aref} ){
		     $ia *= $scale;
		 }
	     }
	     print "content [array]: @{$aref}\n"  if $VERBOSE;
	     $BBDB_params->appendTextNode( "@{$aref}" );

	     $" = ', ';
	     print $SVMFILE "$svm = \[ @{$aref} \];  \% \[$unit\]\n" if $SVM;

	 }
	 next;
     }
     if($iline==0){
	 $unitxpath=$_;
	 $unitxpath=~s/'//g;
	 print "unitxpath: $unitxpath\n"  if $VERBOSE;
	 next if @nodes == 0;
	 my $BBDB_params=elFromXPathWithAttr($root,$unitxpath );   # autovivify node, return it
	 $BBDB_params->appendTextNode( $unit );
	 next;
     }
}

$format=1;
my $state;
$state = $batmldoc->toFile($fout, $format);

sub reorder_valuearray{
    my ($nodes, $idxname) = @_;
    my %aryh;
    my @index;
    my @value;

    foreach ( @{ $nodes } ){
        my $k=$_->getAttribute( $idxname );
        my $v=$_->textContent;
        $aryh{ "$k" } = $v;
    }

    my @an=sort by_number keys(%aryh);
    foreach ( @an ){
        push @index, $_;
        push @value, $aryh{$_};
    }

    return (\@index, \@value);
}

sub by_number {
    $a <=> $b;
}

sub help_print{
    print STDERR <<EOF

Converts TBM XML input file (tbmx_input_file) into BatteryML battery model
file (batml_output_file).

Program errors messages indicate errors in the input file.

Program switches:

--help    This help.

--xml=(on|off)
          Write XML declaration as the first line of the ParameterList
          XML file. Default=on.

--svm=filename
          Write SVM file.

--verbose Add processing printouts as the code executes.

--debug   Create debug files for finding the errors in the converter
          program. Does not help much in tracing invalid input.

EOF
}
