#!/usr/bin/perl -w
use strict;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
use Sort::Key::Multi qw(sikeysort);

use Getopt::Long;
use Digest::MD5 qw(md5_hex);

my %msgs_by_room;
my @msgs;

my $nmsgs = 0;
my $textsize = 0;
my $stringsize = 0;

my $verbose;
my $debug;
my $citplus;
my $ids_only;
my $ids_byroom_only;
my $range_only;
my $print_one;
my $csv;
my $htmltext;
my $htmltable;
my $htmlfiles;

GetOptions("debug|d"  => \$debug,
           "v" => \$verbose,
           "citplus" => \$citplus,
           "ids" => \$ids_only,
           "idsbyroom" => \$ids_byroom_only,
           "range" => \$range_only,
           "csv" => \$csv,
           "htmltext" => \$htmltext,
           "htmltable" => \$htmltable,
           "htmlfiles=s" => \$htmlfiles,
           "printone=i" => \$print_one,
    )   # flag
    or die("Error in command line arguments\n");


=for does this work

0 = \377 = 0xFF = message start marker
1 = room number - 255 rooms
2 = attribute byte
4+ = zstring Message ID

followed by fields:
first byte = field marker
subsequent bytes = content
ending with 0x00;

M = message body always comes last.

A = author  LABELSIZE
B = subject   80
C = copy  LABELSIZE
D = time  LABELSIZE
F = fwd   LABELSIZE
G = group LABELSIZE
I = reply LABELSIZE
J = creg  LABELSIZE
j = ccont LABELSIZE
L = link  63
M = message body
N = title LABELSIZE
n = surname   LABELSIZE
O = oname = origin node name = LABELSIZE
o = oreg  = origin node region = LABELSIZE
P = fpath = 128
p = tpath = 128
Q = ocont = orgin node country LABELSIZE
q = czip  LABELSIZE
R = room  LABELSIZE
S = srcId LABELSIZE
s = soft  LABELSIZE
T = to    LABELSIZE
X = x     LABELSIZE
Z = zip   LABELSIZE
z = rzip  LABELSIZE
. = sig   90
_ = usig  90

Special rooms:
#define LOBBY           0       /* Lobby> is >always< room 0.           */
#define MAILROOM        1       /* Mail>  is >always< room 1.           */
#define AIDEROOM        2       /* Aide)  is >always< room 2.           */
#define DUMP            3       /* Dump>  is >always< room 3.           */
#define DUNGEON         4       /* ....>  is >always< room 4.           */



=cut

# Weird ones:

# ÿ 1388 C1354 M 

# <FF>^@<C4>18371^@A^A0^AeCaptain James T Kirk^@D727293992^@RThe Back Alley^@!Audin Malmin^@Mwow.
# ÿ Ä18371 A0eCaptain James T Kirk D727293992 RThe Back Alley !Audin Malmin Mwow.
# ÿ 171 AItalian Stallion D685495492 RWhen you need to page AUDIN!!! MSorry the sysop is not aroused'



sub GetStr() {
    my $ch = '';
    my $str;

    while(ord($ch = getc()) != 0x00 && !eof()) {
        $str .= $ch;
    }
    $stringsize += length($str) if $str;
    return $str;
}

sub mci {
	my %colors = (
			"a" => "#404040",
			"b" => "#ff0000",
			"c" => "#00ff00",
			"d" => "#ffff00",
			"e" => "#0000ff",
			"f" => "#ff00ff",
			"g" => "#00ffff",
			"h" => "#ffffff",
			);
	my %styles = (
            "0" => "font-weight:normal; color:#929292; background-color:transparent; text-decoration:none",

            "1" =>                                                                  "text-decoration:blink",
#           "1" =>                     "color:#00ffff; background-color:transparent; text-decoration:none",
            "2" =>                     "color:#ffff00; background-color:#ff0000;     text-decoration:none",
#           "2" =>                                                                  "text-decoration:line-through",
            "3" => "font-weight:bold",
#           "3" => "color:#ffff00; background-color:transparent",
            "4" =>                                                                  "text-decoration:underline",
#           "4" => "color:#ff00ff: background-color:transparent",
            "5" => "",
			);
	$_ = shift;
	my $space_at_end = shift || 0;

        return $_ if ! m/\x01/;

	s{&}{&amp;}g;
	s{<}{&lt;}g;
	s{>}{&gt;}g;
	my @c = keys %colors;
	my $fg_spans = s,\x01+([abcdefgh]),"<span style=\"color:" .            $colors{   $1 } . "\">",eg;
	my $bg_spans = s,\x01+([ABCDEFGH]),"<span style=\"background-color:" . $colors{lc($1)} . "\">",eg;
	my $style_spans = s,\x01+([01234]),"<span style=\""                  . $styles{   $1 } . "\">",eg;
	my $random_fg_spans = s,\x01+r,"<span style=\"color:" .            $colors{$c[rand(scalar @c)]} . "\">",eg;
	my $random_bg_spans = s,\x01+R,"<span style=\"background-color:" . $colors{$c[rand(scalar @c)]} . "\">",eg;
	if ($space_at_end) {
		$_ .= " ";
	}
	$_ = '<span style="' . $styles{"0"} . '">'. $_;
	$_ .= "</span>" x ($fg_spans + $bg_spans + $style_spans + 1);
	s{\x01.}{}g;
	return $_;
}


sub escapeCSV {
    $_ = shift;
    s/\,/ /g;
    s/\n/<BR>/g;

    return $_;
}

sub escapeHTML {
    $_ = shift;
    s/</&lt;/g;
    s/\n/<br>\n/g;
    s/\x01.//g;
    return $_;
}

sub escapeBody {
    $_ = shift;
    s/</&lt;/g;
    s/\x01.//g;
    return $_;
}

sub escapeHTMLnbsp {
    $_ = shift;
    s/ /&nbsp;/g;
    s/</&lt;/g;
    s/\n/<br>\n/g;
    s/\x01.//g;
    return $_;
}

sub msg2htmltable(%) {
    my $msg = shift;
    my $msgbody = $msg->{M};
    
    print("<tr>".
          "<td>$msg->{msgID}</td>".
          "<td class='nowrap'>". $msg->{timeString} . "</td>".
          "<td class='nowrap'>". ord($msg->{roomID})."</td>".
          "<td class='nowrap'>$msg->{R}</td>".
          "<td class='nowrap'>$msg->{A}</td>".
          "<td class='nowrap'>$msg->{T}</td>".
          "<td class='msgbody'>$msgbody</td>".
          "</tr>\n");
}

sub htmlheader() {
    return ("<html>".
            "<head><link rel='stylesheet' type='text/css' href='text.css'>".
            "</head>".
            "<body>\n");
}

sub msg2htmltext(%) {
    my $msg = shift;
    my $msgbody = $msg->{M};

    my $s;
    
    $s .=("<br>".
          "<span class='header'>#<a href='#$msg->{msgID}' id='$msg->{msgID}'>$msg->{msgID}</a> " . 
          ord($msg->{roomID}) . " " .
          scalar(localtime($msg->{D}))
        );
    
    $s .= (" [" . mci($msg->{N}) . "] ") if defined($msg->{N});

    $s .= ("</span> <span class='nym'>". mci($msg->{A}) . "</span> <span class='header'>");

    $s .= (" [" . mci($msg->{n}) . "] ") if defined($msg->{n});

    $s .= (" To $msg->{T} ") if defined($msg->{T}) && $msg->{T} ne '';

    $s .= (" @ $msg->{O}") if defined($msg->{O});
    $s .= (", $msg->{o}") if defined($msg->{o});
    $s .= (", $msg->{Q} ") if defined($msg->{Q});

    $s .= ("</span><br>\n");
    
    $s .= ("Copy of: $msg->{C}<br>\n") if defined($msg->{C});

    $s .= ("<span class='msgbody'>" . mci($msgbody) ."</span>");
   
    return $s;
}

sub msg2searchable($%) {
    my $roomfile = shift;
    my $msg = shift;

    my $s;
    
    $s .=("<br>");

    $s .=("<span class='searchhead'>");

    $s .=("#<a href='$roomfile#$msg->{msgID}' id='$msg->{msgID}'>$msg->{msgID}</a> ");

    $s .= sprintf(" <span class='searchfixed'>%-30.30s</span> ", 
        $msg->{R});

    $s .=(scalar(localtime($msg->{D})));
    
    $s .= (" [$msg->{N}] ") if defined($msg->{N});

    $s .= (" <span class='nym'>" . escapeHTMLnbsp($msg->{A}) . "</span>");

    $s .= (" [$msg->{n}] ") if defined($msg->{n});

    $s .= (" To $msg->{T} ") if defined($msg->{T}) && $msg->{T} ne '';

    $s .= (" @ $msg->{O}") if defined($msg->{O});
    $s .= (", $msg->{o}") if defined($msg->{o});
    $s .= (", $msg->{Q} ") if defined($msg->{Q});

    $s .= ("</span>");

    $s .= ("<br>\n");
    
    $s .= ("Copy of: $msg->{C}<br>\n") if defined($msg->{C});

    $s .= (escapeBody($msg->{M}));

    $s .= "<br>\n";
   
    return $s;

}

sub msg2status(%) {
    my $msg = shift;

    my $mtext = substr($msg->{M}, 0, 50);
    $mtext =~ s/[\t\n\r]/ /g;

    printf("%30s | %10d | %8.8b 0x%2.2x | %8.8b 0x%2.2x | %4.4s | %3d | %10d | %24.24s | %-30.30s | %-30.30s | %-30.30s | %5d | %s\n", 
           $msg->{GUID},
           $msg->{msgID}, 
           $msg->{attr},
           $msg->{attr},
           $msg->{attr2},
           $msg->{attr2},
           $msg->{attrs},
           $msg->{roomID}, 
           $msg->{D}, 
           $msg->{timeString}, 
           $msg->{cleanA},
           $msg->{T} || "", 
           $msg->{R}, 
           length($msg->{M}),
           $mtext
        );
}

while(!eof()) {
    my %msg;

    print STDERR "toss: " if $debug;
    while(ord((my $c = getc())) != 0xFF && !eof()) {
        print STDERR "$c" if $debug;
    }
    print STDERR "done tossing\n" if $debug;

    if($citplus) {
        $msg{attr} = ord(getc());

        if ($msg{attr} & 0x40) {
            $msg{attr2} = ord(getc());
            print "\nSecond ATTR BYTE\n";
                
        } else {
            $msg{attr2} = 0;
        }
        
        $msg{roomID} = ord(getc());
        $msg{roomID2} = ord(getc()) if $msg{attr} & 0x20; # bigroom
    } else {
        $msg{roomID} = ord(getc());
        $msg{attr} = ord(getc());
        $msg{attr2} = 0;
    }

    $msg{msgID} = GetStr();

    if($msg{msgID} == '1') {
        # msgid 1 is weird...
        print STDERR "Throw out message ID 1...\n";
#        getc();
#        next;
    }

    my $c;

    do {
        $c = getc();
        my $str = GetStr();
        print STDERR "$c - $str\n" if $debug;
        $msg{$c} = $str;
    } while ($c ne 'M' && !eof());

#define MAILROOM        1       /* Mail>  is >always< room 1.           */
#define AIDEROOM        2       /* Aide)  is >always< room 2.           */
#define DUMP            3       /* Dump>  is >always< room 3.           */
#define DUNGEON         4       /* ....>  is >always< room 4.           */

    $msg{R} = 'Aide' if $msg{roomID} == 2;

    $msg{D} = '0' if ! defined $msg{D};
    $msg{M} = '' if ! defined $msg{M};
    $msg{A} = '' if ! defined $msg{A};
    $msg{T} = '' if ! defined $msg{T};
    $msg{R} = '' if ! defined $msg{R};

    $msg{attrs} = '';
    $msg{attrs} .= 'C' if $msg{attr} & 0x08; # compressed
    $msg{attrs} .= 'B' if $msg{attr} & 0x20; # bigroom

    $msg{M} = "...compressed...\n" if $msg{attr} & 0x08; # compressed

    $msg{cleanA} = $msg{A};
    $msg{cleanA} =~ s/\x01.//g;;

    $msg{cleanR} = $msg{R};
    $msg{cleanR} =~ s/\x01.//g;;

    $msg{timeString} = scalar(gmtime($msg{D} + 28800)); # annoying dos time conversion...

    $msg{GUID} = sprintf("%10.10d.%4.4d.%10.10d", $msg{msgID}, $msg{roomID}, $msg{D});

    $nmsgs++;
    $textsize += length($msg{M}) if $msg{M};

    msg2status(\%msg) if $verbose;
    
    push @{ $msgs_by_room{$msg{roomID}} }, \%msg;
    push @msgs, \%msg;
}

print("\n\n\n\n\n");

print STDERR Dumper(\%msgs_by_room) if $debug;
print STDERR Dumper(\@msgs) if $debug;

if($print_one) {
    foreach my $msg (sort {$a->{msgID} <=> $b->{msgID} } @msgs) {
        if($msg->{msgID} == $print_one)
        { 
            msg2status($msg);
            print Dumper(\$msg);
        } 
    }
}

if($ids_only) {
    print "\n";
    foreach my $msg (sort {ord($a->{D}) cmp ord($b->{D})} @msgs) {
#    foreach my $msg (sort {$a->{msgID} <=> $b->{msgID} } @msgs) {
        msg2status($msg);
    }
    print "\nWTF";
}
elsif($ids_byroom_only) {
    print "\n";

    foreach my $msg (sort {$a->{roomID} cmp $b->{roomID} || ord($a->{D}) cmp ord($b->{D}) } @msgs) {
        msg2status($msg);
    }
    print "\n";
}
elsif($range_only) {
    my @sorted = sort {$a->{msgID} <=> $b->{msgID} } @msgs;

    my $msg = $sorted[0];
    print  "$msg->{msgID} \t$msg->{D} \t$msg->{timeString} \t$msg->{A} \t$msg->{R}\n";
    $msg = $sorted[1];
    print  "$msg->{msgID} \t$msg->{D} \t$msg->{timeString} \t$msg->{A} \t$msg->{R}\n";
    $msg = $sorted[-2];
    print  "$msg->{msgID} \t$msg->{D} \t$msg->{timeString} \t$msg->{A} \t$msg->{R}\n";
    $msg = $sorted[-1];
    print  "$msg->{msgID} \t$msg->{D} \t$msg->{timeString} \t$msg->{A} \t$msg->{R}\n";


}
elsif($csv) {
    foreach my $msg (sort {$a->{msgID} <=> $b->{msgID} } @msgs) {
        print("$msg->{D}$msg->{msgID}$msg->{roomID},".
              "$msg->{msgID},$msg->{D},$msg->{timeString}," . 
              escapeCSV($msg->{R}) . ",".
              escapeCSV($msg->{A}) . ",".
              escapeCSV($msg->{T}) . ",".
              escapeCSV($msg->{M}) . ",".
              "\n");
    }

}
elsif($htmltable) {
    print("<html>".
          "<head><style>.nowrap { white-space: nowrap;}</style></head>".
          "<body><table border>");
    
    foreach my $msg (sort {$a->{roomID} cmp $b->{roomID} || $a->{msgID} cmp $b->{msgID} } @msgs) {
        next if $msg->{T};
        msg2htmltable($msg);
    }
    print "</table></body></html>\n";
}
elsif($htmltext) {
    print("<html>".
          "<head><link rel='stylesheet' type='text/css' href='text.css'>".
          "</head>".
          "<body>\n");
    
    my $current_room = -1;
    my $current_room_name = '';

    foreach my $msg (sort {($a->{R} cmp $b->{R}) || ($a->{msgID} <=> $b->{msgID}) } @msgs) {
#    foreach my $msg (sort {($a->{R} cmp $b->{R}) || ($a->{D} <=> $b->{D}) } @msgs) {
#        if($current_room != $msg->{roomID}) {
#            $current_room = $msg->{roomID};

 #           print("<br><span class='roomprompt'>$msg->{R}></span><br>");
 #       }

        if($current_room_name ne $msg->{R}) {
            $current_room_name = $msg->{R};

            print("<br><span class='roomprompt'>". escapeHTMLnbsp($msg->{R}) . "></span> <span class='blink'>_</span><br>\n");
        }

        next if $msg->{T};
        msg2htmltext($msg);
    }
    print "</body></html>\n";
}
elsif($htmlfiles) {
    my $current_room;
    my $nmessages = 0;
    my $nemail = 0;

    my $current_outfile = '';

    my $last_guid;

    my $out;
    my $indexout;
    my $everythingout;

    open($indexout, '>', $htmlfiles . '/index.html');
    print($indexout htmlheader());

    open($everythingout, '>', $htmlfiles . '/index-all.html');
    print($everythingout htmlheader());


#    foreach my $msg (sort { $a->{R} cmp $b->{R} || ord($a->{D}) <=> ord($b->{D}) } @msgs) {
    foreach my $msg (sikeysort { $_->{cleanR}, $_->{D} } @msgs) {  # that was annoying.

        if(!defined $current_room || $current_room ne $msg->{R}) {
            print($out "<br><span class='roomprompt'>". mci($current_room) . "></span> <span class='blink'>_</span><br>\n") if $out;
            print($everythingout "<br><span class='roomprompt'>". mci($current_room) . "></span> <span class='blink'>_</span><br>\n") if $out;
            
            if(defined $current_room) {
                print($indexout 
                      "<span class='roompromptindex'>\n" .
                      "<span class='name'><a href='$current_outfile'>". mci($current_room . '>') . "</a></span>\n" .
                      "<span class='stats'>$nmessages messages, $nemail exclusive, 0 new</span>\n" .
                      "</span>\n");
                close($out);

                $nmessages = 0;
                $nemail = 0;
            }

            $current_outfile = md5_hex($msg->{R}) . '.html';
            
            print("NEW ROOM : $current_outfile $msg->{R}\n");

            open($out, '>', $htmlfiles . '/' . $current_outfile);

            print($out htmlheader());

            print($out "<br><span class='roomprompt'>". mci($msg->{R}) . "></span><br>\n");

            $current_room = $msg->{R};
        }

        if($last_guid ne $msg->{GUID}) {
            print "  "; msg2status($msg);
            $last_guid = $msg->{GUID};

            if($msg->{T} eq '') {
                print $out msg2htmltext($msg);
                print $everythingout msg2searchable($current_outfile, $msg);
                $nmessages++;
            } else {
                $nemail++;
            }
        } else {
            print "D "; msg2status($msg);
        }
    }

    close($indexout);
    close($everythingout);

    open($everythingout, '>', $htmlfiles . '/index-all.html');
    print($everythingout htmlheader());

    foreach my $msg (sort { ord($a->{D}) <=> ord($b->{D}) } @msgs) {
        if($msg->{T} eq '') {
            print $everythingout msg2searchable(md5_hex($msg->{R}) . '.html', $msg);
        }
    }

    close($everythingout);

}

else {

}


print STDERR "$nmsgs found\n";
print STDERR "$textsize bytes of message content\n";
print STDERR "$stringsize bytes of strings\n";

