import cs311utils.*;

import javax.swing.JFrame;
import java.awt.Color;
import java.io.File;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.swing.JFileChooser;
import java.util.StringTokenizer;
import java.util.Vector;

/** Main application.  This file will compile but will not run (throws null pointer exceptions) until the Deque is implemented.  
 * When run, it opens a file selection dialog.  Select BainbridgeIsland.dat or Kauai.dat and hit OK.
 */

public class Main
{
    private static JFileChooser fileChooser = new JFileChooser( System.getProperty( "user.dir" ) );

    
    public static void main( String[] args )
    {
        // Open data file to read in points
        File file = Main.getOpenFile();
        Vector vec = null;
        try
        {
            // Open the file and read from it using an appropriate stream object
            BufferedReader in = new BufferedReader( new FileReader( file ) );
            // Each line contains an x y location, read them in to a StringBuffer then
            // pass the String from the buffer to a StringTokenizer.  From there, tokenize
            // into each x and y value and parse into a double.  Store these in Point2D objects
            StringBuffer buffer = new StringBuffer();
            while ( in.ready() )
            {
                buffer.append( in.readLine() );
                buffer.append( "\n" );
            }
            StringTokenizer st = new StringTokenizer( buffer.toString() );
            // Now run through a loop parsing out the numbers.  Since we don't know how
            // many there will be, store them in a Vector
            vec = new Vector();
            // This while loop will fail if there are not an even number of number
            // values in the string, since it is getting two tokens at a time before
            // checking that there are more
            while( st.hasMoreTokens() )
            {
                Point2D pt = new Point2D( Double.parseDouble(st.nextToken()),
                                           Double.parseDouble(st.nextToken()) );
                vec.add( pt );
            }       
        }
        catch( Exception e )
        {
            System.out.println("Error reading file.");
            System.exit(1);
        }
        
        // At this point we have all our data in a vector of points called vec
        
        // Compute the convex hull using Melkman's algorithm
        DequeADT dq = Main.melkman( vec );
        
        // Plot the two sets of data
        JFrame f = new JFrame("Coastline Convex Hull");
        f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        f.setSize( 720, 520 );
        f.setLocation( 400, 300 );
        // The following comes from the cs311utils.jar library
        SimpleCartesianPlot plot = new SimpleCartesianPlot( 1.5, 1.0, 700 );
        plot.setPlotBackgroundColor( new Color( 193, 215, 255 ) );
        plot.setBackground( Color.WHITE );
        plot.setXLabel("Longitude (degrees)");
        plot.setYLabel("Latitude (degrees)");
        
        // Create a data object to hold the original data
        DataSet2D data = new DataSet2D();
        // Add points one by one
        for( int i = 0; i < vec.size(); ++i )
        {
            Point2D pt = (Point2D)(vec.get(i));
            data.add( pt.x, pt.y, 0.0 );        // We're not using the 3rd dimension
        }
        // Add data to the plot
        plot.addDataSet( "Original data", data,
                SimpleCartesianPlot.PLOT_TYPE_LINE | 
                SimpleCartesianPlot.PLOT_LINE_SOLID | 
                SimpleCartesianPlot.PLOT_COLOR_BLUE,
                1, 1 );
                
        // And for the computed Hull
        data = new DataSet2D();
        // Add points one by one
        while( !dq.isEmpty() )
        {
            Point2D pt = (Point2D)(dq.popBack());
            data.add( pt.x, pt.y, 0.0 );        // We're not using the 3rd dimension
        }
        // Add data to the plot
        plot.addDataSet( "Convex Hull", data,
                SimpleCartesianPlot.PLOT_TYPE_POINTS | 
                SimpleCartesianPlot.PLOT_TYPE_LINE | 
                SimpleCartesianPlot.PLOT_LINE_SOLID | 
                SimpleCartesianPlot.PLOT_POINT_SQUARE | 
                SimpleCartesianPlot.PLOT_POINT_FILLED | 
                SimpleCartesianPlot.PLOT_COLOR_RED,
                1, 2 );
                
                
        java.awt.Container cp = f.getContentPane();
        cp.add( plot );     // Add the plot component to the content pane
        // And then add a label at the top
        cp.add( new MyJLabel( file.getName(), MyJLabel.CENTER ),
                java.awt.BorderLayout.NORTH);
        // Now show the window
        f.setVisible(true);
    }
    

    /**
     *  Get a File object for opening from the local file system.
     *
     *@return    A file to be opened. If file does not exist or has problems it
     *      returns null.
     */
    public static File getOpenFile()
    {
        File file;
        fileChooser.setDialogTitle( "Select a file to open" );
        int status = fileChooser.showOpenDialog( null );
        if ( status == JFileChooser.APPROVE_OPTION )
        {
            file = fileChooser.getSelectedFile();
            
            if ( file.exists() && file.canRead() )
                return file;
        }
        return null;// no file selected
    }
    
    /** Compute the convex hull of the simple polygon defined in the given vector
        using Melkman's algorithm.  The hull is returned in a deque.  Points in the
        vector must be Point2D objects.*/
    public static DequeADT melkman( Vector v )
    {
        DequeADT d = new Deque( );
        
        Point2D pa = (Point2D)(v.get(0));
        Point2D pb = (Point2D)(v.get(1));
        Point2D pc = (Point2D)(v.get(2));
        
        // The following is an outline of the pseudocode from the lab.  It is commented out
        // because it is incomplete -- you must finish it.

        if( Point2D.isLeft(pa,pb,pc) )
        {
            d.pushBack(pa);
            d.pushBack(pb);         
        }
        else
        {
            d.pushBack(pb);
            d.pushBack(pa);
        }
        int i = 3;
        while( i < v.size() )
        {
            while( Point2D.isLeft( d.front() , pc , v.get(i) ) &&
                   Point2D.isLeft( pc , d.back() , v.get(i) ) &&
                    i < v.size() - 1 )
            {
                ++i;
            }
            if( i >= v.size() )
                break;
                
                
                
            Point2D pd;

            d.pushBack(pc);
            d.pushFront(pc);
            boolean removingFront = true;
            while( removingFront == true )
            {
                pd = d.popFront();
                if( Point2D.isLeft( d.front() , pd , v.get(i) ) )
                {
                    d.pushFront();
                    removingFront = false;
                }
            }
            
            boolean removingBack = true;
            while( removingBack )
            {
                pd = d.popBack();
                if( Point2D.isLeft( v.get(i) , pd , d.back() ) )
                {
                    d.pushBack(pd);
                    removingBack = false;
                }
            }
            
            
            pc = (Point2D)(v.get(i));
            ++i;
        }
        return d;
    }
}

