
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.io.*;
import java.util.*;

class TestResult {
    String _fileName;
    int _maxPoints;
    int _points;
    boolean _passed;

    TestResult( String fileName, int maxPoints ) {
	_fileName = fileName;
	_maxPoints = maxPoints;
	_points = 0;
	_passed = false;
    }
};

class BoilerDrawMain extends JFrame
implements MouseInputListener, ActionListener 
{
    BoilerDraw _bDraw;
    BoilerDrawListener _bDrawListener;
    boolean _recordMode;
    boolean _recording;
    String _recordLog;
    boolean _beingDragged;

    // Calls used when events come from a real source
    public void mouseMoved( MouseEvent evt ) {
    }
  
    public void mouseDragged( MouseEvent evt ) {
	_bDrawListener.mouseDragged( evt.getX(), evt.getY() );
	if ( _recording ) {
	    if ( !_beingDragged ) {
		addRecordLog( "MouseDragged|" + evt.getX() + "|" + evt.getY() );
		_beingDragged = true;
	    }
	}
    }
  
    public void mouseClicked( MouseEvent evt ) {
    }
  
    public void mousePressed( MouseEvent evt ) {
	_bDrawListener.mousePressed( evt.getX(), evt.getY() );
	if ( _recording ) {
	    addRecordLog( "MousePressed|" + evt.getX() + "|" + evt.getY() );
	    _beingDragged = false;
	}
    }
  
    public void mouseReleased( MouseEvent evt ) {
	_bDrawListener.mouseReleased( evt.getX(), evt.getY() );
	if ( _recording ) {
	    if ( _beingDragged ) {
		addRecordLog( "MouseDragged|" + evt.getX() + "|" + evt.getY() );
	    }
	    addRecordLog( "MouseReleased|" + evt.getX() + "|" + evt.getY() );
	    _beingDragged = false;
	}
    }
  
    public void mouseEntered( MouseEvent evt ) {
    }
  
    public void mouseExited( MouseEvent evt ) {
    }

    boolean runOneTest( String fileName ) {
	System.out.println("\n---- Start " + fileName + " -----" );

	boolean passed = false;

	try {
	    BufferedReader in = 
		new BufferedReader( new FileReader( fileName ) );
	    
	    // read line by line
	    String s = in.readLine();
	    while ( !s.equals("Result:") ) {
		System.out.println("REPLAY: " + s );
		
		StringTokenizer t = new StringTokenizer( s, "|" );
		String command = t.nextToken();
		if (command.equals( "MouseDragged" ) ||
		    command.equals( "MouseReleased" ) ||
		    command.equals( "MousePressed" ) ) {
		    
		    Integer i = new Integer( t.nextToken() );
		    int x = i.intValue();
		    
		    i = new Integer( t.nextToken() );
		    int y = i.intValue();
		    
		    if ( command.equals( "MouseDragged" ) ) {
			_bDrawListener.mouseDragged( x, y );
		    }
		    else if ( command.equals( "MouseReleased" ) ) {
			_bDrawListener.mouseReleased( x, y );
		    }
		    else if ( command.equals( "MousePressed" ) ) {
			_bDrawListener.mousePressed( x, y );
		    }
		}
		else if ( command.equals("Open") ) {
		    _bDrawListener.openFile( t.nextToken() );
		}
		else if ( command.equals("Save") ) {
		    _bDrawListener.saveFile( t.nextToken() );
		}
		else if ( command.equals("Text") ) {
		    _bDrawListener.addText( t.nextToken() );
		}
		else if ( command.equals("Menu") ) {
		    _bDrawListener.menuOption( t.nextToken() );
		}
		s = in.readLine();
	    }
	    
	    String expected = "";
	    s = in.readLine();
	    while ( s != null ) {
		expected += s + "\n";
		s = in.readLine();
	    }
	    
	    System.out.println( "----Your Figures-----" );
	    String yours = _bDraw.toString();
	    System.out.println( yours );
	    System.out.println( "----Expected Figures-----" );
	    System.out.println( expected );

	    String nospacesyours="";
	    for ( int i = 0; i < yours.length(); i++ ) {
	      if ( yours.charAt( i ) != ' ' ) {
		nospacesyours += yours.charAt(i);
	      }
	    }
	    
	    String nospacesexpected="";
	    for ( int i = 0; i < expected.length(); i++ ) {
	      if ( expected.charAt( i ) != ' ' ) {
		nospacesexpected += expected.charAt(i);
	      }
	    }
	    
	    if ( nospacesyours.equals( nospacesexpected ) ) {
		passed = true;
	    }
	    
	    in.close();
	}
	catch ( Exception e ) {
	    System.out.println( "*** Error in test file: " + e );
	    e.printStackTrace();
	}

	if ( passed ) {
	    System.out.println( ">>> Test Passed. ");
	}
	else {
	    System.out.println( "*** Test Failed. ");
	}
	
	return passed;
    }

    String rightJustify( String s, int n ) {
	for ( int i = s.length(); i < n; i++ ) {
	    s = " " + s;
	}
	return s;
    }

    String leftJustify( String s, int n ) {
	for ( int i = s.length(); i < n; i++ ) {
	    s = s + " ";
	}
	return s;
    }

    void runTestSet( String setName ) {
	System.out.println("\n---- Test Set \"" + setName + "\"----" );

	Vector tests = new Vector();

	try {
	    BufferedReader in = 
		new BufferedReader( new FileReader( setName ) );
	    
	    // read line by line
	    String s = in.readLine();
	    while ( s != null ) {
		System.out.println( s );
		StringTokenizer t = new StringTokenizer( s, "|" );
		String fileName = t.nextToken();
		Integer i = new Integer( t.nextToken() );
		int maxPoints = i.intValue();

		tests.add( new TestResult( fileName, maxPoints ));
		s = in.readLine();
	    }
	    in.close();
	}
	catch ( IOException e ) {
	    System.out.println( "*** Error reading test set: " + e );
	}
	
	for ( int i = 0; i < tests.size(); i++ ) {
	    TestResult t = (TestResult) tests.get( i );
	    t._passed = runOneTest( t._fileName );
	    if ( t._passed ) {
		t._points = t._maxPoints;
	    }
	}

	int total = 0;
	int maxTotal = 0;

	String summary = "";
	summary += "------------------------------------------\n";
	summary += "--- Test Summary for \"" + setName + "\" \n";
	summary += "------------------------------------------\n";

	for ( int i = 0; i < tests.size(); i++ ) {
	    TestResult t = (TestResult) tests.get( i );
	    summary += leftJustify( t._fileName, 28 ) + " " +
		rightJustify( "" + t._points, 5 ) + " of " + 
		rightJustify( "" + t._maxPoints, 5 ) + "\n";
	    total += t._points;
	    maxTotal += t._maxPoints;
	}

	summary += "------------------------------------------\n";
	summary += "Total: " + total + " of " + maxTotal;

	System.out.println( summary );

	// Write summary to test.log
	String testlog = "test.log";

	try {
	    PrintWriter out =
		new PrintWriter( new FileOutputStream( testlog ) );
	    out.print( summary );
	    out.close();
	}
	catch ( Exception e ) {
	    System.out.println( "*** Error writing test log" );
	}
    }

    public void actionPerformed( ActionEvent evt ) {
	if ( evt.getSource() instanceof JMenuItem ) {
	    String arg = evt.getActionCommand();
	    
	    if ( arg.equals( "Open" ) ) {
		JFileChooser fc = new JFileChooser(".");
		fc.addChoosableFileFilter( new TestFileFilter( "fig", 
					   "BoilerDraw files end with .fig" ));
		fc.showOpenDialog( _bDraw );
		if ( fc.getSelectedFile() != null ) {
		    _bDrawListener.openFile( fc.getSelectedFile().getName() );
		    if ( _recording ) {
			addRecordLog( "Open|" + fc.getSelectedFile().getName() );
		    }
		}
	    }
	    else if ( arg.equals( "Save" ) ) { 
		JFileChooser fc = new JFileChooser(".");
		fc.addChoosableFileFilter( new TestFileFilter( "fig", 
					   "BoilerDraw files end with .fig" ));
		fc.showSaveDialog( _bDraw );
		if ( fc.getSelectedFile() != null ) {
		    _bDrawListener.saveFile( fc.getSelectedFile().getName() );
		    if ( _recording ) {
			addRecordLog( "Save|" + fc.getSelectedFile().getName() );
		    }
		}
	    }
	    else if ( arg.equals( "Text" ) ) { 
		String txt = JOptionPane.showInputDialog( _bDraw, "Input Text:");
		if ( txt != null ) {
		    _bDrawListener.addText( txt );
		    if ( _recording ) {
			addRecordLog( "Text|" + txt );
		    }
		}
	    }
	    else if ( arg.equals( "Start" ) ) {
		System.out.println( "----Start Recording----\n" );
		_recordLog = "";
		_recording = true;
		_bDrawListener.menuOption( "New" );
		_bDrawListener.menuOption( "Reset Undo" );
		addRecordLog( "Menu|New" );
		addRecordLog( "Menu|Reset Undo" );
	    }
	    else if ( arg.equals( "Stop" ) ) {
		System.out.println( "----Stop Recording----" );

		// Save record log
		JFileChooser fc = new JFileChooser(".");
		fc.addChoosableFileFilter( new TestFileFilter( "tst", 
					   "BoilerDraw tests end with .tst" ));
		
		fc.showDialog( _bDraw, "Save Test" );
		if ( fc.getSelectedFile() != null ) {
		    String fileName = fc.getSelectedFile().getName();
		    try {
			PrintWriter out =
			    new PrintWriter( new FileOutputStream( fileName ) );
			out.print( _recordLog );
			out.println( "Result:" );
			out.print( _bDraw.toString() );
			out.close();
		    }
		    catch ( Exception e ) {
			System.out.println( "*** Error writing file" );
		    }
		}

		System.out.println( "Log:" );
		System.out.println( _recordLog );
		System.out.println( "Result:" );
		System.out.println( _bDraw.toString() );
		_recording = false;
	    }
	    else if ( arg.equals( "Run one" ) ) {
		// run test
		JFileChooser fc = new JFileChooser(".");	
		fc.addChoosableFileFilter( new TestFileFilter( "tst", 
					   "BoilerDraw tests end with .tst" ));
		fc.showDialog( _bDraw, "Run Test" );

		if ( fc.getSelectedFile() == null ) {
		    return;
		}
		
		String fileName = fc.getSelectedFile().getName();

		runOneTest( fileName );
	    }
	    else if ( arg.equals( "Run set" ) ) {
		// run test
		JFileChooser fc = new JFileChooser(".");
		fc.addChoosableFileFilter( new TestFileFilter( "tsts", 
					   "BoilerDraw test sets end with .tsts" ));
		fc.showDialog( _bDraw, "Run Test Set" );

		if ( fc.getSelectedFile() == null ) {
		    return;
		}
		
		String fileName = fc.getSelectedFile().getName();

		runTestSet( fileName );
	    }
	    else if ( arg.equals( "Abort" ) ) {
		System.out.println( "----Abort Recording----\n" );
		_recording = false;
	    }
	    else {
		_bDrawListener.menuOption( arg );
		if ( _recording ) {
		    addRecordLog( "Menu|" + arg );
		}
	    }
	}
    }
    
    void addMenu( JMenuBar mbar, String []  menuOptions ) {
	JMenu m = new JMenu( menuOptions[ 0 ] );
	for ( int i = 1; i < menuOptions.length; i++ ) {
	    JMenuItem mi = new JMenuItem( menuOptions[ i ] );
	    mi.addActionListener( this );
	    m.add( mi );
	}
	
	mbar.add(m);
    }
    
    void setUpMenuBar() {
	JMenuBar mbar = new JMenuBar();
	
	// File menu
	String [] fileMenu = { "File", "New", "Open", "Save", "To String", "Exit" };
	addMenu( mbar, fileMenu );
	
	// Edit Menu
	String [] editMenu = { "Edit", "Undo", "Redo", "Reset Undo", "Select", "Delete" };
	addMenu( mbar, editMenu );
	    boolean _recording;

	// Drawing Menu
	String [] drawingMenu = { "Draw", "Line", "Oval", "Rectangle", "Text" };
	addMenu( mbar, drawingMenu );

	// Color Menu
	String [] colorMenu = new String[ Figure.palette.length + 1 ];
	colorMenu[ 0 ] = "Color";
	for ( int i = 0; i < Figure.palette.length; i++ ) {
	    colorMenu[ i + 1 ] = Figure.palette[ i ];
	}
	addMenu( mbar, colorMenu );
	
	// Group Menu
	String [] groupMenu = { "Group", "Send to back", 
				"Bring to front", "Group", "Ungroup" };
	addMenu( mbar, groupMenu );
	
	// Test Menu
	String [] testMenu = { "Test", "Run one", "Run set"  };
	addMenu( mbar, testMenu );
	
	if ( _recordMode ) {
	    // Record Menu
	    String [] recordMenu = { "Record", "Start", "Stop", "Abort" };
	    addMenu( mbar, recordMenu );
	}
	
	setJMenuBar( mbar );
    }

    void addRecordLog( String s ) {
	_recordLog += s + "\n";
	System.out.println( "RECORD: " + s );
    }

    BoilerDrawMain( BoilerDraw bDraw, BoilerDrawListener bDrawListener, boolean recordMode ) {
	super( "BoilerDraw" );
	
	_bDraw = bDraw;
	_bDrawListener = bDrawListener;
	_recordMode = recordMode;
	
	// Add event handler to close window
	addWindowListener( new WindowAdapter() { 
		public void windowClosing(WindowEvent e) { System.exit(0); } } );
	
	getContentPane().add( bDraw );
	
	setUpMenuBar();
	
	// Set the size of the window 
	setSize(500, 400);
	setVisible( true );
    }
    
    public static void main( String [] args ) {
	boolean recordMode = false;
	String testSet = null;

	if ( args.length > 0 ) {
	    if ( args[ 0 ].equals( "-record" ) ) {
		recordMode = true;
	    }
	    else {
		testSet = args[ 0 ];
	    }
	}

	BoilerDraw bDraw = new BoilerDraw();
	BoilerDrawListener bDrawListener = new BoilerDrawUndoListener( bDraw );
	BoilerDrawMain bDrawMain = new BoilerDrawMain( bDraw, bDrawListener, recordMode );

	if ( testSet == null ) {
	    // input from mouse
	    bDraw.addMouseListener( bDrawMain );
	    bDraw.addMouseMotionListener( bDrawMain );
	}
	else {
	    // Input from test set
	    bDrawMain.runTestSet( testSet );
	    System.exit(0);
	}
    }
}

