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 PrintWriter outWriter;       // Public for debug purposes 

    // A variable to keep track of thinking mode (i.e. xboard stats facility)
    private static boolean thinkingOn = false;

    public static void main(String[] args) {
        // Initialize some variables
        board = new Board();
        int maxdepth = 2;     // Depth can be set from command line with "-d DEPTH"
         
        // We make a FileWriter so that we can keep a log of what xboard sent to the
        // engine(this is merely a log and has nothing to do with the funcitonality
	// of the program.)  The following block of code is all fluff.  Please ignore.
        outWriter = new PrintWriter(System.err, true);;
        if (args == null || args.length < 1) {
                try {
                        PrintStream ps = new PrintStream(
                                new FileOutputStream("ChessEngine.log"), true);
                        outWriter = new PrintWriter(ps, true);
                        // Set the standard error to print to the file as well
                        // I can't see an easy way to get it in xboard.
                        System.setErr(new PrintStream(ps));
                } catch (IOException ex) {
                        ex.printStackTrace();
                }
        } else {        // Parse the command line
                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.
                                PipedReader snk = new PipedReader();
                                try {
                                        outWriter = new PrintWriter(new PipedWriter(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 PrintWriter(new FileWriter("/dev/null"),
                                                    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);
                                }
                        }
                }
        }

        s = new Search(maxdepth);
        outWriter.println("Search depth set to " + maxdepth);

	// 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));

	// Variables to reference strings recieved and sent back
	String command = "";
	String returnCommand = "";

	// 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.
	System.out.println("feature done=0");
        
        // 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
            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("Xboard sent: " + command);

	    // Respond to the message
	    returnCommand = evaluateMessage(command, outWriter);
                        
            // Transmit some kind of message back to xboard
	    if (!returnCommand.equals("")) {
		System.out.println(returnCommand);
	    }
        }
    }

    private static String evaluateMessage(String command, PrintWriter outWriter) {
	// 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.");
            StringTokenizer strtok = new StringTokenizer(command);
            strtok.nextToken();    // discard first token
            Move m = new Move(strtok.nextToken());
            outWriter.println("Move is: " + m);
	    // Do something with the move
            board.makeMove(m);
	    // Return a move
	    Move returnMove = s.getMove(board);
            board.movePiece(returnMove);
	    outWriter.println("Returning move " + returnMove + "\n" + board);
	    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("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=1 " +    // 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=\"Java Team 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
	    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) {
        // If this command is a move trim the tag and return true
        return ((new StringTokenizer(command)).nextToken()).equals("usermove");
    }

    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 BufferedReader in;
        private JScrollBar bar;
        
        public StatusDisplayThread(PipedReader 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("Monaco", Font.PLAIN, 12));
                ta.append("Status:\n");
                in = new BufferedReader(snk);
        }
        
        public void run() {
                while (true) {
                        try {
                                ta.append(in.readLine() + "\n");
                                ta.setCaretPosition(ta.getText().length());
                        } catch (IOException ex) {
                                ex.printStackTrace();
                        }
                }
        }
}