/**
 * @author
 *  Behrooz Badii 	bb2122@columbia.edu
 *  Hanhua Feng         hanhua@cs.columbia.edu
 *  Edan Harel 	        edan@columbia.edu
 */
package CookieCutter.g6;

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

class Painter extends JPanel {
    static Painter main = null;

    private JFrame frame;

    class Element {
        Object object;
        Color color;
        boolean filled;

        Element( Object o, int c ) {
            object = o;
            filled = ( c & 0xff000000 ) != 0;
            if ( filled )
                color = new Color( c, true );
            else
                color = new Color( c & 0xffffff );            
        }
    }

    Vector elements = new Vector();

    double xmin = -2, xmax = 5.0, ymin = -2.0, ymax = 5.0;
    // double xmin = -0.6, xmax = 1.2, ymin = -0.3, ymax = 1.5;
    int width, height;
    static int counter = 0;

    /** create a painting panel
     * @param frame    the parent frame where the painting panel is located
     */
    public Painter( JFrame frame ) {
        this.frame = frame;
    }

    public void setRange( double x0, double x1, double y0, double y1 ) {
        xmin = x0;
        xmax = x1;
        ymin = y0;
        ymax = y1;
    }

    public static void setMain( Painter p ) {
        main = p;
    }

    public static void waitClick() {
        try 
        {
            while ( counter <= 0 )
            {
                Thread.sleep( 50 );
            }
            counter--;
        }
        catch ( InterruptedException e ) 
        {
        }
    }

    int scaleX( double x ) {
        return (int)( (x - xmin) * width / (xmax-xmin) );
    }

    int scaleY( double y ) {
        return (int)( (ymax - y) * height / (ymax-ymin) );
    }

    int scaleXS( double w ) {
        return (int)( w * width / (xmax-xmin) );
    }

    int scaleYS( double h ) {
        return (int)( h * height / (ymax-ymin) );
    }

    public void draw( Object o, int color ) {
        elements.add( new Element( o, color ) );

        repaint();
    }

    public void clear() {
        elements = new Vector();
        repaint();
    }

    public void paintComponent( Graphics g ) {
        int dist = g.getFont().getSize();
        int line = 1;

        super.paintComponent( g );

        Insets insets = getInsets();
        width = getWidth() - insets.left - insets.right;
        height = getHeight() - insets.top - insets.bottom;

        Vector data = elements;

        for ( int i=0; i<data.size(); i++ )
        {
            Element ele = (Element) data.get( i );
            Object o = ele.object;
            Color c = ele.color;
            
            g.setColor( c );
            
            if ( o instanceof Point ) 
            {
                Point p = (Point) o;
                g.drawRect( scaleX(p.x()), scaleY(p.y()), 1, 1 );
            }
            else if ( o instanceof Segment )
            {
                Segment s = (Segment) o;
                g.drawLine( scaleX(s.x0()), scaleY(s.y0()), 
                            scaleX(s.x1()), scaleY(s.y1()) );
            }
            else if ( o instanceof Polygon )
            {
                Polygon pg = (Polygon) o;
                int n = pg.n();
                int[] x = new int[n];
                int[] y = new int[n];

                for ( int j=0; j<n; j++ )
                {
                    x[j] = scaleX(pg.x(j));
                    y[j] = scaleY(pg.y(j));
                }

                if ( ele.filled )
                    g.fillPolygon( x, y, n );
                else
                    g.drawPolygon( x, y, n );
            }
            else if ( o instanceof Rectangle )
            {
                Rectangle r = (Rectangle)o;
                if ( ele.filled )
                {
                    g.fillRect( scaleX(r.x0()), scaleY(r.y1()),
                                scaleXS(r.width()), scaleYS(r.height()) );
                }
                else
                {
                    g.drawRect( scaleX(r.x0()), scaleY(r.y1()),
                                scaleXS(r.width()), scaleYS(r.height()) );
                }
            }
            else if ( o instanceof String )
            {
                g.drawString( (String)o, 2, dist * line++ );
            }
        }
    }


    public static final Painter create( String title, int width, int height ) {
        try {
            UIManager.setLookAndFeel(
                UIManager.getCrossPlatformLookAndFeelClassName() );
        } catch (Exception e) {}

        JFrame frame = new JFrame( title );
        Painter pane = new Painter( frame );
        frame.getContentPane().add( pane, BorderLayout.CENTER );

        frame.addWindowListener(
            new WindowAdapter()
            {
                public void windowClosing( WindowEvent e ) {
                    counter = 1000;
                    System.exit(0);
                }
            });

        pane.addMouseListener(
            new MouseAdapter()
            {
                public void mouseClicked( MouseEvent e ) {
                    counter++;
                }
            } );

        pane.setPreferredSize( new Dimension( width, height ) );
        frame.pack();

        frame.setLocationRelativeTo( null );
        frame.setVisible( true );

        return pane;
    }

    // modified from Util.java
    public static String[] split(String delim, String str) {
        
        ArrayList list = new ArrayList();
        StringTokenizer st = new StringTokenizer( str, delim );
        
        while ( st.hasMoreTokens() )
            list.add( st.nextToken() );

        return (String[]) list.toArray( new String[0] );
    }

    // modified from CookieCutter.java
    private static  Polygon scanPolygon( StringBuffer sb ) {
        Vector vlist = new Vector();

        if (sb.length() <= 0) 
            return null;

        String[] toks = split( ",\t\n ", new String(sb) );
        
        for ( int i=0; i<toks.length-1; i+=2 )
            vlist.add( 
                new Point( Double.parseDouble(toks[i]), 
                           Double.parseDouble(toks[i+1]) ) );
        
        if ( vlist.size() <= 0 )
            return null;

        return new Polygon( (Point[]) vlist.toArray( new Point[0] ) );
    }

    // this function is modified from from CookieCutter.java
    static Polygon[] readGameFile( String filename ) {
        StringBuffer sb = new StringBuffer();
        Vector vlist = new Vector();
        File file = new File( filename );

        if ( !file.exists() )
            return null;

        try
        {
            BufferedReader br = new BufferedReader( new FileReader(file) );

            String str;
            while ( ( str = br.readLine()) != null ) 
            {
                if ( str.length() < 1 )
                    continue;
                
                if ( str.charAt(0) == '#' )
                    continue;
                
                if ( str.charAt(0) == '!' ) 
                {
                    Polygon pg = scanPolygon( sb );
                    if ( pg != null )
                        vlist.add( pg );
                    sb = new StringBuffer();
                    continue;
                }
                
                sb.append(str);
                sb.append("\n");
            }

            Polygon pg = scanPolygon( sb );
            if ( pg != null )
                vlist.add( pg );
        
            return (Polygon[]) vlist.toArray( new Polygon[0] );
        } 
        catch( Exception e )
        {
            e.printStackTrace();
        }

        return null;
    }

    private static String[] _cmp_text = { 
        "Equal", "Greater than", "Less than", "Disjoint", "Joint" };


    private static void test_cut( Painter pane, Polygon pg0, Polygon pg1, 
                                  double x, double y, 
                                  double dx, double dy,
                                  int index ) {

        pane.draw( pg0.translate( x, y ), 0xa0a0a0 );
        pane.draw( pg1.translate( x, y ), 0x808080 );
        
        Polygon[] cpgs = pg0.crossify( pg1 );

        if ( cpgs != null )
        {
            for ( int j=0; j<cpgs[0].n(); j++ )
                pane.draw( 
                    cpgs[0].vertex(j).translate(x,y), 0x008000 );
            
            for ( int j=0; j<cpgs[1].n(); j++ )
                pane.draw( 
                    cpgs[1].vertex(j).translate(x,y), 0x800000 );
                    
            pane.draw( cpgs[0].translate( x+dx, y+dy ), 0x008000 );
            pane.draw( cpgs[1].translate( x+dx, y+dy ), 0x800000 );

            pane.draw( "Relation of two modified polygons in case " 
                       + index + ": " 
                       + _cmp_text[cpgs[0].compare( cpgs[1] )], 0 );
        }
        else
        {
            pane.draw( "Relation of two original polygons in case " 
                       + index + ": " 
                       + _cmp_text[ pg0.compare( pg1 ) ], 0 );
        }

        Polygon[][] sets;
        try {
            sets = pg0.cutsets( pg1 );
        }
        catch (GeometryException e) 
        {
            sets = null;
        }

        if ( sets != null )
        {
            for ( int k=0; k<2; k++ )
            {
                System.out.println( "Set#" + k 
                                    + " [" + sets[k].length + "]  " );
                                    
                for ( int j=0; j<sets[k].length; j++ )
                {
                    pane.draw( 
                        sets[k][j].translate( x+2*dx, y+2*dy ),
                        k*0xff0000 + 0x1000080 + (j*255/sets[k].length) );
                    System.out.println( sets[k][j].toString() );
                }
            }
        }
    }

    public static void main( String[] args ) {
        Painter pane = create( "Polygon Test", 640, 640 );
        Polygon[] pgs = null;
        main = pane;

        if ( 1 == args.length ) 
        {
            pgs = readGameFile( args[0] );
            if ( null == pgs )
                System.err.println( "File not found: " + args[0] );
        }

        if ( pgs == null )
        {
            pgs = new Polygon[] { new Polygon( new Point[] {
                new Point( 0.1, 0.1 ),
                new Point( 0.2, 1.2 ),
                new Point( 0.7, 0.3 ),
                new Point( -0.1, -0.5 ) } ) };
        }

        Rectangle rect = new Rectangle( 1, 3, 1, 1 );

        for ( int i=0; i<pgs.length; i++ )
        {
            pane.draw( rect, 0xffaa66 );
            Polygon pg = pgs[i];

            pane.draw( "Polygon: " + pg.toString(), 0 );

            StringBuffer sb = new StringBuffer( "Angles:" );
            for ( int j=0; j<pg.n(); j++ )
            {
                sb.append( " " );
                sb.append( Plane.format( Math.toDegrees( pg.angle(j) ) ) );
            }
            pane.draw( sb.toString(), 0 );
            pane.draw( "isValid(): " + pg.isValid(), 0 );
            pane.draw( "isSimple(): " + pg.isSimple(), 0 );
            pane.draw( "isNegative(): " + pg.isNegative(), 0 );
            pane.draw( "isConvex(): " + pg.isConvex(), 0 );
            
            Polygon pg0 = pg.translate( 1, 3 );

            pane.draw( pg0, 0 );
            pane.draw( pg0.rotate( 1, 3, 2*Math.PI*30/360 ), 0x20 );
            pane.draw( pg0.rotate( 1, 3, 2*Math.PI*60/360 ), 0x40 );
            pane.draw( pg0.rotate( 1, 3, 2*Math.PI*90/360 ), 0x60 );
            pane.draw( pg0.rotate( 1, 3, 2*Math.PI*120/360 ), 0x80 );
            
            Polygon pg1 = pg.translate( 2, 3 );
        
            pane.draw( pg1, 0 );
            pane.draw( pg1.rotate( 3, 4, 2*Math.PI*30/360 ), 0x20 );
            pane.draw( pg1.rotate( 3, 4, 2*Math.PI*60/360 ), 0x40 );
            pane.draw( pg1.rotate( 3, 4, 2*Math.PI*90/360 ), 0x60 );
            pane.draw( pg1.rotate( 3, 4, 2*Math.PI*120/360 ), 0x80 );
            
            Polygon pg2 = pg.rotate( 3, 3, -2*Math.PI*45/360 );
            pane.draw( pg2, 0 );
            pane.draw( pg2.dilate( 0.0001 ), 0x20);
            pane.draw( pg2.dilate( 0.05 ), 0x40 );
            pane.draw( pg2.dilate( 0.1 ), 0x60 );
            pane.draw( pg2.dilate( 0.2 ), 0x80 );
            
            pane.draw( pg0.center(), 0 );
            pane.draw( pg0.centroid(), 0xffffff );
            pane.draw( pg2.center(), 0 );
            pane.draw( pg2.centroid(), 0xffffff );

            pane.draw( "Rotated polygon: " + pg2.toString(), 0 );
            pane.draw( "Rotated isNegative(): " + pg2.isNegative(), 0 );

            pg = pg.abs();

            for ( int l=0; l<4; l++ )
            {
                int x = l%2*3-1, y= -l/2+2;
                Polygon pg3 = pg.rotate( 0, 0, Math.toRadians( 25 * l ) );
                // pg3 = pg.translate( 0, 0.1 ); 
                
                test_cut( pane, pg, pg3, x, y, 1, 0, l );
            }


  Polygon tpg0 = new Polygon( new Point[] { new Point(0.2054,0.4), new Point(2.6333,0.4), new Point(2.6333,0.6), new Point(0.4733,0.6), new Point(0.4733,0.5786) } );
  Polygon tpg1 = new Polygon( new Point[] { new Point(0.0933,1.0747), new Point(0.0933,0.7786), new Point(0.4733,0.5253), new Point(0.4733,0.8214) } );

        System.out.println( "Find cut sets of " + tpg0.toString() 
                            + " and " + tpg1.toString() );

/*
Polygon tpg0 = new Polygon( new Point[] { new Point(0.4474,0.4), new Point(2.6333,0.4), new Point(2.6333,0.6), new Point(0.6974,0.6), new Point(0.701,0.5971), new Point(0.6733,0.5786), new Point(0.6733,0.5168), new Point(0.6333,0.5488) } ); 

Polygon tpg1 = new Polygon( new Point[] { new Point(0.157,0.1677), new Point(0.6896,0.5938), new Point(0.6444,0.6555), new Point(0.3097,0.4323), new Point(-0.2229,0.0062), new Point(-0.1778,-0.0555) } );

            Polygon tpg0 = new Polygon(
            new Point[] {
                new Point(0.4333,0.6), new Point(0.1333,0.4), new Point(2.6333,0.4), new Point(2.6333,0.6), new Point(0.4333,0.6) } );

            Polygon tpg1 = new Polygon( new Point[] {
                new Point(0.1333,0.4), new Point(0.1333,0.2), new Point(0.6333,0.6), new Point(0.6333,0.8) } );
*/
         
            test_cut( pane, tpg0, tpg1, -1, -2, 0, 1, 100 );
            
            waitClick();
            pane.clear();
        }
    }
}
