import java.io.*;
import java.util.*;
import javax.swing.*;
import java.awt.Font;
import java.awt.event.*;

public class ChessEngine {

    //***********************************************************************************
    //start of jason's code
    //**********************************************************************************
    
    private static Board board;
    private static Search s;
    public static PrintStream outWriter;       // Public for debug purposes
    private static boolean xboard;
    private static boolean white;

    // A variable to keep track of thinking mode (i.e. xboard profiling facility)
    private static boolean thinkingOn = false;  // Not used currently

    public static void main(String[] args) {
        // Initialize some variables
        board = new Board();
        int maxdepth = 3;     // Depth can be set from command line with "-d DEPTH"
        white = false;
        
        // This Reader is used to get input from xboard via stdin
	// Output is sent to xboard through the default Writer System.out
        BufferedReader inReader = new BufferedReader(new InputStreamReader(System.in));
        
        // We make a PrintStream so that we can keep a log of what xboard sent to the
        // engine(this is merely for debug purposes and has nothing to do with the 
        //funcitonality of the program in most cases.)
        outWriter = new PrintStream(System.out, true);  // This used to be a PrintWriter
        System.setErr(outWriter); // This may seem strange, but it's difficult
                                  // to see an error in xboard mode otherwise.
        
        // IMPORTANT!  Since the addition of command line play one of three flags is required
        //             to play in xboard mode.  They are -s, -f, -n.  See below for details.
        // Parse command line parameters.
        // Some of the commands do not make sense together.
        // Most of them do not make sense for a command line game.
        if (args != null && args.length > 0) {
                for (int i = 0; i < args.length; i++) {
                        if (args[i].equals("-s")) {
                                // Now on the -s flag instead of writing everything to a file
                                // a seperate thread is created to bring up a frame with a text
                                // area that will recieve all of the output.
                                PipedInputStream snk = new PipedInputStream();
                                try {
                                        outWriter = new PrintStream(new PipedOutputStream(snk), true);
                                } catch (IOException ex) {
                                        ex.printStackTrace();
                                }
                                new StatusDisplayThread(snk).start();
                        } else if (args[i].equals("-n")) {
                                // On the flag -n send everything to /dev/null and waste a whole
                                // bunch of clock cycles sending it there.  This is the truly
                                // wasteful method of turning debug print statements off.
                                try {
                                        outWriter = new PrintStream(new FileOutputStream("/dev/null"),
                                                                    true);
                                } catch (IOException ex) {
                                        ex.printStackTrace();
                                }
                        } else if (args[i].equals("-f")) {
                                // Now on flag -f send output to a log file.  This isn't as useful
                                // as it used to be.
                                try {
                                        outWriter = new PrintStream(new FileOutputStream("ChessEngine.log"),
                                                                    true);
                                } catch (IOException ex) {
                                        ex.printStackTrace();
                                }
                        } else if (args[i].equals("-d")) {
                                // On the flag -d set the search depth to the parameter following.
                                // The parameter and flag should be seperated by a space e.g. "-d 4"
                                try {
                                        maxdepth = Integer.parseInt(args[i + 1]);
                                } catch (Exception ex) {
                                        System.err.println("Please enter a parameter with -d.");
                                        System.exit(0);
                                }
                        }
                }
        }

        // Now that we've parsed the command line we can set up some stuff and print out a
        // welcome message.
        outWriter.println("Welcome to CS4210's Java Chess Engine");
        
        s = new Search(maxdepth);
        outWriter.println("Search depth set to " + maxdepth);
        outWriter.println(board);
        outWriter.print(">");

	// Variables to reference strings recieved and sent back
	String command = "";
	String returnCommand = "";
        
        // This is a special case used to determine if the game is being played with xboard
        // or via the command line.
        try {
            command  = inReader.readLine();
        } catch (Exception ex) {
                ex.printStackTrace();
        }
        
        if (command.equals("xboard")) {
            // Instruct xboard to wait until initialization is finished before trying to
            // start a new game.
            // This may not be necessary, but will prevent unwanted behavior.
            xboard = true;
            System.out.println("feature done=0");
        } else {
            xboard = false;
            evaluateMessage(command);
        }
                
        
        // Start an infinite loop to take arguments from stdin and spit moves
	// back out to stdout.

        while (true) {
            // Get a line of text from xboard/command line
            try {
                command = inReader.readLine();
            } catch (IOException ex) {
		// Print an exception to stderr and log file
                ex.printStackTrace();
		ex.printStackTrace(outWriter);
            }
            
            // Do something with the text
            // Print out to the log file so that we can see what happened
            outWriter.println("Recieved: " + command);

	    // Respond to the message
	    returnCommand = evaluateMessage(command);
                        
            // Transmit some kind of message back to xboard
	    if (xboard && !returnCommand.equals("")) {
		System.out.println(returnCommand);
	    }
        }
    }

    private static String evaluateMessage(String command) {
	// I think that it will be more understandable and java-like to use
	// a series of "if-else if" statements rather than a switch statement.
	// We will put the most frequently called commands at the top to avoid
	// constantly evaluating infrequently used commands.
	// Commands which do not require response will return the empty string.
	// Most commands to not require a response, but the most frequent command
        // (a move command) always requires a response.
	
	if (commandIsMove(command)) {
	    outWriter.println("Evaluating a move.");
            Move m = new Move(command);
            outWriter.println("Move is: " + m);
	    // Do something with the move
            if (board.isLegal(m)) {
                board.makeMove(m);
            } else {
                outWriter.print("Illegal move: " + m + "\n>");
                return "";
            }
	    // Return a move
	    Move returnMove = s.getMove(board, white);
            board.movePiece(returnMove);
	    outWriter.println("Returning move " + returnMove + "\n" + board);
            outWriter.print(">");
	    return ("move " + returnMove);
	} else if (command.equals("undo") || command.equals("remove")) {
            outWriter.println("Undoing a move.");
                // This is usually a remove which means undo one move for
                // each side.
            board.unmakeMove();
            board.unmakeMove();
            outWriter.println(board);
	    return "";
	} else if (command.equals("post")) {
	    outWriter.println("Turning thinking on.");
	    thinkingOn = true;
	    return "";
	} else if (command.equals("nopost")) {
	    outWriter.println("Turning thinking off.");
	    thinkingOn = false;
	    return "";
	} else if (command.equals("quit")) {
	    outWriter.println("I'm quitting.");
	    System.exit(0);
	    return "";           // We will never get here
	} else if (command.equals("new")) {
	    outWriter.println("Starting a new game.");
	    // Initialize everything for a new game
	    return "";
	} else if (commandIsTimeConstruct(command)) {
	    outWriter.println("Trying to do something related to time.");
	    // Do something time related
	    return "";
        } else if (commandIsSetDepth(command)) {
            outWriter.println("Setting max search depth.");
            StringTokenizer strtok = new StringTokenizer(command);
            strtok.nextToken();
            int depth = Integer.parseInt(strtok.nextToken());
            // set maxdepth to depth
            outWriter.println("Max search depth set to " + depth);
            return "";
        } else if (command.equals("white")) {
            white = true;
            return "";
        } else if (command.equals("black")) {
            white = false;
            return "";
        } else if (command.equals("go")) {
            // This does not conform exactly to xboard protocol, but should
            // work for now.
            if (white) {
                outWriter.println("Leaving force mode.");
                Move returnMove = s.getMove(board, white);
                board.movePiece(returnMove);
                outWriter.println("Returning move " + returnMove + "\n" + board);
                outWriter.print(">");
                return ("move " + returnMove);
            } else {
                return "";
            }
	} else if (command.equals("protover 2")) {
	    outWriter.println("Setting up xboard.");
	    // Construct a string representing the state in which
	    // we want xboard to be when the game starts.
            // This basically just turns of a bunch of commands that xboard 
            // might normally send.
	    return ("feature usermove=0 " +    // Don't send "usermove" before each move
		    "time=0 " +                // No time information for now
		    "draw=0 " +                // No draws for now
		    "sigint=0 " +              // Don't send signals
		    "sigterm=0 " +
		    "reuse=0 " +               // No new games for now
		    "analyze=0 " +             // Don't enter analyze mode
		    "myname=\"CS4210 Java Engine\"" +  //Name for title bar
		    "done=1");                 // We're done setting up
	} else if (command.equals("xboard") || command.equals("random") ||
		   command.equals("easy") || command.equals("hard")) {
	    // Meaningless commands to us
            outWriter.println("Ignoring command " + command);
	    return "";
	} else {
	    return ("Error (unknown command): " + command);
	}
    }

    // Commands which require response(action):
    // "new" reset everything for a new game
    // "quit" clean up the engine and exit (exit immediately)
    // "sd DEPTH" The engine should limit its thinking to DEPTH ply
    // "time N, otim N" set times for engine and player
    // "usermove MOVE" any move like d2d4
    // "undo" undo the last move(s) relys on "force" perhaps
    // "remove" undo the last two moves(i.e. one for each side) does not rely on force

    // Much less important commands
    // "computer" the other player is also a chess engine
    // "draw" accept a draw
    // "force" not extremely important, make engine stop playing
    // "go" if force is implemented leave force mode
    // "result" get the result of the game(form xboard)

    private static boolean commandIsMove(String command) {
        // This command is a move if it's length is four and the second character
        // is a digit
        return (command.length() == 4 && Character.isDigit(command.charAt(1)));
    }

    private static boolean commandIsTimeConstruct(String command) {
	String temp = (new StringTokenizer(command)).nextToken();
	return (temp.equals("level") || temp.equals("time") || temp.equals("otim"));
    }
    
    private static boolean commandIsSetDepth(String command) {
        return ((new StringTokenizer(command)).nextToken()).equals("sd");
    }

//*************************************************************************
//End of Jason's Code
//*************************************************************************
}// end of public class ChessTest

// This thread creates a JFrame that reports everything written by the
// other thread.
class StatusDisplayThread extends Thread {
        private JTextArea ta;
        private BufferedInputStream in;
        private JScrollBar bar;
        
        public StatusDisplayThread(PipedInputStream snk) {
                JFrame frame = new JFrame("Status");
                ta = new JTextArea("");
                JScrollPane sp = new JScrollPane(ta);
                frame.getContentPane().add(sp);
                bar = sp.getVerticalScrollBar();
                frame.setSize(300, 300);
                frame.setLocation(100, 100);
                frame.setVisible(true);
                ta.setFont(new Font("Courier", Font.PLAIN, 12));
                in = new BufferedInputStream(snk);
        }
        
        public void run() {
                while (true) {
                        try {
                                ta.append(String.valueOf((char)in.read()));
                                ta.setCaretPosition(ta.getText().length());
                        } catch (IOException ex) {
                                ex.printStackTrace();
                        }
                }
        }
}