/*
 *
 * Copyright (c) 2007
 * Adrian Michel
 * http://www.tradery.com
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Adrian Michel makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
*/

package  com.tradery.contentmodel;

import com.tradery.cmdline.CmdLineApp;
                                                                                          
import java.util.Vector;
import java.util.Stack;

public class ContentModel extends CmdLineApp
{
  private static final String HELP_FLAG = "help";
  private static final String QUESTION_FLAG = "?";
  private static final String DUMP_STATE_MACHINE = "s";
  private static final String DUMP_EXPRESSION = "e";
  private static final String DUMP_SYNTAX_TREE = "t";
  private static final String DUMP_ALL = "a";
  private static final String REVERSE_POLISH_NOTATION = "r";
  
  
  private boolean dumpExpression = false;
  private boolean dumpStateMachine = false;
  private boolean dumpSyntaxTree = false;
  private boolean reversePolishNotation = false;
  
  public ContentModel()
  {
    super("ContentModel"); // Pass through the name of the executable as it appears in the filesystem

    // Register options that can be specified on the commandline along with help text
    
    registerOption(HELP_FLAG, "help", "Dump verbose help about options to the screen", true);
    registerOption(QUESTION_FLAG, "usage", "Give usage information", true);
    registerOption(DUMP_STATE_MACHINE, "dump state machine", "Dumps info about the state machine", true);
    registerOption(DUMP_EXPRESSION, "dump content model expression", "Dumps the regular expression that is being analysed", true);
    registerOption(DUMP_SYNTAX_TREE, "dump the syntax tree", "Dumps detailed info about the syntax tree", true);
    registerOption(DUMP_ALL, "dump all the info", "Dumps detailed info about the syntax tree, expression and state machine", true);
    registerOption(REVERSE_POLISH_NOTATION, "reverse polish notation flag", "Accepts content model regular expressions in reverse polish notation, where character \"^\" is Pop", true );
    registerRemaining("Remaining parameters", "Content model expression and strings", "Content model expression and strings to be checked against this content model" );
  }
  
  public static void main (String[] args)
  {
    ContentModel model = new ContentModel();
    
    try
    {
      // Run the application
      model.run(args);
    }
    catch(CmdLineInvalidOption e)
    {
      // Give the user help if they specify an invalid option
      System.out.println(e.getName() + ": Invalid Option -" + e.getOption() + " " + e.getMessage());            
      System.out.println(model.usageString());
    }
    catch(CmdLineException e)
    {
      // Log any parsing errors
      System.out.println(e.getName() + ": " + e.getMessage());          
    }
    
  }
  
  public static void runDFA( AbstractState state, SyntaxTree m, String str )
  {
    ModelWriter.println( "--------- Running the State Machine ---------" );                             
    boolean b = true;
    String s = new String();
    boolean inWord = false;
    
    for( int n = 0; n < str.length() && b; n++ )
    {
      
      char c = str.charAt( n );
      switch( c )
      {
        case ',':
        case ' ':
        case '\t':
        case '\n':
        case '\r':
          if( inWord )
          {
            if( !state.doTransition( s ) )
            {
              b = false;
              break;
            }
            inWord = false;
            s = "";
          }
          break;
        default:
          inWord = true;
          s += str.substring( n, n + 1 );
          break;
      }
      
      
    }
    if( inWord )
    {
      if( !state.doTransition( s ) )
        b = false;
    }
    
    if( b )                                     
      b = state.canTerminate();

    ModelWriter.print( "\"" + str + "\" " );
    ModelWriter.println( ( b ? "matches " : "doesn't match ") + m.toString() );
  }
  
  public void processOption( String opt, String value )
  {
    if( opt.equals( DUMP_STATE_MACHINE ) )
      dumpStateMachine = true;
    else if( opt.equals( DUMP_EXPRESSION ) )
      dumpExpression = true;
    else if( opt.equals( DUMP_SYNTAX_TREE ) )
      dumpSyntaxTree = true;
    else if( opt.equals( REVERSE_POLISH_NOTATION ) )
      reversePolishNotation = true;
    else if( opt.equals( DUMP_ALL ) )
    {
      dumpStateMachine = true;
      dumpSyntaxTree = true;
      dumpExpression = true;
    }
    else if( opt.equals( HELP_FLAG ) )
    {
      help();
    }
    else if( opt.equals( QUESTION_FLAG ) )
    {
      help();
    }
    else
    {
      help();
    }
  }
  
  void help() { System.out.println( helpString() ); }

  
  public void processApplication( String[] args )
  {
    try
    {
      if( args == null )
      {
        help();
        return;
      }
      
      AbstractExpression e;
      if( reversePolishNotation )
        e = new ReversePolishNotationFE( "" );
      else
        e = new DirectNotationFE( "" );
      
      SyntaxTree m = (SyntaxTree)e;
      
      e.parse( args[ 0 ] );
      if( dumpExpression )
        m.dumpExpression();
      
      m.compile();
      if( dumpSyntaxTree )
        m.dump();
      if( dumpStateMachine )
        m.dumpStateMachine();

      for( int n = 1; n < args.length; n++ )
      {
        AbstractState state = m.getInitialState();
        runDFA( state, m, args[ n ] );
      }
    }
    catch( AmbiguousContentModelException e )
    {
      ModelWriter.println( "Content model is ambiguous" );
    }
    catch( BadLimitsException e )
    {
      ModelWriter.print( "Bad limits in range: " + e.toString() );
    }
  }
}

        // unambiguous
        // (a|b)*,a,b,b
        // a?,b
        // (a,b,e,f)*|(c,d)|(g,h)
        // (a[2,3],b[2,3])[5,6]
        // (a|b)+
        // a?
        // (b)(b*a)*
        // a?,b?,c?
        // (a+,b+,d+)?,c
        // ((a?,b?,h,i)|(c*,d?,k)+|e|(f,g))
        // (a*,b?,c)
        // c,d,e*
        // (a|b|c)[2,4]
        // a[2,4],(a|b)[1,1],(a,b,c?)*
        // (a|b)[2,3]

        // ambiguous
        // a*|(a,b)
        // (a,b)|(a,c)
        // (a,b)*|(a,c)
        // a,(b,a)*,(b|<empty>)
        // a|(a,b)