package mrc;

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import java.net.*;
import java.util.StringTokenizer;

public class Cliente implements Runnable {
    // Connect status constants

    public final static int NULL = 0;
    public final static int DISCONNECTED = 1;
    public final static int DISCONNECTING = 2;
    public final static int BEGIN_CONNECT = 3;
    public final static int CONNECTED = 4;
    // Other constants
    public final static String statusMessages[] = {
        " Error, no pude conectarme!",
        " Desconectado",
        " Desconectándome...",
        " Conectándome...",
        " Conectado"
    };
    public final static Cliente instanciaDeCliente = new Cliente();
    public final static String END_CHAT_SESSION = "(disconnecting)";
    // Connection atate info
    public static String hostIP = "localhost";
    public static String nick = "yo";
    public static int port = 54321;
    public static int connectionStatus = DISCONNECTED;
    public static String statusString = statusMessages[connectionStatus];
    public static StringBuffer toAppend = new StringBuffer("");
    public static StringBuffer toSend = new StringBuffer("");
    // Various GUI components and info
    public static JFrame mainFrame = null;
    public static JTextArea chatText = null;
    public static JTextField chatLine = null;
    public static JPanel statusBar = null;
    public static JLabel statusField = null;
    public static JTextField statusColor = null;
    public static JTextField ipField = null;
    public static JTextField nickField = null;
    public static JTextField portField = null;
    public static JButton connectButton = null;
    public static JButton disconnectButton = null;
    public static JTextField imagePathnameField = null;
    public static JCheckBox shareCheckBox = null;
    public static JLabel pictureLabel = null;
    // TCP Components
    public static ServerSocket hostServer = null;
    public static Socket socket = null;
    public static BufferedReader in = null;
    public static PrintWriter out = null;

    private static JPanel initOptionsPane() {
        final String HOST_TOOLTIP_TEXT = "Dirección o nombre del servidor (IRC)²";
        final String PORT_TOOLTIP_TEXT = "Puerto de atención del servidor (IRC)²";
        final String NICK_TOOLTIP_TEXT = "Su apodo durante la sesión (todos los mensajes estarán firmados así)";
        JPanel pane = null;
        ActionAdapter buttonListener = null;

        // Create an options pane
        JPanel optionsPane = new JPanel(new GridLayout(4, 1));

        // IP address input
        pane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        JLabel hostLabel = new JLabel("Host IP:");
        hostLabel.setToolTipText(HOST_TOOLTIP_TEXT);
        pane.add(hostLabel);
        ipField = new JTextField(10);
        ipField.setToolTipText(HOST_TOOLTIP_TEXT);
        ipField.setText(hostIP);
        ipField.setEnabled(true);
        ipField.addFocusListener(new FocusAdapter() {

            public void focusLost(FocusEvent e) {
                ipField.selectAll();
                if (connectionStatus != DISCONNECTED) {
                    changeStatusNTS(NULL, true);
                } else {
                    hostIP = ipField.getText();
                }
            }
        });
        pane.add(ipField);
        optionsPane.add(pane);

        // Port input
        pane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        JLabel portLabel = new JLabel("Port:");
        portLabel.setToolTipText(PORT_TOOLTIP_TEXT);
        pane.add(portLabel);
        portField = new JTextField(10);
        portField.setEditable(true);
        portField.setToolTipText(PORT_TOOLTIP_TEXT);
        portField.setText((new Integer(port)).toString());
        portField.addFocusListener(new FocusAdapter() {

            public void focusLost(FocusEvent e) {
                // should be editable only when disconnected
                if (connectionStatus != DISCONNECTED) {
                    changeStatusNTS(NULL, true);
                } else {
                    int temp;
                    try {
                        temp = Integer.parseInt(portField.getText());
                        port = temp;
                    } catch (NumberFormatException nfe) {
                        portField.setText((new Integer(port)).toString());
                        mainFrame.repaint();
                    }
                }
            }
        });
        pane.add(portField);
        optionsPane.add(pane);

        // Nickname input
        pane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        JLabel nickLabel = new JLabel("Apodo:");
        nickLabel.setToolTipText(NICK_TOOLTIP_TEXT);
        pane.add(nickLabel);
        nickField = new JTextField(10);
        nickField.setText(nick);
        nickField.setToolTipText(NICK_TOOLTIP_TEXT);
        nickField.setEditable(true);
        pane.add(nickField);
        optionsPane.add(pane);

        // Connect/disconnect buttons
        JPanel buttonPane = new JPanel(new GridLayout(1, 2));
        buttonListener = new ActionAdapter() {

            public void actionPerformed(ActionEvent e) {
                // Request a connection initiation
                if (e.getActionCommand().equals("connect")) {
                    //TODO: ajustar el estado al proceso de conexión
                    // changeStatusNTS(BEGIN_CONNECT, true); <- correcto!!
                } // Disconnect
                else {
                    //TODO: ajustar el estado al proceso de conexión
                    //changeStatusNTS(DISCONNECTING, true);<- correcto!!
                }
            }
        };
        connectButton = new JButton("Connect");
        connectButton.setMnemonic(KeyEvent.VK_C);
        connectButton.setActionCommand("connect");
        connectButton.addActionListener(buttonListener);
        connectButton.setEnabled(true);
        disconnectButton = new JButton("Disconnect");
        disconnectButton.setMnemonic(KeyEvent.VK_D);
        disconnectButton.setActionCommand("disconnect");
        disconnectButton.addActionListener(buttonListener);
        disconnectButton.setEnabled(false);
        buttonPane.add(connectButton);
        buttonPane.add(disconnectButton);
        optionsPane.add(buttonPane);

        return optionsPane;
    }

    private static JPanel initChatPanel() {
        JPanel chatPane = new JPanel(new BorderLayout());
        chatText = new JTextArea(10, 20);
        chatText.setLineWrap(true);
        chatText.setEditable(false);
        chatText.setForeground(Color.blue);
        JScrollPane chatTextPane = new JScrollPane(chatText,
                JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        chatLine = new JTextField();
        chatLine.setEnabled(false);
        chatLine.addActionListener(new ActionAdapter() {

            public void actionPerformed(ActionEvent e) {
                String s = chatLine.getText();
                if (!s.equals("")) {
                    sendString(s);
                    chatLine.setText("");
                }
            }
        });
        chatPane.add(chatLine, BorderLayout.SOUTH);
        chatPane.add(chatTextPane, BorderLayout.CENTER);
        chatPane.setPreferredSize(new Dimension(200, 200));
        return chatPane;
    }

    private static JPanel initImagePanel() {
        final String SHARE_TOOLTIP_TEXT = "Compartir su foto con otros usuarios";
        final String PATHNAME_TOOLTIP_TEXT = "Ruta al archivo con la imagen a compartir";

        JPanel iP = new JPanel(new BorderLayout());
        //imagePathnameField.set

        JPanel imageSubPane = new JPanel(new GridLayout(2, 1));
        JPanel pane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        JLabel shareLabel = new JLabel("Compartir");
        shareLabel.setToolTipText(SHARE_TOOLTIP_TEXT);
        pane.add(shareLabel);
        shareCheckBox = new JCheckBox();
        shareCheckBox.setEnabled(true);
        shareCheckBox.setToolTipText(SHARE_TOOLTIP_TEXT);
        pane.add(shareCheckBox);
        imageSubPane.add(pane);
        pane = new JPanel(new FlowLayout(FlowLayout.LEFT));
        imagePathnameField = new JTextField(10);
        imagePathnameField.setEditable(true);
        imagePathnameField.setToolTipText(PATHNAME_TOOLTIP_TEXT);

        pane.add(imagePathnameField);
        imageSubPane.add(pane);

        //ImageIcon imageIcon = new ImageIcon("/tmp/pic12902.jpg");
        ImageIcon imageIcon = new ImageIcon();

        //JPanel imageIconPanel = new JPanel();
        pictureLabel = new JLabel();
        pictureLabel.setText("imagen");

        iP.add(pictureLabel, BorderLayout.NORTH);
        iP.add(imageSubPane, BorderLayout.SOUTH);
        return iP;
    }

    public static void actualizoIMG(byte[] img) {
        ImageIcon ii = new ImageIcon(img);
        pictureLabel.setIcon(ii);
        pictureLabel.setText("");
    }

    private static void inicializoGUI() {
        // Set up the status bar
        statusField = new JLabel();
        statusField.setText(statusMessages[DISCONNECTED]);
        statusColor = new JTextField(1);
        statusColor.setBackground(Color.red);
        statusColor.setEditable(false);
        statusBar = new JPanel(new BorderLayout());
        statusBar.add(statusColor, BorderLayout.WEST);
        statusBar.add(statusField, BorderLayout.CENTER);

        JPanel optionsPane = initOptionsPane();

        JPanel chatPane = initChatPanel();

        JPanel imagePane = initImagePanel();

        // Set up the main pane
        JPanel mainPane = new JPanel(new BorderLayout());
        mainPane.add(statusBar, BorderLayout.SOUTH);
        mainPane.add(optionsPane, BorderLayout.WEST);
        mainPane.add(imagePane, BorderLayout.EAST);
        mainPane.add(chatPane, BorderLayout.CENTER);

        // Set up the main frame
        mainFrame = new JFrame("(IRC)² Client - Redes 2010");
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainFrame.setContentPane(mainPane);
        mainFrame.setSize(mainFrame.getPreferredSize());
        mainFrame.setLocation(200, 200);
        mainFrame.pack();
        mainFrame.setVisible(true);
    }

    private static void changeStatusTS(int newConnectStatus, boolean noError) {
        if (newConnectStatus != NULL) {
            connectionStatus = newConnectStatus;
        }

        if (noError) {
            statusString = statusMessages[connectionStatus];
        } else {
            statusString = statusMessages[NULL];
        }

        SwingUtilities.invokeLater(instanciaDeCliente);
    }

    private static void changeStatusNTS(int newConnectStatus, boolean noError) {
        if (newConnectStatus != NULL) {
            connectionStatus = newConnectStatus;
        }

        if (noError) {
            statusString = statusMessages[connectionStatus];
        } else {
            statusString = statusMessages[NULL];
        }

        instanciaDeCliente.run();
    }

    private static void appendToChatBox(String s) {
        synchronized (toAppend) {
            toAppend.append(s);
        }
    }

    private static void sendString(String s) {
        synchronized (toSend) {
            toSend.append(s + "\n");
        }
    }

    private static void cleanUp() {
        //TODO: implementar la limpieza de los componentes de red en caso de desconexión
    }

    public void run() {
        switch (connectionStatus) {
            case DISCONNECTED:
                connectButton.setEnabled(true);
                disconnectButton.setEnabled(false);
                ipField.setEnabled(true);
                portField.setEnabled(true);
                nickField.setEnabled(true);
                chatLine.setText("");
                chatLine.setEnabled(false);
                statusColor.setBackground(Color.red);
                break;

            case DISCONNECTING:
                connectButton.setEnabled(false);
                disconnectButton.setEnabled(false);
                ipField.setEnabled(false);
                portField.setEnabled(false);
                nickField.setEnabled(false);
                chatLine.setEnabled(false);
                statusColor.setBackground(Color.orange);
                break;

            case CONNECTED:
                connectButton.setEnabled(false);
                disconnectButton.setEnabled(true);
                ipField.setEnabled(false);
                portField.setEnabled(false);
                nickField.setEnabled(false);
                chatLine.setEnabled(true);
                statusColor.setBackground(Color.green);
                break;

            case BEGIN_CONNECT:
                connectButton.setEnabled(false);
                disconnectButton.setEnabled(false);
                ipField.setEnabled(false);
                portField.setEnabled(false);
                nickField.setEnabled(false);
                chatLine.setEnabled(false);
                chatLine.grabFocus();
                statusColor.setBackground(Color.orange);
                break;
        }

        // Make sure that the button/text field states are consistent
        // with the internal states
        ipField.setText(hostIP);
        portField.setText((new Integer(port)).toString());
        statusField.setText(statusString);
        chatText.append(toAppend.toString());
        toAppend.setLength(0);

        mainFrame.repaint();
    }

    public static void main(String args[]) {
        String s;

        inicializoGUI();

        ImageServer is = new ImageServer(imagePathnameField, shareCheckBox);
        new Thread(is).start();

        while (true) {
            try { // Poll every ~10 ms
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }

            switch (connectionStatus) {
                case BEGIN_CONNECT:
//TODO: iniciar la conexión
//                    try {
//                        changeStatusTS(CONNECTED, true);
//                    } catch (IOException e) {
//                        cleanUp();
//                        changeStatusTS(DISCONNECTED, false);
//                    }
                    break;

                case CONNECTED:
//                    try {
                        if (toSend.length() != 0) {
                            if (!shareCheckBox.isSelected()) {
//TODO: enviar el mensaje, codificando que no se comparte la imagen
                            } else {
//TODO: enviar el mensaje, codificando que se comparte la imagen
                            }
//TODO: actualizar el estado                            changeStatusTS(NULL, true);
                        }

//                        if (in.ready()) {
//TODO: de forma NO BLOQUEANTE, recibir lo que haya en el buffer y agregarlo al texto.
//TODO: además, si el mensaje indica que el emisor comparte su imagen, lanzar un
//TODO: ImageClient que procese la recepción de la imagen.
  //                      }
// Ante una falla, me desconecto
//            } catch (IOException e) {
//                        cleanUp();
//                        changeStatusTS(DISCONNECTED, false);
//                    }
                    break;

                case DISCONNECTING:
                    cleanUp();
                    changeStatusTS(DISCONNECTED, true);
                    break;

                default:
                    break; // do nothing
            }
        }
    }
}

class ActionAdapter implements ActionListener {

    public void actionPerformed(ActionEvent e) {
    }
}

class ImageClient implements Runnable {

    String server;
    String port;
    JLabel pictureLabel;

    ImageClient(String server, String port, JLabel pictureLabel) {
        this.server = server;
        this.port = port;
        this.pictureLabel = pictureLabel;
    }

    public void run() {

//TODO: implementar un proceso de recepción que reciba una imagen de largo desconocido.
// Cuando se corta la conexión se considera el fin del mensaje y los bytes recibidos son
// utilizados para invocar :
//            Cliente.actualizoIMG(img);
    }
}

class ImageServer implements Runnable {

    JTextField imagePathnameField;
    JCheckBox shareCheckBox;
    private int port;

    ImageServer(JTextField imagePathnameField, JCheckBox shareCheckBox) {
        this.imagePathnameField = imagePathnameField;
        this.shareCheckBox = shareCheckBox;
        this.port = 0;
    }

    int getPort() {
        return this.port;
    }

    class ImageFeeder implements Runnable {

        private Socket s;
        private String pathname;
        private boolean enabled;

        ImageFeeder(Socket s, String pathname, boolean enabled) {
            this.s = s;
            this.pathname = pathname;
            this.enabled = enabled;
        }

        public void run() {
//TODO: Implementar el thread de atención del servidor de imagenes. Si el combo "compartir" no está checked, apenas se recibe
// una conexión nueva, se la cierra.
// Si el combo está checked, se abre el archivo indicado por el pathname, se lee y transmite "como viene"
// por el socket. Cuando se llega al fin del archivo, se cierra el socket.
        }
    }

    public void run() {
        ServerSocket serverSocket = null;
//        try {
//            serverSocket = new ServerSocket();
//            serverSocket.bind(null);
//TODO: Implementar el thread que recibe  nuevas conexiones y despacha los threads de atención.
//            while (true) {
//                Abro un socket s
//                ImageFeeder imf = new ImageFeeder(s, this.imagePathnameField.getText(), this.shareCheckBox.isSelected());
//                new Thread(imf).start();
//            }

    }
}
