/*
 * vim: set ts=8:
 *
 * Copyright (C) 2007 Speecys Corporation
 *		Toshiaki Nozawa <nozawa@speecys.com>
 * All rights reserved.
 *
 */

package	com.speecys.remoConRT;


import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.charset.*;
import java.nio.channels.*;
import java.util.regex.*;
import javax.swing.*;

public class RemoteCtrl
extends WindowAdapter
implements ActionListener
{
    private static final String FN_ATOMIC
	= System.getProperty("user.home")
	+ System.getProperty("file.separator")
	+ "RemoCtrl.Executing";

    private static final File ATM_F = new File(FN_ATOMIC);


    private static final String copyright = "@(#) Copyright (C) 2007 Speecys Corporation		Toshiaki Nozawa		All rights reserved.";

    // Charset and encoder for US-ASCII
    private static Charset charset = Charset.forName("US-ASCII");
    static CharsetEncoder Encoder = charset.newEncoder();

    // default port
    private static final int REMOCON_DEFAULT_PORT = 5001;

    // socket
    private String host;
    private int port = REMOCON_DEFAULT_PORT;
    private SocketChannel sc = null;

    // Direct byte buffer for reading
    private ByteBuffer buf = ByteBuffer.allocateDirect(1024);

    private final JFrame	frame;

    private final static String[] bConLbl = {"connect", "disconnect"};
    private final JButton bCon;

    enum MotionID {
	Zero
	, Forward, Back, Right, Left, Jump
	, scenArio
	, Mail, Tanaka
	, neWsTopics, cNn
	, appleDance
	, Introduction
	, scHedule
	, Ecmd, Gcmd, Kcmd, Ocmd
	, Scmd, Ucmd, Vcmd, Xcmd
    };

    /**
     *
     */
    private RemoteCtrl(){
	JFrame.setDefaultLookAndFeelDecorated(true);

	frame = new JFrame("MIRAI-RT Remote Controler");
	frame.addWindowListener(this);
	frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	Container frCon = frame.getContentPane();

	JPanel basePnl = new JPanel(new BorderLayout(20, 20));

	JPanel movPnl = new JPanel(new BorderLayout(5, 5));
	JPanel optPnl = new JPanel(new GridLayout(0, 1));

	basePnl.add(movPnl, BorderLayout.CENTER);
	basePnl.add(optPnl, BorderLayout.SOUTH);
	frCon.add(basePnl);

	bCon = new JButton(bConLbl[0]);
	bCon.setMnemonic(KeyEvent.VK_C);
	bCon.addActionListener(this);
	basePnl.add(bCon, BorderLayout.NORTH);

	for (MotionID mid : MotionID.values()) {
	    if (mid.equals(MotionID.Zero)) {
		optPnl.add(new MotionBtn(mid));
	    } else if (mid.compareTo(MotionID.Jump) <= 0){
		String[] bname = {
		    BorderLayout.NORTH
		    , BorderLayout.SOUTH
		    , BorderLayout.EAST
		    , BorderLayout.WEST
		    , BorderLayout.CENTER
		};

		movPnl.add(new MotionBtn(mid)
		    , bname[mid.ordinal() - MotionID.Forward.ordinal()]);

	    } else {
		optPnl.add(new MotionBtn(mid));

		if (mid.equals(MotionID.scHedule)) {
		    optPnl.add(new JSeparator());
		}
	    }
	}

	frame.pack();
	frame.setVisible(true);
    }

    /**
     *
     */
    private RemoteCtrl(String host){
	this();
	this.host = host;
    };

    /**
     *
     */
    private RemoteCtrl(String host, int port){
	this(host);
	this.port = port;
    }

    /**
     * for ActionListener
     */
    public void  actionPerformed(ActionEvent e){
	System.out.println("connect");
	try {
	    connect();
	} catch (IOException ex) {
	    ex.printStackTrace();
	}
    }

    /**
     * WindowAdapter override
     */
    public void windowClosed(WindowEvent e)
    {
	ATM_F.delete();
	super.windowClosed(e);
	System.exit(0);
    }

    /**
     *
     */
    private void connect() throws IOException {
	if (sc == null || sc.isOpen() == false){
	    try {
		InetSocketAddress isa
		    = new InetSocketAddress(InetAddress.getByName(host), port);

		// Connect
		sc = SocketChannel.open();
		sc.connect(isa);
		bCon.setText(bConLbl[1]);
	    } catch (IOException e) {
		// Make sure we close the channel (and hence the socket)
		if (sc != null) {
		    sc.close();
		}

		throw e;
	    }
	} else {
	    // disconnect
	    sc.close();
	    bCon.setText(bConLbl[0]);
	}
    }

    /**
     *
     */
    public static void main(String[] args)
    throws Exception
    {
	RemoteCtrl rc;

	if (ATM_F.createNewFile()) {

	    if (args.length < 1) {
		System.err.println("Usage: java RemoteCtrl host[:port]");
		return;
	    }

	    String[] sp = args[0].split(":");

	    // perse port no.
	    if (sp.length >= 2 && Pattern.matches("[0-9]+", sp[1])) {
		rc = new RemoteCtrl(sp[0], Integer.parseInt(sp[1]));
	    } else {
		rc = new RemoteCtrl(sp[0]);
	    }

	    try {
		rc.connect();
	    } catch (IOException ex) {
		ex.printStackTrace();
	    }

	    ATM_F.deleteOnExit();
	} else {
	    System.err.println("Existing file that named \"" + FN_ATOMIC + "\"");
	    System.err.println("Make sure to delete \"" + FN_ATOMIC + "\", and retry.");

	    synchronized (ATM_F) {
		ATM_F.wait(10 * 1000);	// sleep 10 sec
	    }
	}
    }

    private class MotionBtn
    extends JButton
    implements ActionListener
    {
	private SubCmdPacket subcmd;
	MotionBtn(MotionID mid)
	{
	    super(mid.toString() + "  (" + mid.ordinal() + ")");

	    String lstr = mid.toString();
	    String[] sp = lstr.split("[A-Z]");
	    setMnemonic(lstr.codePointAt(lstr.indexOf(sp[1]) - 1));

	    subcmd = SubCmdPacket.getMotionPacket((mid.equals(MotionID.Zero) ? 201 : mid.ordinal()));
	    addActionListener(this);
	}

	public void  actionPerformed(ActionEvent e){
	    try {
		subcmd.writeTo(sc);
	    } catch (java.io.IOException x) {
		x.printStackTrace();
	    }
	}
    }
}
