import tango.core.Exception;

import tango.io.Stdout;
import tango.io.Conduit;
import tango.io.MemoryConduit;
import tango.io.Console;
import tango.io.Print;

import tango.io.protocol.Writer;

import tango.text.stream.LineIterator;

import tango.text.convert.Integer;
import tango.text.convert.Layout;

import tango.text.Regex;
import tango.text.Util;

/*
  Workaround for bug in Tango 0.99
  see http://www.dsource.org/projects/tango/ticket/546
*/
ubyte
byteFromHex( char[] hex )
in
  {
    assert( hex.length == 1 || hex.length == 2 );
  }
body
  {
    ubyte r = 0;
    foreach( h; hex )
      {
        ubyte d = 0;
        switch( h )
          {
          case '0': d = 0; break;
          case '1': d = 1; break;
          case '2': d = 2; break;
          case '3': d = 3; break;
          case '4': d = 4; break;
          case '5': d = 5; break;
          case '6': d = 6; break;
          case '7': d = 7; break;
          case '8': d = 8; break;
          case '9': d = 9; break;
          case 'a', 'A': d = 10; break;
          case 'b', 'B': d = 11; break;
          case 'c', 'C': d = 12; break;
          case 'd', 'D': d = 13; break;
          case 'e', 'E': d = 14; break;
          case 'f', 'F': d = 15; break;
          }
        r = r * 16 + d;
      }

    return r;
  }

unittest
  {
    assert( 0 == byteFromHex( "00" ) );
    assert( 0 == byteFromHex( "0" ) );
    assert( 1 == byteFromHex( "1" ) );
    assert( 1 == byteFromHex( "01" ) );
    assert( 0xb == byteFromHex( "b" ) );
    assert( 0xb == byteFromHex( "B" ) );
    assert( 0xb == byteFromHex( "0B" ) );
    assert( 0xb == byteFromHex( "0b" ) );
  }

ubyte[]
stringImageToBinary( char[] stringImage )
  {
    char[]
    convert( char[] image )
      {
        char[] result;
        for( uint i = 0; i != image.length; ++i )
          {
            auto ch = image[ i ];
            if( '\\' == ch )
              {
                ++i;
                ch = image[ i ];
                switch( ch )
                  {
                    case '\\', '"', '|', '{', '}': break;
                    case 'n' : ch = '\n'; break;
                    case 'r' : ch = '\r'; break;
                    case 't' : ch = '\t'; break;
                    case 'x' :
                      char[] hexCode = [
                          image[ i + 1 ],
                          image[ i + 2 ] ];
                      i += 2;
                      ch = cast(char) byteFromHex( hexCode );
                    break;
                  }
              }

            result ~= ch;
          }

        return result;
      }

    return cast(ubyte[])( convert( convert( stringImage ) ) );
  }

ubyte[]
hexImageToBinary( char[] hexImage )
  {
    ubyte[] result;
    foreach( hex; delimit( hexImage, " " ) )
      result ~= byteFromHex( hex );

    return result;
  }

char[]
makeHexTextRepresentation( ubyte[] binImage )
  {
    auto resultBuffer = new MemoryConduit;
    auto print = new Print!(char)( new Layout!(char), resultBuffer );
    for( uint i = 0; i != binImage.length; )
      {
        print.format( "{0,2:X2}", binImage[ i ] );
        if( ++i < binImage.length )
          print.print( ' ' );
      }

    return cast(char[]) resultBuffer.slice;
  }

unittest
  {
    auto r = stringImageToBinary( `\x0b\x0a\x03` );
    assert( r == [ cast(ubyte)0x0b, 0x0a, 0x03 ] );
  }

class AnalyzingResult
  {
  public :
    abstract bool isGoodMessage();
    abstract char[] toUtf8();
  }

class HeaderElement
  {
  public :
    this(
      ubyte id,
      ubyte[] data )
      {
        id_ = id;
        data_ = data;
      }

    ubyte id() { return id_; }
    ubyte[] data() { return data_; }

  private :
    ubyte id_;
    ubyte[] data_;
  }

class SuccessfulResult : AnalyzingResult
  {
  public :
    this()
      {
        headerElements_ = [];
        message_ = [];
      }

    this(
      HeaderElement[] headerElements,
      ubyte[] message )
      {
        headerElements_ = headerElements;
        message_ = message;
      }

    override bool isGoodMessage() { return true; }
    override char[] toUtf8()
      {
        auto resultBuffer = new MemoryConduit;
        auto print = new Print!(char)( new Layout!(char), resultBuffer );

        if( headerElements_.length )
          foreach( element; headerElements_ )
            print.format( "Element: Id: {0:X}, Data: {1}; ",
                element.id,
                makeHexTextRepresentation( element.data ) );

        print.format( "Message: {0}",
            makeHexTextRepresentation( message_ ) );

        return cast(char[]) resultBuffer.slice;
      }

  private :
    HeaderElement[] headerElements_;
    ubyte[] message_;
  }

class BadResult : AnalyzingResult
  {
  public :
    this(
      char[] reason,
      ubyte[] fullBody )
      {
        reason_ = reason;
        fullBody_ = fullBody;
      }

    override bool isGoodMessage() { return false; }
    override char[] toUtf8()
      {
        auto resultBuffer = new MemoryConduit;
        auto print = new Print!(char)( new Layout!(char), resultBuffer );

        print.format( "What: {0}; Full body: {1}",
            reason_,
            makeHexTextRepresentation( fullBody_ ) );

        return cast(char[]) resultBuffer.slice;
      }

  private :
    char[] reason_;
    ubyte[] fullBody_;
  }

AnalyzingResult
tryHandleHeadersAndBody( ubyte[] binImage )
  {
    if( !binImage.length )
      return new SuccessfulResult;

    Layout!(char) print = null;
    Layout!(char) printer()
      {
        if( print is null )
          print = new Layout!(char);
        return print;
      }

    ubyte headerLength = binImage[ 0 ];
    if( !headerLength )
      return new BadResult(
          "Zero header length",
          binImage );
    if( headerLength >= binImage.length )
      return new BadResult(
          printer.convert(
              "Header length too big! Length: {0}, Body length: {1}",
              headerLength,
              binImage.length ),
          binImage );

    HeaderElement[] elements;
    for( int i = 1; i + 2 < headerLength; )
      {
        ubyte elementId = binImage[ i++ ];
        ubyte elementSize = binImage[ i++ ];
        if( elementSize <= headerLength + 1 - i )
          elements ~= new HeaderElement(
              elementId,
              binImage[ i..(i + elementSize) ].dup );
        else
          return new BadResult(
              printer.convert(
                  "Invalid element size! Position={0}, "
                      "HeaderLength={1}, ElementSize:{2}",
                  i,
                  headerLength,
                  elementSize ),
              binImage );

        i += elementSize;
      }

    ubyte[] message = binImage[ (1 + headerLength)..$ ];
    foreach( b; message )
      if( b & 0x80 )
        return new BadResult(
            printer.convert(
                "Byte with non-zero highest bit in message! Byte: {0:X}", b ),
            binImage );

    return new SuccessfulResult(
        elements,
        binImage[ (1 + headerLength)..$ ].dup );
  }

void
showBadMessage( char[] line, AnalyzingResult analyzingResult )
  {
    auto additionalDataMatch = search( line,
        `^LOG \[([^]]+)\].*{source_addr "([^"]+)" }.*{dest_addr "([^"]+)" }` );
    Stdout.formatln( "ERROR -- at: [{0}]; from '{1}'; to '{2}'; {3}",
        additionalDataMatch.match( 1 ),
        additionalDataMatch.match( 2 ),
        additionalDataMatch.match( 3 ),
        analyzingResult );
  }

ubyte[]
tryExtractShortMessageBody( char[] line )
  {
    auto shortMessageFieldMatch = search( line, `{short_message "(.+)" }` );
    if( shortMessageFieldMatch !is null )
      return stringImageToBinary( shortMessageFieldMatch.match( 1 ) );

    auto tlvMessagePayloadMatch = search( line,
        `{tlv \{t 0x424} \{l 0x([0-9A-Fa-f]+)} \{v ([^}]+)}` );
    if( tlvMessagePayloadMatch !is null )
      return hexImageToBinary( tlvMessagePayloadMatch.match( 2 ) );

    return null;
  }

void
parseLine( char[] line, bool showGoodMessages )
  {
    if( line.length <= 2 )
      return;

    auto binImage = tryExtractShortMessageBody( line );
    if( binImage !is null )
      {
        auto analyzingResult = tryHandleHeadersAndBody( binImage );
        if( analyzingResult.isGoodMessage() )
          {
            if( showGoodMessages )
              Stdout.formatln( "OK: {0}", analyzingResult );
          }
        else
          showBadMessage( line, analyzingResult );
      }
    else
      Stderr.formatln( "nothing found in {0}", line );
  }

void
readLines( bool showGoodMessages )
  {
    foreach( line; new LineIterator!(char)( Cin.stream ) )
      parseLine( line, showGoodMessages );
  }

struct Options
  {
    bool showGoodMessages_ = false;
  }

Options
processArgs( char[][] args )
  {
    Options result;
    foreach( a; args )
      {
        if( "--show-good" == a )
          result.showGoodMessages_ = true;
        else
          throw new IllegalArgumentException(
              "unsupported agrument: " ~ a );
      }

    return result;
  }

void
main( char[][] args )
  {
    try
      {
        auto options = processArgs( args[ 1..$ ] );
        readLines( options.showGoodMessages_ );
      }
    catch( Exception x )
      {
        Stderr.formatln( "*** Exception: {0}", x );
      }
  }

// vim:ts=2:sts=2:sw=2:expandtab:

Hosted by uCoz