#!/perl -w
# NAME: convstranon.pl
# AIM: Deal with a structrue conversion
# 8/30/2009 - geoff mclane - http://geoffair.net/mperl/
use strict;
use warnings;
require 'logfile.pl' or die "Unable to load logfile.pl ...\n";
# log file stuff
my ($LF);
my $pgmname = $0;
if ($pgmname =~ /\w{1}:\\.*/) {
   my @tmpsp = split(/\\/,$pgmname);
   $pgmname = $tmpsp[-1];
}
my $outfile = "temp.$pgmname.txt";
open_log($outfile);
## prt( "$0 ... Hello, World ...\n" );

my $str_mem = 'member';
my $str_line1 = '--LINE_ONE--';
my $out_file = 'tempsstr.txt';

my @res_words = qw( static typedef struct unsigned int enum const char uint8_t short uint16_t long uint32_t int64_t );
my @warnings = ();

my $do_anon_2 = 0;   # convert the first string

my $ID3v1_GENRE_MAX = 125;

my $gcc_struct_anon2_short = <<EOF;
const char *ff_id3v1_genre_str[$ID3v1_GENRE_MAX + 1] = {
      [0] = "Blues",
      [1] = "Classic Rock",
};
EOF

my $gcc_struct_anon2 = <<EOF;
const char *ff_id3v1_genre_str[$ID3v1_GENRE_MAX + 1] = {
      [0] = "Blues",
      [1] = "Classic Rock",
      [2] = "Country",
      [3] = "Dance",
      [4] = "Disco",
      [5] = "Funk",
      [6] = "Grunge",
      [7] = "Hip-Hop",
      [8] = "Jazz",
      [9] = "Metal",
     [10] = "New Age",
     [11] = "Oldies",
     [12] = "Other",
     [13] = "Pop",
     [14] = "R&B",
     [15] = "Rap",
     [16] = "Reggae",
     [17] = "Rock",
     [18] = "Techno",
     [19] = "Industrial",
     [20] = "Alternative",
     [21] = "Ska",
     [22] = "Death Metal",
     [23] = "Pranks",
     [24] = "Soundtrack",
     [25] = "Euro-Techno",
     [26] = "Ambient",
     [27] = "Trip-Hop",
     [28] = "Vocal",
     [29] = "Jazz+Funk",
     [30] = "Fusion",
     [31] = "Trance",
     [32] = "Classical",
     [33] = "Instrumental",
     [34] = "Acid",
     [35] = "House",
     [36] = "Game",
     [37] = "Sound Clip",
     [38] = "Gospel",
     [39] = "Noise",
     [40] = "AlternRock",
     [41] = "Bass",
     [42] = "Soul",
     [43] = "Punk",
     [44] = "Space",
     [45] = "Meditative",
     [46] = "Instrumental Pop",
     [47] = "Instrumental Rock",
     [48] = "Ethnic",
     [49] = "Gothic",
     [50] = "Darkwave",
     [51] = "Techno-Industrial",
     [52] = "Electronic",
     [53] = "Pop-Folk",
     [54] = "Eurodance",
     [55] = "Dream",
     [56] = "Southern Rock",
     [57] = "Comedy",
     [58] = "Cult",
     [59] = "Gangsta",
     [60] = "Top 40",
     [61] = "Christian Rap",
     [62] = "Pop/Funk",
     [63] = "Jungle",
     [64] = "Native American",
     [65] = "Cabaret",
     [66] = "New Wave",
     [67] = "Psychadelic",
     [68] = "Rave",
     [69] = "Showtunes",
     [70] = "Trailer",
     [71] = "Lo-Fi",
     [72] = "Tribal",
     [73] = "Acid Punk",
     [74] = "Acid Jazz",
     [75] = "Polka",
     [76] = "Retro",
     [77] = "Musical",
     [78] = "Rock & Roll",
     [79] = "Hard Rock",
     [80] = "Folk",
     [81] = "Folk-Rock",
     [82] = "National Folk",
     [83] = "Swing",
     [84] = "Fast Fusion",
     [85] = "Bebob",
     [86] = "Latin",
     [87] = "Revival",
     [88] = "Celtic",
     [89] = "Bluegrass",
     [90] = "Avantgarde",
     [91] = "Gothic Rock",
     [92] = "Progressive Rock",
     [93] = "Psychedelic Rock",
     [94] = "Symphonic Rock",
     [95] = "Slow Rock",
     [96] = "Big Band",
     [97] = "Chorus",
     [98] = "Easy Listening",
     [99] = "Acoustic",
    [100] = "Humour",
    [101] = "Speech",
    [102] = "Chanson",
    [103] = "Opera",
    [104] = "Chamber Music",
    [105] = "Sonata",
    [106] = "Symphony",
    [107] = "Booty Bass",
    [108] = "Primus",
    [109] = "Porn Groove",
    [110] = "Satire",
    [111] = "Slow Jam",
    [112] = "Club",
    [113] = "Tango",
    [114] = "Samba",
    [115] = "Folklore",
    [116] = "Ballad",
    [117] = "Power Ballad",
    [118] = "Rhythmic Soul",
    [119] = "Freestyle",
    [120] = "Duet",
    [121] = "Punk Rock",
    [122] = "Drum Solo",
    [123] = "A capella",
    [124] = "Euro-House",
    [125] = "Dance Hall",
};
EOF

my $str_list = <<EOF;
static const int8_t si_prefixes['z' - 'E' + 1]={
    ['y'-'E']= -24,
    ['z'-'E']= -21,
    ['a'-'E']= -18,
    ['f'-'E']= -15,
    ['p'-'E']= -12,
    ['n'-'E']= -9,
    ['u'-'E']= -6,
    ['m'-'E']= -3,
    ['c'-'E']= -2,
    ['d'-'E']= -1,
    ['h'-'E']=   2,
    ['k'-'E']=   3,
    ['K'-'E']=   3,
    ['M'-'E']=   6,
    ['G'-'E']=   9,
    ['T'-'E']=  12,
    ['P'-'E']=  15,
    ['E'-'E']=  18,
    ['Z'-'E']=  21,
    ['Y'-'E']=  24,
};
EOF

my $struct_size = 0;

sub word_in_array($$) {
	my ($wd,$rar) = @_;
	foreach my $w (@{$rar}) {
		if ($w eq $wd) {
			return 1;
		}
	}
	return 0;
}

sub word_in_res_words($) {
	my ($wd) = shift;
	return word_in_array($wd, \@res_words);
}

sub trim_ends($) {
   my ($t) = shift;
   $t = substr($t,1) while ($t =~ /^\s/);	# clear leading
   $t = substr($t,0,length($t)-1) while ($t =~ /\s$/);	# and trailing
   return $t;
}

sub conv_str_string($) {
   my ($txt) = shift;
   my ($len, $i, $ch, $pc, $lnn, $word, $def, $instr, $name, $line, $isan, $wasan, $msg);
   my ($num, $val, $key, $line1);
   $len = length($txt);
   $ch = '';
   $lnn = 0;
   $name = '';
   prt( "Processing $len chars...\n" );
   $word = '';
   $def = '';
   $instr = 0;
   $line = '';
   $isan = 0;
   $line1 = '';
   my %hash = ();
   for ($i = 0; $i < $len; $i++) {
      $pc = $ch;
      $ch = substr($txt,$i,1);
      $wasan = $isan;
      $isan = ($ch =~ /\w/) ? 1 : 0;
      $msg = "$lnn: ";
      if ($ch eq "\n") {
         $lnn++;
      } elsif ($ch =~ /\s/) {
         if (length($word)) {
            $line .= ' ' if (length($line));
            $line .= $word;
            $word = '';
         }
      } else {
         $word .= $ch;
      }

      if ($ch eq "\n") {
         if (length($word)) {
            $line .= ' ' if (length($line));
            $line .= $word;
            $word = '';
         }
         #prt("$lnn: [$line]$instr\n");
         if ($instr) {
            if ($line =~ /^\s*(\[\s*(\d+)\s*\]){1}\s*=\s*(".+")\s*,*$/) {
               $num = $1;
               # $val = $2;
               $val = $3;
               $num =~ s/^\[//;
               $num =~ s/\]$//;
               $num = trim_ends($num);
               #prt( "Looks good... got [$num] = [$val]\n" );
               $key = sprintf("%s%06d", $str_mem, $num);
               $hash{$key} = $val;
            } elsif (length($line1) == 0) {
               $line1 = $line;
               $hash{$str_line1} = $line1;
               prt( "Set line1 [$str_line1] => [$line1]\n" );
            }
         } else {
            if ($line =~ /\}\s*;/) {
               prt( "Closed on [$line]\n" );
            } else {
               prt( "What is this? [$line]!\n" );
            }
         }
         $word = '';
         $line = '';
      }
      if ($instr) {
         if ($ch eq '}') {
            $instr-- if ($instr);
            #$msg .= "Exit $ch = $instr";
         }
      } else {
         if ($ch eq '{') {
            $instr++;
            #$msg .= "Enter $ch - $instr";
         }
      }
   }
   return \%hash;
}

sub show_hash($) {
   my ($rh) = @_;
   my ($k,$v, $len, $max, $msg, $k2, $tmp, $cnt);
   $max = 0;
   $cnt = 0;
   foreach $k (keys %{$rh}) {
      if ($k =~ /^$str_mem/) {
         $v = ${$rh}{$k};
         $len = length($v);
         $max = $len if ($len > $max);
         $cnt++;
      }
   }
   $max += 3;  # space plus coma, + 1
   $k = $str_line1;
   if (defined ${$rh}{$k}) {
      $v = ${$rh}{$k};
      prt( "$v\n" );
   }
   $len = 0;
   foreach $k (sort keys %{$rh}) {
      if ($k =~ /^$str_mem/) {
         $v = ${$rh}{$k};
         $msg = " $v,";
         $msg .= ' ' while (length($msg) < $max);
         $len++;
         $tmp = sprintf("%3d",$len);
         $k2 = substr($k,length($str_mem));
         $msg .= " /* $tmp ($k2) */";
         prt( "$msg\n" );
      }
   }
   prt( "};\n" );
}

sub conv_str_stg($) {
   my ($stg) = shift;
   my @arr = split(/\n/,$stg);
   my $len = scalar @arr;
   my ($line, $lens, $i, $c, $inq, $wd);
   my ($wd1,$wd2,$wd3);
   my ($owd1,$owd2,$owd3);
   my ($first,$second,$sign,$numb, $key);
   my $lnn = 0;
   prt( "Process $len lines...\n" );
   $line = $arr[0];
   $wd1 = '';
   $wd2 = '';
   $wd3 = '';
   my %hash = ();
   if ($line =~ /\[(.+)\]/) {
      $lens = $1;
      prt( "Count = $lens\n" );
      $len = length($lens);
      $inq = 0;
      $wd = '';
      for ($i = 0; $i < $len; $i++) {
         $c = substr($lens,$i,1);
         next if ($c eq '-');
         next if ($c eq '+');
         if ($inq) {
            if ($c eq "'") {
               $inq = 0;
            }

         }
         if ($c eq "'") {
            $inq = 1;
            $i++;
            $wd = '';
            for (; $i < $len; $i++) {
               $c = substr($lens,$i,1);
               if ($c eq "'") {
                  last;
               }
               $wd .= $c;
            }
            if (length($wd1)) {
               if (length($wd2)) {
                  # nothing
               } else {
                  $wd2 = $wd;
                  $wd = '';
               }
            } else {
               $wd1 = $wd;
               $wd = '';
            }
         } else {
            $wd3 .= $c if !($c =~ /\s/);
         }
      }
      prt( "wd1=[$wd1] wd2=[$wd2] wd3=[$wd3]\n" );
      $owd1 = ord($wd1);
      $owd2 = ord($wd2);
      $owd3 = $wd3;
      prt( "owd1=[$owd1] owd2=[$owd2] owd3=[$owd3]\n" );
      my $value = ($owd1 - $owd2 + $owd3);
      $struct_size = $value;
      $hash{'line1'} = [ $line, $value, 0 ];
      prt( "Struct counter = $value\n" );
      $len = scalar @arr;
      my $oE = ord('E');
      prt( "ord for 'E' is $oE\n" );
      $lnn = 0;
      for ($i = 1; $i < $len; $i++) {
         $lnn++;
         $line = $arr[$i];
         $line = trim_all($line);
         # like ['y'-'E']= -24,
         if ($line =~ /\['(\w+)'-'(\w+)'\]\s*=\s*(-*)(\d+|\s)\s*,/) {
            $first = $1;
            $second = $2;
            $sign = $3;
            $numb = $4;
            #prt( "$1 - $2 = [$3][$4]\n" );
            prt( "$first - $second = [$sign][$numb]\n" );
            if ($second ne 'E') {
               prt( "Second is NOT 'E'!\n" );
               exit(1);
            }
            $value = ord($first) - $oE;
            $key = sprintf("mem%03d",$value);
            if ($sign eq '-') {
               $numb = -$numb;
            }
            $hash{$key} = [$numb, $line, $lnn] ;
         } elsif ($line =~ /\}\s*;/) {
            prt( "Last [$line]\n" );
         } else {
            prt( "WHAT [$line]\n" );
            last;
         }
      }

   }
   return \%hash;
}

sub show_hash2($) {
   my ($rh) = @_;
   my ($k, $v, $n, $o, $num, $lnn);
   my $cnt = 0;
   my $ind = '   ';
   my ($min,$len,$tmp);
   my $msg = '';
   $min = 0;
   foreach $k (keys %{$rh}) {
      $v = ${$rh}{$k};
      $n = ${$v}[0];
      $o = ${$v}[1];
      if ($k =~ /^mem/) {
         $tmp = "$ind $n,";
         $len = length($tmp);
         $min = $len if ($len > $min);
      }
   }
   prt( "min width = $min\n" );
   foreach $k (sort keys %{$rh}) {
      $v = ${$rh}{$k};
      $n = ${$v}[0];
      $o = ${$v}[1];
      if ($k eq 'line1') {
         $tmp = "$n  /* $o members */\n";
         prt($tmp);
         $msg .= $tmp;
      } else {
         $lnn = ${$v}[2];
         $num = substr($k,3);
         while ($num > $cnt) {
            $tmp = "$ind 0,";
            $tmp .= ' ' while (length($tmp) < $min);
            $tmp = "$tmp /* $cnt = blank */\n";
            prt($tmp);
            $msg .= $tmp;
            $cnt++;
         }
         # prt( "$k => $n /* $cnt = $o */\n" );
         $tmp = "$ind $n,";
         $tmp .= ' ' while (length($tmp) < $min);
         $tmp = "$tmp /* $cnt = $o ($lnn) */\n";
         prt($tmp);
         $msg .= $tmp;
         $cnt++;
      }
   }
   $tmp = "}; /* $cnt members */\n";
   prt($tmp);
   $msg .= $tmp;
}


if ($do_anon_2) {
   my $ref_hash = conv_str_string($gcc_struct_anon2);
   show_hash($ref_hash);
} else {
   my $rh = conv_str_stg($str_list);
   my $str = show_hash2($rh);
   write2file($str,$out_file);
   prt( "Written out file $out_file...\n" );
   system($out_file);
}

close_log($outfile,1);
exit(0);

# eof
