/*
 * MegaMek - Copyright (C) 2003,2004 Ben Mazur (bmazur@sev.org)
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2 of the License, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 *  for more details.
 */

package megamek.test;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.Socket;
import java.net.ServerSocket;

import megamek.common.Board;
import megamek.common.net.Connection;
import megamek.common.net.ConnectionFactory;
import megamek.common.net.ConnectionListenerAdapter;
import megamek.common.net.DisconnectedEvent;
import megamek.common.net.Packet;
import megamek.common.net.PacketReceivedEvent;

/**
 * This class provides an AWT GUI for testing the transmission
 * and reception of <code>Packet</code>s.
 *
 * @author      James Damour <suvarov454@users.sourceforge.net>
 */
public class PacketTool extends Frame implements Runnable {

    /**
     * The currently-loaded <code>Board</code>.  May be <code>null</code>.
     */
    private Board board = null;

    /**
     * The panel containing the connection controls.
     */
    private Panel panConnect = null;

    /**
     * The panel containing the transimission controls.
     */
    private Panel panXmit = null;

    /**
     * The text control where the host name is entered.
     */
    private TextField hostName = null;

    /**
     * The text control where the host port is entered.
     */
    private TextField hostPort = null;

    /**
     * The label where the board name is displayed.
     */
    private Label boardName = null;

    /**
     * The name of the most-recently-created board file.
     * May be <code>null</code>.
     */
    private String fileName = null;

    /**
     * The button that sends the loaded board.
     */
    private Button butSend = null;

    /**
     * The connection to the other peer.
     */
    private Connection conn = null;

    /**
     * Display a window for testing the transmission of boards.
     */
    public static void main( String[] args ) {
        Frame frame = new PacketTool();

        // set visible on middle of screen
        Dimension screenSize = frame.getToolkit().getScreenSize();
        frame.pack();
        frame.setLocation(
            screenSize.width / 2 - frame.getSize().width / 2,
            screenSize.height / 2 - frame.getSize().height / 2);

        // Show the window.
        frame.setVisible(true);

    }

    /**
     * Create a window for testing the transmission of boards.
     * The window will <em>not</em> be displayed by this constructor;
     * calling functions should call <code>setVisible(true)</code>.
     */
    public PacketTool() {
        super( "Board Transmition" );
        Button button = null;
        Panel main = null;

        // Handle the frame stuff.
        this.addWindowListener( new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    quit();
                }
            } );

        // Create the panels.
        main = new Panel();
        main.setLayout( new GridLayout(0,1) );
        panConnect = new Panel();
        panConnect.setLayout( new GridLayout(0,2) );
        main.add( panConnect );
        panXmit = new Panel();
        panXmit.setLayout( new GridLayout(0,1) );
        panXmit.setEnabled( false );
        main.add( panXmit );
        this.add( main );

        // Populate the connection panel.
        panConnect.add( new Label(" Connect To:") );
        hostName = new TextField( "localhost", 10 );
        panConnect.add( hostName );
        panConnect.add( new Label("Port Number:") );
        hostPort = new TextField( "2346", 10 );
        panConnect.add( hostPort );
        button = new Button( "Listen" );
        button.addActionListener( new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    (new Thread(PacketTool.this, "Packet Reader")).start();
                }
            } );
        panConnect.add( button );
        button = new Button( "Connect" );
        button.addActionListener( new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    connect();
                }
            } );
        panConnect.add( button );

        // Populate the transmission panel.
        button = new Button( "Load Board" );
        button.addActionListener( new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    boardLoad();
                }
            } );
        panXmit.add( button );
        boardName = new Label();
        boardName.setAlignment( Label.CENTER );
        panXmit.add( boardName );
        butSend = new Button( "Send" );
        butSend.addActionListener( new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    send();
                }
            } );
        butSend.setEnabled( false );
        panXmit.add( butSend );

    }

    /**
     * Close this window.
     */
    public synchronized void quit() {
        if ( null != this.conn ) {
            conn.close();
        }
        System.exit(0);
    }

    /**
     * Connect to the specified host.
     */
    public void connect() {
        String host = hostName.getText();
        int port = 0;
        try {
            port = Integer.parseInt( hostPort.getText() );
            conn = ConnectionFactory.getInstance().createServerConnection(new Socket(host, port), 1 );
            //conn = new XmlConnection( this, new Socket(host, port), 1 );            
            System.out.println( "Connected to peer." );
            conn.addConnectionListener(connectionListener);

            board = new Board();
            panConnect.setEnabled( false );
            panXmit.setEnabled( true );
        }
        catch ( Throwable err ) {
            err.printStackTrace();
        }
    }

    /**
     * Load a board from a file.
     */
    public void boardLoad() {
        FileDialog fd = new FileDialog(this, "Load Board...", FileDialog.LOAD);
        fd.setDirectory("data" + File.separator + "boards");
        if ( boardName.getText().length() > 0 ) {
            fd.setFile( boardName.getText() );
        }
        fd.setLocation(this.getLocation().x + 150, this.getLocation().y + 100);
        fd.setVisible(true);
        
        if(fd.getFile() == null) {
            // I want a file, y'know!
            return;
        }
        String curpath = fd.getDirectory();
        String curfile = fd.getFile();
        // load!
        try {
            InputStream is = new FileInputStream(new File(curpath, curfile));
            board.load(is);
            // okay, done!
            is.close();

            // Record the file's name.
            boardName.setText( curfile );

            // Enable the send button.
            butSend.setEnabled( true );
        } catch(IOException ex) {
            System.err.println("error opening file to save!");
            System.err.println(ex);
        }
    }

    /**
     * Send the loaded board to the peer.
     */    
    public void send() {
        long start = conn.bytesSent();
        Packet packet = new Packet(Packet.COMMAND_SENDING_BOARD, board);
        /* 2003-12-21 : prove connectivity first, then add refinements 
        **              like data compression.
        packet.zipData();
        */
        conn.send( packet );
        System.out.print( "Bytes sent: " );
        System.out.print( conn.bytesSent() - start );
        System.out.print( ", total sent: ");
        System.out.println( conn.bytesSent() );
    }

    /**
     * Listen for incoming clients.
     */
    public void run() {
        int port = 0;
        try {
            port = Integer.parseInt( hostPort.getText() );
            ServerSocket serverSocket = new ServerSocket( port );
            Socket s = serverSocket.accept();
            serverSocket.close();
                
            System.out.println("Accepted peer connection.");
                
            conn = ConnectionFactory.getInstance().createServerConnection(s, 0);
            //conn = new XmlConnection(this, s, 0);
            conn.addConnectionListener(connectionListener);
            
            board = new Board();
            panConnect.setEnabled( false );
            panXmit.setEnabled( true );

        } catch( Throwable err ) {
            err.printStackTrace();
        }
    }

    /**
     * Process a packet from a connection.
     * <p/>
     * Implements <code>ConnectionHandler</code>.
     *
     * @param   id - the <code>int</code> ID the connection that
     *          received the packet.
     * @param   packet - the <code>Packet</code> to be processed.
     */
    public synchronized void handle( int id, Packet packet ) {
        System.out.print( "Connection #" );
        System.out.print( id );
        System.out.print( ", received a " );
        if ( null == packet ) {
            System.out.print( "null" );
        } else {
            switch ( packet.getCommand() ) {
            case Packet.COMMAND_SERVER_GREETING:
                System.out.print( "COMMAND_SERVER_GREETING" ); break;
            case Packet.COMMAND_CLIENT_NAME:
                System.out.print( "COMMAND_CLIENT_NAME" ); break;
            case Packet.COMMAND_LOCAL_PN:
                System.out.print( "COMMAND_LOCAL_PN" ); break;
            case Packet.COMMAND_PLAYER_ADD:
                System.out.print( "COMMAND_PLAYER_ADD" ); break;
            case Packet.COMMAND_PLAYER_REMOVE:
                System.out.print( "COMMAND_PLAYER_REMOVE" ); break;
            case Packet.COMMAND_PLAYER_UPDATE:
                System.out.print( "COMMAND_PLAYER_UPDATE" ); break;
            case Packet.COMMAND_PLAYER_READY:
                System.out.print( "COMMAND_PLAYER_READY" ); break;
            case Packet.COMMAND_CHAT:
                System.out.print( "COMMAND_CHAT" ); break;
            case Packet.COMMAND_ENTITY_ADD:
                System.out.print( "COMMAND_ENTITY_ADD" ); break;
            case Packet.COMMAND_ENTITY_REMOVE:
                System.out.print( "COMMAND_ENTITY_REMOVE" ); break;
            case Packet.COMMAND_ENTITY_MOVE:
                System.out.print( "COMMAND_ENTITY_MOVE" ); break;
            case Packet.COMMAND_ENTITY_DEPLOY:
                System.out.print( "COMMAND_ENTITY_DEPLOY" ); break;
            case Packet.COMMAND_ENTITY_ATTACK:
                System.out.print( "COMMAND_ENTITY_ATTACK" ); break;
            case Packet.COMMAND_ENTITY_UPDATE:
                System.out.print( "COMMAND_ENTITY_UPDATE" ); break;
            case Packet.COMMAND_ENTITY_MODECHANGE:
                System.out.print( "COMMAND_ENTITY_MODECHANGE" ); break;
            case Packet.COMMAND_ENTITY_AMMOCHANGE:
                System.out.print( "COMMAND_ENTITY_AMMOCHANGE" ); break;
            case Packet.COMMAND_ENTITY_VISIBILITY_INDICATOR:
                System.out.print( "COMMAND_ENTITY_VISIBILITY_INDICATOR" ); break;
            case Packet.COMMAND_CHANGE_HEX:
                System.out.print( "COMMAND_CHANGE_HEX" ); break;
            case Packet.COMMAND_BLDG_ADD:
                System.out.print( "COMMAND_BLDG_ADD" ); break;
            case Packet.COMMAND_BLDG_REMOVE:
                System.out.print( "COMMAND_BLDG_REMOVE" ); break;
            case Packet.COMMAND_BLDG_UPDATE_CF:
                System.out.print( "COMMAND_BLDG_UPDATE_CF" ); break;
            case Packet.COMMAND_BLDG_COLLAPSE:
                System.out.print( "COMMAND_BLDG_COLLAPSE" ); break;
            case Packet.COMMAND_PHASE_CHANGE:
                System.out.print( "COMMAND_PHASE_CHANGE" ); break;
            case Packet.COMMAND_TURN:
                System.out.print( "COMMAND_TURN" ); break;
            case Packet.COMMAND_ROUND_UPDATE:
                System.out.print( "COMMAND_ROUND_UPDATE" ); break;
            case Packet.COMMAND_SENDING_BOARD:
                System.out.print( "COMMAND_SENDING_BOARD" );
                /*
                ** Save the board here.
                */
                Board recvBoard = (Board) packet.getObject(0);
                try {
                    recvBoard.save( new FileOutputStream("xmit.board") );
                } catch ( IOException ioErr ) {
                    ioErr.printStackTrace();
                }
                break;
            case Packet.COMMAND_SENDING_ENTITIES:
                System.out.print( "COMMAND_SENDING_ENTITIES" ); break;
            case Packet.COMMAND_SENDING_PLAYERS:
                System.out.print( "COMMAND_SENDING_PLAYERS" ); break;
            case Packet.COMMAND_SENDING_TURNS:
                System.out.print( "COMMAND_SENDING_TURNS" ); break;
            case Packet.COMMAND_SENDING_REPORTS:
                System.out.print( "COMMAND_SENDING_REPORTS" ); break;
            case Packet.COMMAND_SENDING_GAME_SETTINGS:
                System.out.print( "COMMAND_SENDING_GAME_SETTINGS" ); break;
            case Packet.COMMAND_SENDING_MAP_SETTINGS:
                System.out.print( "COMMAND_SENDING_MAP_SETTINGS" ); break;
            case Packet.COMMAND_QUERY_MAP_SETTINGS:
                System.out.print( "COMMAND_QUERY_MAP_SETTINGS" ); break;
            case Packet.COMMAND_END_OF_GAME:
                System.out.print( "COMMAND_END_OF_GAME" ); break;
            case Packet.COMMAND_DEPLOY_MINEFIELDS:
                System.out.print( "COMMAND_DEPLOY_MINEFIELDS" ); break;
            case Packet.COMMAND_REVEAL_MINEFIELD:
                System.out.print( "COMMAND_REVEAL_MINEFIELD" ); break;
            case Packet.COMMAND_REMOVE_MINEFIELD:
                System.out.print( "COMMAND_REMOVE_MINEFIELD" ); break;
            case Packet.COMMAND_SENDING_MINEFIELDS:
                System.out.print( "COMMAND_SENDING_MINEFIELDS" ); break;
            case Packet.COMMAND_REROLL_INITIATIVE:
                System.out.print( "COMMAND_REROLL_INITIATIVE" ); break;
            case Packet.COMMAND_SET_ARTYAUTOHITHEXES:
                System.out.print( "COMMAND_SET_ARTYAUTOHITHEXES" ); break;
            default:
                System.out.print( "unknown" ); break;
            }
        }
        System.out.println( " packet." );
    }

    /**
     * Called when it is sensed that a connection has terminated.
     * <p/>
     * Implements <code>ConnectionHandler</code>.
     *
     * @param   conn - the <code>Connection</code> that has terminated.
     */ 
    public synchronized void disconnected( Connection conn ) {
        // write something in the log
        System.out.println("s: connection " + conn.getId() + " disconnected");
        
        // kill the connection and remove it from any lists it might be on
        panXmit.setEnabled( false );
        butSend.setEnabled( false );
        boardName.setText( "" );
        board = null;
        conn = null;
        panConnect.setEnabled( true );
    }

    private ConnectionListenerAdapter connectionListener = new ConnectionListenerAdapter() {

        /**
         * Called when it is sensed that a connection has terminated.
         */
        public void disconnected(DisconnectedEvent e) {
                PacketTool.this.disconnected(e.getConnection());
        }

        public void packetReceived(PacketReceivedEvent e) {
            PacketTool.this.handle(e.getConnection().getId(),e.getPacket());
        }

    };

}
