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

class BoilerDraw 
    extends JPanel
    implements BoilerDrawListener
{
    static final int SELECT_MODE = 1;
    static final int LINE_MODE = 2;
    static final int OVAL_MODE = 3;
    static final int RECTANGLE_MODE = 4;

    List _figures;
    int _mode;
    int _currentX;
    int _currentY;
    int _oldX;
    int _oldY;

    Vector _currentFigures;
    String _currentColor;
    boolean _paintMarkRectangle;

    public void mouseDragged( int x, int y ) {
	_currentX = x;
	_currentY = y;

	switch (_mode) {
	case RECTANGLE_MODE:
	case OVAL_MODE:
	case LINE_MODE:
	    // Resize all current elements
	    for ( int i = 0; i < _currentFigures.size(); i++ ) {
		Figure f = (Figure) _currentFigures.get( i );
		f.resize( _oldX, _oldY, _currentX, _currentY );
	    }
	    repaint();
	    break;
	case SELECT_MODE:
	    if ( ! _currentFigures.isEmpty() ) {
		int dx = _currentX - _oldX;
		int dy = _currentY - _oldY;

		// Move all the elements
		for ( int i = 0; i < _currentFigures.size(); i++ ) {
		    Figure f = (Figure) _currentFigures.get( i );
		    f.changePosition( dx, dy );
		}

		_oldX = _currentX;
		_oldY = _currentY;
		repaint();
	    }
	    else {
		_paintMarkRectangle = true;
		repaint();
	    }
	    break;
	}
    }
  
    public void mousePressed( int x, int y ) {
	_currentX = x;
	_currentY = y;
	_oldX = _currentX;
	_oldY = _currentY;

	switch (_mode) {
	case RECTANGLE_MODE:
	    Rectangle r = new Rectangle( _currentColor, _currentX, _currentY, 0, 0 );
	    r.resize( _currentX, _currentY, _oldX, _oldY );
	    _figures.addToFront( r );
	    _currentFigures.add(r);
	    repaint();
	    break;
	case OVAL_MODE:
	    Oval o = new Oval( _currentColor, _currentX, _currentY, 0, 0 );
	    o.resize( _currentX, _currentY, _oldX, _oldY );
	    _figures.addToFront( o );
	    _currentFigures.add(o);
	    repaint();
	    break;
	case LINE_MODE:	
	    Line l = new Line( _currentColor, _currentX, _currentY, _oldX, _oldY );
	    l.resize( _currentX, _currentY, _oldX, _oldY );
	    _figures.addToFront( l );
	    _currentFigures.add(l);
	    repaint();
	    break;
	case SELECT_MODE:
	    // Find selected figure

	    // Search for selected figure
	    Figure found = null;
	    ListForwardIterator iterator = new ListForwardIterator( _figures );
	    while ( iterator.hasMoreNodes() ) {
		Figure f = (Figure) iterator.nextNode();
		if ( f.isSelected( _currentX, _currentY ) ) {
		    found = f;
		    break;
		}
	    }

	    if ( found != null ) {
		// Found a figure
		// Search if it is one of the already selected figures
		boolean alreadySelected = false;
		for ( int i = 0; i < _currentFigures.size(); i++ ) {
		    Figure f = (Figure) _currentFigures.get( i );
		    if ( f == found ) {
			alreadySelected = true;
			break;
		    }
		}

		if ( !alreadySelected ) {
		    // Selected figure was not in currentFigures
		    _currentFigures.clear();
		    _currentFigures.add( found );
		}
	    }
	    else {
		// no figure was selected
		_currentFigures.clear();
	    }
		
	    repaint();
	}
    }
  
    public void mouseReleased( int x, int y ) {
	switch (_mode) {
	case RECTANGLE_MODE:
	case OVAL_MODE:
	case LINE_MODE:	
	    _mode = SELECT_MODE;
	    break;
	case SELECT_MODE:
	    break;
	}
	
	if ( _paintMarkRectangle ) {
	    // get box
	    int minX = _oldX;
	    int maxX = _currentX;
	    int minY = _oldY;
	    int maxY = _currentY;
	    if ( minX > maxX ) {
		minX = _currentX;
		maxX = _oldX;
	    }
	    if ( minY > maxY ) {
		minY = _currentY;
		maxY= _oldY;
	    }

	    // Find selected figures
	    _currentFigures.clear();
	    ListForwardIterator iterator = new ListForwardIterator( _figures );
	    while ( iterator.hasMoreNodes() ) {
		Figure f = (Figure) iterator.nextNode();
		if ( f.isInBox( minX, minY, maxX-minX, maxY-minY ) ) {
		    _currentFigures.add(f);
		}
	    }
 	    _paintMarkRectangle = false;
	    repaint();
	}
    }
  
    public void menuOption( String option ) {
	if ( option.equals( "Select" ) ) {
	    _mode = SELECT_MODE;
	    _currentFigures.clear();
	    repaint();
	}
	else if ( option.equals( "Line" ) ) {
	    _mode = LINE_MODE;
	    _currentFigures.clear();
	    repaint();
	}
	else if ( option.equals( "Oval" ) ) {
	    _mode = OVAL_MODE;
	    _currentFigures.clear();
	    repaint();
	}
	else if ( option.equals( "Rectangle" ) ) {
	    _mode = RECTANGLE_MODE;
	    _currentFigures.clear();
	    repaint();
	}
	else if ( option.equals( "New" ) ) {
	    _figures.clear();
	    _currentFigures.clear();
	    _currentColor = "Green";
	    _mode = SELECT_MODE;
	    _paintMarkRectangle = false;
	    repaint();
	}
	else if ( option.equals( "Delete" ) ) {
	    if ( ! _currentFigures.isEmpty() ) {
		for ( int i = 0; i < _currentFigures.size(); i++ ) {
		    Figure f = (Figure) _currentFigures.get( i );
		    f.unlink();
		}
		_currentFigures.clear();
		repaint();
	    }
	}
	else if ( option.equals( "Bring to front" ) ) {
	    if ( !_currentFigures.isEmpty() ) {
		for ( int i = 0; i < _currentFigures.size(); i++ ) {
		    Figure f = (Figure) _currentFigures.get( i );
		    f.unlink();
		}
		for ( int i = _currentFigures.size()-1; i >= 0; i-- ) {
		    Figure f = (Figure) _currentFigures.get( i );
		    _figures.addToFront( f );
		}
		repaint();
	    }
	}
	else if ( option.equals( "Send to back" ) ) {
	    if ( !_currentFigures.isEmpty() ) {
		for ( int i = 0; i < _currentFigures.size(); i++ ) {
		    Figure f = (Figure) _currentFigures.get( i );
		    f.unlink();
		}
		for ( int i = 0; i < _currentFigures.size(); i++ ) {
		    Figure f = (Figure) _currentFigures.get( i );
		    _figures.addToTail( f );
		}
		repaint();
	    }
	}
	else if ( option.equals( "Group" ) ) {
	    if ( _currentFigures.isEmpty() ) {
		return;
	    }

	    // get the selected element in front
	    ListNode first = (ListNode)	_currentFigures.get(0);
	    ListNode previous = (ListNode) first.getPrevious();

	    for ( int i = 0; i < _currentFigures.size(); i++ ) {
		Figure f = (Figure) _currentFigures.get( i );

		f.unlink();
	    }
	    Figure group = new FigureGroup(_currentFigures);
	    previous.addNext( group );
	    _currentFigures = new Vector();
	    _currentFigures.add( group );
	    repaint();
	}
	else if ( option.equals( "Ungroup" ) ) {
	    Vector newCurrentFigures = new Vector();
	    for ( int i = 0; i < _currentFigures.size(); i++ ) {
		Figure f = (Figure) _currentFigures.get( i );

		if ( f instanceof FigureGroup ) {
		    FigureGroup fg = (FigureGroup) f;
		    ListNode previous = f.getPrevious();

		    fg.unlink();

		    for ( int j=fg.figures().size()-1; j >=0; j-- ) {
			previous.addNext( (Figure) fg.figures().get(j) );
		    }

		    // Add figures in order
		    for ( int j=0; j < fg.figures().size(); j++ ) {
			newCurrentFigures.add( (Figure) fg.figures().get(j) );
		    }
		}
		else {
		    newCurrentFigures.add(f);
		}
	    }

	    _currentFigures = newCurrentFigures;
	    repaint();
	    System.out.println("menuOption end\n");

	}
	else if ( option.equals( "To String" ) ) {
	    System.out.println( toString() );
	}
	else if ( option.equals( "Exit" ) ) {
	    System.exit( 0 );
	}
	else {
	    // See if it matches a color
	    boolean colorFound = false;
	    for ( int i =0; i < Figure.palette.length; i++ ) {
		if ( Figure.palette[i].equals( option ) ) {
		    colorFound = true;
		    _currentColor = option;
		}
	    }
	    if ( colorFound && !_currentFigures.isEmpty() ) {
		for ( int i = 0; i < _currentFigures.size(); i++ ) {
		    Figure f = (Figure) _currentFigures.get( i );
		    f.setColor( _currentColor );
		}
		repaint();
	    }
	}
    }

    public Figure readOneFigure( String s, BufferedReader in ) 
	throws IOException 
    {
	StringTokenizer t = new StringTokenizer( s, "|" );
	String figureName = t.nextToken();
	if ( figureName.equals( "Rectangle" )) {
	    return new Rectangle( t );
	}
	else if ( figureName.equals( "Oval" )) {
	    return new Oval( t );
	}
	else if ( figureName.equals( "Line" )) {
	    return new Line( t );
	}
	else if ( figureName.equals( "Text" )) {
	    return new Text( t, this );
	}
	else if ( figureName.equals( "FigureGroup" )) {
	    return new FigureGroup( t, in, this );
	}
	
	return null;
    }

    public void openFile( String fileName ) {
	_figures.clear();
	_currentFigures.clear();
	_mode = SELECT_MODE;
	_paintMarkRectangle = false;

	try {
	    BufferedReader in = new BufferedReader( new FileReader( fileName ) );
	    
	    // read line by line
	    String s = in.readLine();
	    while ( s != null ) {
		Figure f = readOneFigure( s, in );
		_figures.addToFront( f );
		
		s = in.readLine();
	    }
	    in.close();
	    repaint();
	}
	catch ( Exception e ) {
	    System.out.println( "*** Error reading file: " + e );
	}

	repaint();
    }

    public String toString() {
	String s = "";
	ListBackwardIterator iterator = new ListBackwardIterator( _figures );
	while ( iterator.hasMoreNodes() ) {
	    Figure f = (Figure) iterator.nextNode();
	    s += f.toString();
	}

	//s += "--- Selected Figures ---";
	//for ( int i = 0; i < _currentFigures.size(); i++ ) {
	//Figure f = (Figure) _currentFigures.get( i );
	//s += f.toString();
	//}

	return s;
    }

    public void saveFile( String fileName ) {
	try {
	    PrintWriter out = new PrintWriter( new FileOutputStream( fileName ) );
	    out.print( toString() );
	    out.close();
	}
	catch ( Exception e ) {
	    System.out.println( "*** Error writing file" );
	}
    }

    public void addText( String text ) {
	Text t = new Text( _currentColor, _currentX, _currentY, text, this );
	t.resize( _currentX, _currentY, _oldX, _oldY );
	_figures.addToFront( t );
	_currentFigures.clear();
	_currentFigures.add(t);
	repaint();
    }
    
    public void paintComponent( Graphics g ) {
	super.paintComponent( g );
	ListBackwardIterator iterator = new ListBackwardIterator( _figures );
	
	while ( iterator.hasMoreNodes() ) {
	    Figure f = (Figure) iterator.nextNode();
	    f.paint( g );
	}

	for ( int i = 0; i < _currentFigures.size(); i++ ) {
	    Figure f = (Figure) _currentFigures.get( i );
	    f.paintSelected( g );
	}

	if ( _paintMarkRectangle ) {
	    // Paint mark rectangle
	    g.setColor( Color.blue.brighter() );
	    int minX = _oldX;
	    int maxX = _currentX;
	    int minY = _oldY;
	    int maxY = _currentY;
	    if ( minX > maxX ) {
		minX = _currentX;
		maxX = _oldX;
	    }
	    if ( minY > maxY ) {
		minY = _currentY;
		maxY= _oldY;
	    }
	    g.drawRect( minX, minY, maxX - minX, maxY - minY );
	}

    }
    
    public BoilerDraw() {
	_figures = new List();
	_currentFigures = new Vector();
	_currentColor = "Green";
	_mode = SELECT_MODE;
	_paintMarkRectangle = false;
    }
}

