#!/usr/bin/env perl
#
# ctdb_y2h - convert CTDB info from YAML to HTML
#
# Generates:
#
#   ndx_*.html	packet index pages
#   tc_*.html	telecommand packet summary pages
#   tm_*.html	telemetry   packet summary pages
#
# Note: ctdb_y2t is VERY similar, but generates tbl/troff, instead.
#
# Written by Rich Morin, rdm@slac.stanford.edu

use Data::Dumper;

use strict;
use warnings;

require 'fsw_docs.pl';

{
    my (@DIV, @desc,
        %CHAP, %HEP, %TITLE,
        %desc, %desc2, %h, %pkt, %type,
        $CTDB, $D, $HDR, $O,
        $b_hi, $b_lo, $bd, $chap, $count, $field, $ftf,
        $last, $ndx, $offset, $out,
        $p_desc, $p_field, $p_len, $p_pkt, $p_type, $pkt,
        $r_ctdb, $r1, $r2, $r3, $r4, $sect, $size,
        $t0, $t1, $t2, $t3, $t4, $type);

    $CTDB = 'edit/ctdb.yml';
    $D     = 40;
    $O     = 50;

    $HDR = <<EOT;
  </head>
<!--  This is generated HTML.  Do NOT edit this file!
      See bin/ctdb_y2h and edit/ctdb.yml, instead.
-->
  <body>
    <img src="../images/LAT-FSW-logo.gif">

    <hr>
    <p>
      <a href="http://www-glast.slac.stanford.edu"
        >GLAST/LAT</a> &gt;

      <a href="http://www-glast.slac.stanford.edu/Elec_DAQ/Elec_DAQ_docs.htm"
        >DAQ and FSW</a> &gt;

      <a href="../FSW_home.html"
        >FSW</a> &gt;

EOT

    @DIV = ('A' .. 'Z');

    %CHAP  = (tc => 'Telecommand',
              tm => 'Telemetry'
             );

    # Hand-Edited Pages

    %HEP   = (tc => '../edit/ctdb_tc_hdr.html',
              tm => '../edit/ctdb_tm_hdr.html'
             );

    %TITLE = (name       => 'name',
              field_name => 'name, within field',
              sect_name  => 'name, within manual section',
              type_name  => 'name, within type'
             );

    # Get CTDB information.

    $r_ctdb = load_ctdb($CTDB);

    # Write tele{command,metry} pages.

    foreach $chap qw(tc tm) {

        foreach $type (sort(keys(%{ $r_ctdb->{ $CHAP{$chap} } }))) {

            $r1 = $r_ctdb->{ $CHAP{$chap} }{$type};

            $type{ $r1->{Section} } = $type;

            $r2 = $r1->{Header};

            ($p_type = $type) =~ s|_| |g;
 
            next unless (defined($r2->[4]));				# KLUDGE

            undef %h;
            $h{Version} = (split(/,/, $r2->[0]))[1];
            $h{T}       = (split(/,/, $r2->[1]))[1];
            $h{SH}      = (split(/,/, $r2->[2]))[1];

            if ($chap eq 'tc') {
                $h{APID}    =           (split(/,/, $r2->[3]))[1];
                $h{SF}      = 'SF = ' . (split(/,/, $r2->[4]))[1];
            }

            foreach $pkt (sort(keys(%{ $r1->{Packets} }))) {

                ($p_pkt = $pkt) =~ s|_| |g;
                $r2     =  $r1->{Packets}{$pkt};
                $sect   =  $r2->{Section};

                if ($chap eq 'tm') {
                    $h{APID} = $r2->{APID};
                    $h{SF}   = (defined($r2->{SF})) ? "SF = $r2->{SF}"
                                                    : 'SF';
                }

                $p_desc  =  ($r2->{Name} ne $p_pkt) ? "$r2->{Name}."
                                                    : '';

                $p_desc .= "  $r2->{Description_0}"
                    if (defined($r2->{Description_0}));

                $p_desc .= "  <p>\n  $r2->{Description_1}"
                    if (defined($r2->{Description_1}));

                $t1    = $r2->{Packet_Length};
                $p_len = ($t1 =~ m|^\d+$|) ? " = $t1"
                                           : ": $t1";

                $t2 = "        <td width=$O align=center>offset</td>\n";
                for ($t3=15; $t3>=0; $t3--) {
                    $t2 .= "        <td width=$D align=center>$t3</td>\n";
                }

                chomp($p_desc);
                chomp($t2);

                $out = <<EOT;
<html>
  <head>
    <title>"$p_pkt" $CHAP{$chap} Packet</title>
$HDR
      <b>"$p_pkt" $CHAP{$chap} Packet</b>
    <p><hr>

    <h3>$sect&nbsp;&nbsp;&nbsp;&nbsp;$p_pkt</h3>
    <p><b>Packet Type:</b>   $p_type $CHAP{$chap}<br>
    <p><b>Description:</b>
    <ul>
      $p_desc
    </ul>
    <p><b>Layout:</b>
    <p>

    <basefont size="1">
    <table border="1" cellspacing="1">
      <tr bgcolor=lightgrey>
$t2
      </tr><tr>
        <td width=$O align=center bgcolor=lightgrey>0x00</td>
        <td align=center colspan=3
           ><a href="$HEP{$chap}#Version">Version = $h{Version}</a></td>
        <td align=center
           ><a href="$HEP{$chap}#T">T=$h{T}</a></td>
        <td align=center
           ><a href="$HEP{$chap}#SH">SH=$h{SH}</a></td>
        <td align=center colspan=11
           ><a href="$HEP{$chap}#APID">APID = $h{APID}</a></td>
      </tr><tr>
        <td width=$O align=center bgcolor=lightgrey>0x02</td>
        <td align=center colspan=2
           ><a href="$HEP{$chap}#SF">$h{SF}</a></td>
        <td align=center colspan=14
           ><a href="$HEP{$chap}#Sequence_Count">Sequence Count</a></td>
      </tr><tr>
        <td width=$O align=center bgcolor=lightgrey>0x04</a></td>
        <td align=center colspan=16
           ><a href="$HEP{$chap}#Packet_Length">Packet Length $p_len</a></td>
      </tr><tr>
        <td width=$O align=center bgcolor=lightgrey>0x06</td>
EOT

                if ($chap eq 'tc') {

                    $out .= <<EOT;
        <td align=center>0</td>
        <td align=center colspan=15
           ><a href="$HEP{$chap}#Function_Code"
              >Function Code = $r2->{Function_Code}</a></td>
      </tr><tr>
        <td colspan=17></td>
EOT
                    $offset = 0x08;

                } else {	# tm

                    $out .= <<EOT;
        <td align=center colspan=16
           ><a href="$HEP{$chap}#Timestamp_Seconds"
              >Timestamp Seconds MSW</td>
      </tr><tr>
        <td width=$O align=center bgcolor=lightgrey>0x08</td>
        <td align=center colspan=16
           ><a href="$HEP{$chap}#Timestamp_Seconds"
              >Timestamp Seconds LSW</td>
      </tr><tr>
        <td width=$O align=center bgcolor=lightgrey>0x0a</td>
        <td align=center colspan=16
           ><a href="$HEP{$chap}#Timestamp_Sub_Seconds"
              >Timestamp Sub-Seconds MSW</td>
      </tr><tr>
        <td width=$O align=center bgcolor=lightgrey>0x0c</td>
        <td align=center colspan=16
           ><a href="$HEP{$chap}#Timestamp_Sub_Seconds"
              >Timestamp Sub-Seconds LSW</td>
      </tr><tr>
        <td colspan=17></td>
EOT
                    $offset = 0x0e;
                }

                $count  = 0;
                undef @desc;
                if ($r2->{Fields} ne '') {
                    foreach $field (@{ $r2->{Fields} }) {

                        if ($field !~ m|^\?|) {
                            $r3 = $r_ctdb->{Common}{Fields}{$field};

                            $t1 = ($chap eq 'tm' and
                                   defined($r3->{Description_tm}))
                                ? $r3->{Description_tm}
                                : $r3->{Description};

                            push(@desc, $field) if (defined($t1));

                            $t2 = "$chap:$field";

                            if (defined($r3)) {
                                if (not defined($desc{$t2})) {
                                    $desc{$t2} = (defined($t1))
                                               ? $t1
                                               : '? - no description';
                                }
                            } else {
                                if (not defined($desc{$t2})) {
                                    $desc{$t2} = '? - no entry';
                            }   }
                            $desc2{$field} = $desc{$t2}
                                unless (defined($desc2{$field}));
                        }

                        if ($count%16 == 0) {
                            $offset += 2 * ($count/16);
                            $t1 = sprintf("0x%02x", $offset);
                            $count  =  0;

                            $out .= <<EOT
      </tr><tr>
        <td width=$O align=center bgcolor=lightgrey>$t1</td>
EOT

                        }

                        $bd = "<a href=\"#$field\">";
                        ($p_field = $field) =~ s|_| |g;

                        if ($field =~ m|^[A-Z]|) {

                            $size = $r_ctdb->{Common}{Fields}{$field}{Size};
                            $size = 16 unless (defined($size));
                    
                        } elsif ($field =~ m|^\?(\w+):(\d+)$|) {

                            ($p_field, $size) = ($1, $2);

                        } else {
                            print "???: field='$field'\n";		# DEBUG
                        }

                        if      ($size !~ m|^\d+$|) {	# Not numeric

                            if      ($size =~ m|^L *= *(\d+)$|) {

                                ($b_lo, $b_hi) = ($1, $1);

                            } elsif ($size =~ m|^L *<= *(\d+)$|) {

                                ($b_lo, $b_hi) = (1, $1);

                            } elsif ($size =~ m|^(\d+) *<= *L *<= *(\d+)$|) {

                                ($b_lo, $b_hi) = ($1, $2);

                            } else {
                                print "???: size='$size'\n";		# DEBUG
                                ($b_lo, $b_hi) = (-1, -1);
                            }

                            if ($b_lo == -1) {

                                $size = 16;
                                $t1   = 'TBD';

                            } else {

                                $size = 8 * $b_hi;
                                $t1 = ($b_lo eq $b_hi) ? "D = $b_hi"
                                                       : "D <= $b_hi";
                            }

                            $t2 = 16 - $count;

                            $out .= <<EOT;
        <td align=center colspan=$t2>$bd$p_field ($t1)</a></td>
EOT

                        } elsif ($size > 16) {		# Oversize

                            $t1 = $size - 16;
                            $t2 = sprintf("0x%02x", $offset+2);

                            $out .= <<EOT;
        <td align=center colspan=$t1>$bd$p_field MSW</a></td>
      </tr><tr>
        <td width=$O align=center bgcolor=lightgrey>$t2</td>
        <td align=center colspan=16>$bd$p_field LSW</a></td>
EOT

                        } else {				# "Normal"

                        $out .= <<EOT;
        <td align=center colspan=$size>$bd$p_field</a></td>
EOT

                        }
                        $count += $size;
                        $pkt{field_name}{$p_field}{"$p_pkt:$chap"} = $pkt
                            unless ($field =~ m|^\?|);
                }   }

                $offset += 2 * (int(($count+15)/16));
                $t1 = sprintf("0x%02x", $offset);

                $out .= <<EOT if ($offset > 8);
      </tr><tr>
        <td colspan=17></td>
EOT

                $out .= <<EOT;
      </tr><tr>
        <td width=$O align=center bgcolor=lightgrey>$t1</td>
        <td align=center colspan=16>Packet Checksum</td>
      </tr>
    </table>
    <basefont size="3">
EOT

                if ($#desc >= 0) {
                    $out .= <<EOT;
      <p><b>Fields:</b>
      <ul>
EOT
      
                    foreach $field (sort(@desc)) {

                        ($t1 = $field) =~ s|_| |g;
                        $out .= <<EOT;
        <p><a name="$field"><b>$t1</b></a> -
          $desc{"$chap:$field"}
          <a href="ndx_field_name.html#$field">index</a>

EOT
                    }
                    $out .= "      </ul>\n";
                }

                $out .= <<EOT;
  </body>
</html>
EOT

                $t1 = "${chap}_$pkt.html";
                open(OUT, ">a_cat/$t1") or die "can't open '$t1'";
                print OUT $out;
                close(OUT);

                $pkt{name     }{                        "$p_pkt:$chap"} = $pkt;
                $pkt{sect_name}{                  "$sect $p_pkt:$chap"} = $pkt;
                $pkt{type_name}{"$p_type $CHAP{$chap}"}{"$p_pkt:$chap"} = $pkt;
    }   }   }

    # Write index pages.

#   print Dumper(%pkt);						# DEBUG

    foreach $ndx (sort(keys(%TITLE))) {

        $out = <<EOT;
<html>
  <head>
    <title>Index by $TITLE{$ndx}</title>
$HDR
      <b>Index by $TITLE{$ndx}</b>
    <p><hr>
EOT

        $t2 = "    <h4>See Also:</h4>\n" .
              "    <ul>\n";

        foreach $t1 (sort(keys(%TITLE))) {
            $t2 .= "      <a href=\"ndx_$t1.html\">" .
                   "Index by $TITLE{$t1}</a></br>\n"
                unless ($t1 eq $ndx);
        }
        $t2  .= "    </ul>\n";
        $out .= $t2;
        $out .= "\n";

        if ($ndx eq 'name') {

            # $pkt{name}{"$p_pkt:$chap"} = $pkt;

            $last =  '';
            $out  .= "    <h4>Packets:</h4>\n\n" .
                     "    <pre>\n";
            $r3   =  $pkt{name};
            foreach $t0 (sort(keys(%$r3))) {
                ($p_pkt, $chap) =  split(/:/, $t0);

                $pkt = $r3->{$t0};
                ($t1 = $pkt)  =~ s|_.*||;

                $out .= "\n" if ($t1 ne $last and $last ne '');
                $last = $t1;
                $out .= "  <a href=\"${chap}_$pkt.html\">$p_pkt</a>";

                $out .= ($chap eq 'tm') ? " (tm)\n"
                                        : "\n";
            }
            $out  .= "    </pre>\n";

        } elsif ($ndx eq 'field_name') {

            # $pkt{field_name}{$p_field}{"$p_pkt:$chap"} = $pkt;

            $last =  ' ';
            $out .= "    <p>\n";
            $r3  =  $pkt{field_name};
            foreach $p_field (sort(keys(%$r3))) {
                $r4  =  $r3->{$p_field};
                ($field = $p_field) =~ s| |_|g;

                print "p_field='$p_field'\n"				# DEBUG
                    unless (defined($desc2{$field}));

                $t1 = uc(substr($field, 0, 1));
                if ($t1 ne $last) {
                    foreach $t2 (@DIV) {
                        if ($t2 gt $last and
                            $t2 le $t1) {
                            $out .= "      <a name=\"div_$t2\"><p></a>\n";

                            foreach $t3 (@DIV) {
                                if ($t3 eq $t2) {
                                    $out .= "      &nbsp;<font size=+1>" .
                                            "<b>$t3</b></font>&nbsp;\n";
                                } else {
                                    $out .= "      <a href=\"#div_$t3\">" .
                                            "$t3</a>\n";
                }   }   }   }   }
                $last = $t1;

                $t2 = $desc2{$field};
                chomp($t2);

                $out .= <<EOT;
    <h4><a name="$field">$p_field</a></h4>
    <ul>
      <p>
        $t2
      <p>
EOT

                foreach $t0 (sort(keys(%$r4))) {
                    ($p_pkt, $chap) =  split(/:/, $t0);

                    $pkt = $r4->{$t0};
                    ($t1 = $pkt)  =~ s|_.*||;

                    $out .= "      <a href=\"${chap}_$pkt.html\">$p_pkt</a>";
                    $out .= ($chap eq 'tm') ? " (tm)<br>\n"
                                            :      "<br>\n";
                }
                $out .= "    </ul>\n\n";
            }

            foreach $t2 (@DIV) {
                if ($t2 gt $last and
                    $t2 le 'Z') {
                    $out .= "    <a name=\"div_$t2\"><p></a><br>\n";

                    foreach $t3 (@DIV) {
                        if ($t3 eq $t2) {
                            $out .= "    &nbsp;<font size=+1>" .
                                    "    <b>$t3</b></font>&nbsp;\n";
                        } else {
                            $out .= "    <a href=\"#div_$t3\">$t3</a>\n";
            }   }   }   }

        } elsif ($ndx eq 'sect_name') {

            # $pkt{sect_name}{"$sect $p_pkt:chap"} = $pkt;

            $last =  '';
            $r3   =  $pkt{sect_name};
            foreach $t0 (sort by_sect (keys(%$r3))) {
                ($sect, $chap) =  split(/:/, $t0);

                $pkt = $r3->{$t0};
                ($t1 = $pkt)  =~ s|_.*||;

                ($t1 = $sect) =~ s|^(\d+\.\d+)\..*$|$1|;
                if ($t1 ne $last) {
                    $out .= "    </pre>\n\n" if ($last ne '');
                    ($t2 = $type{$t1}) =~ s|_| |g;
                    $t3 = ($chap eq 'tc') ? ' Telecommands'
                                          : ' Telemetry';
                    $t4 = '&nbsp;' x 4;
                    $out .= "    <h4>$t1$t4$t2 $t3</h4>\n";
                    $out .= "    <pre>\n";
                }
                $last = $t1;
                $out .= "  <a href=\"${chap}_$pkt.html\">$sect</a>";
                $out .= ($chap eq 'tm') ? " (tm)\n"
                                        : "\n";
            }
            $out .= "    </pre>\n\n";

        } elsif ($ndx eq 'type_name') {

            # $pkt{type_name}{$p_type}{"$p_pkt:$chap"} = $pkt;

            $ftf = 0;
            $r3  =  $pkt{type_name};
            foreach $p_type (sort(keys(%$r3))) {
                $r4  =  $r3->{$p_type};

                $out .= "\n" if (++$ftf != 1);
                $out .= "    <b>$p_type</b>\n    <ul>\n";

                foreach $t0 (sort(keys(%$r4))) {
                    ($p_pkt, $chap) =  split(/:/, $t0);

                    $pkt = $r4->{$t0};
                    $out .= "      <a href=\"${chap}_$pkt.html\">" .
                            "$p_pkt</a><br>\n";
                }
                $out .= "    </ul>\n";
        }   }

        $out .= <<EOT;
  </body>
</html>
EOT

        $t1 = "ndx_$ndx.html";
        open(OUT, ">a_cat/$t1") or die "can't open '$t1'";
        print OUT $out;
        close(OUT);
    }

    # Print out "starter description entries" for any undefined fields.

    foreach $t1 (sort {lc($a) cmp lc($b)} (keys(%desc))) {
        if ($desc{$t1} =~ m|^\?|) {
            ($t2, $t3) = split(/:/, $t1);

            print <<EOT;
$t2
      $t3:
        Size:           16
        Description:    '$desc{$t1}'

EOT

}   }   }


# by_sect - helper function, for sorting by section numbers (e.g., 1.2.3)
#
sub by_sect {

    my(@a, @b, $a1, $b1);

    $a  =~ m|^(\d+)\.(\d+)\.(\d+)|;
    $a1 =  sprintf("%03d.%03d.%03d", $1, $2, $3);

    $b  =~ m|^(\d+)\.(\d+)\.(\d+)|;
    $b1 =  sprintf("%03d.%03d.%03d", $1, $2, $3);

    $a1 cmp $b1;
}
