package sivantoledo.nxt;

import java.util.prefs.Preferences;

import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

import sivantoledo.nxt.keyboard.Keyboard;
import sivantoledo.win32.SendInput;

public class OldNXTender implements Sender {
  
  public  final static int    versionMajor = 1;
  public  final static int    versionMinor = 1;
  private final static String shellTitle   = "NXTender: Extend Your NXT";
  
  public static abstract class Plugin {
    private static java.util.Map<String,Plugin> plugins = new java.util.TreeMap<String,Plugin>();
    public Plugin(String name) {
      plugins.put(name,this);
    }
    
    //public abstract static Plugin getInstance(Sender sender);
    public abstract void process(String command);
    public abstract void close();
  }

  /*
   * The implementation of Sender
   */

  public void send(int mailbox, String s) {
    channel_lock.lock();
    Message.sendMessage(c[0], mailbox, s);
    channel_lock.unlock();
  }
  
  public void send(int mailbox, int i) {
    channel_lock.lock();
    Message.sendMessage(c[0], mailbox, i);
    channel_lock.unlock();
  }

  public void send(int mailbox, boolean b) {
    channel_lock.lock();
    Message.sendMessage(c[0], mailbox, b);
    channel_lock.unlock();
  }
  
  public void flushQueue(int mailbox) {
    channel_lock.lock();
    c[0].flushQueue(mailbox);
    channel_lock.unlock();    
  }

  public void flushAllQueues() {
    channel_lock.lock();
    c[0].flushAllQueues();
    channel_lock.unlock();    
  }

  /*
   * Constructor.
   * This is a singlton.
   */
  
  private OldNXTender() {
  }

  private static OldNXTender instance;  
  public static OldNXTender getInstance() {
    if (instance == null) instance = new OldNXTender();
    return instance;
  }
  
  /*
   * Execusion.
   */

  public static void main(String[] arguments) {
    OldNXTender.getInstance().exec();
  }

  private final CommunicationInterface[] c = new CommunicationInterface[1];
  private final java.util.concurrent.locks.Lock channel_lock = 
    new java.util.concurrent.locks.ReentrantLock();

  public void exec() {
    
    java.io.File   plugin_dir = new java.io.File("plugins");
    java.io.File[] plugin_jars = plugin_dir.listFiles(new java.io.FilenameFilter() {
      public boolean accept(java.io.File file, String name) { return name.endsWith(".jar"); }
    });
    //String[] plugin_jars = { "sivantoledo.nxt.keyboard.Keyboard.jar" };
    for (java.io.File plugin_jar: plugin_jars) {
      String jar_name = plugin_jar.getName();
      String class_name = jar_name.substring(0, jar_name.lastIndexOf('.'));
      //System.out.println(class_name);
      
      try {
        java.lang.reflect.Method getInstance = Class.forName(class_name).getMethod("getInstance", new Class[] { Sender.class });
        getInstance.invoke(null, new Object[] { this });
      } catch (Exception e) {
        System.out.println("Failed to load plugin "+class_name+": "+e.getMessage());
      }
    }
    
    System.out.println("Loaded plugins:");
    for (String key: Plugin.plugins.keySet()) {
      System.out.println("  "+key);
    }

    //= new CommunicationChannel("//./COM14");
    
    
    //Keyboard.getInstance(this);
    //Joystick.getInstance(this);
    //Radar.getInstance(this);
    
    final CommunicationInterface.Listener l = new CommunicationInterface.Listener() {
      public void process(byte[] buffer, int length) {
        if (buffer[1] == 0x09 // message
            && 
            buffer[2] == 0x00 // to mailbox 0
           ) {
          String s = Message.parseStringMessage(buffer);
          Log.println("string: <"+s+">");
          
          String plugin_name;
          int end_of_plugin_name = s.indexOf(' ');
          if (end_of_plugin_name != -1) {
            plugin_name = s.substring(0, end_of_plugin_name);
          } else {
            plugin_name = s;
          }
          //Log.println("plugin: "+plugin_name);
          Plugin plugin = Plugin.plugins.get(plugin_name);
          if (plugin != null) plugin.process(s);
          else Log.println("Plugin name "+plugin_name+" is not recognized");
        }        
      }
    };
    
    final Display display = new Display ();
    final Shell shell = new Shell (display);

    final ImageData data = new ImageData(OldNXTender.class.getClassLoader().getResourceAsStream("sivantoledo/nxt/nxt.ico")); 
    final Image image = new Image(display, data);

    final Tray tray = display.getSystemTray ();
    if (tray == null) {
      MessageBox mb = new MessageBox(shell,SWT.ICON_ERROR | SWT.OK);
      mb.setText("Error");
      mb.setMessage("NXTender failed to open a tray icon, exiting.");
      
      mb.open();
    
      shell.dispose();
      image.dispose();
      display.dispose();
      System.exit(-1);
    }
    
    final TrayItem tray_item = new TrayItem (tray, SWT.NONE);
    tray_item.setToolTipText("NXTender");
    tray_item.setImage (image);
    tray_item.addListener (SWT.Show, new Listener () {
      public void handleEvent (Event event) {
        System.out.println("show");
      }
    });
    tray_item.addListener (SWT.Hide, new Listener () {
      public void handleEvent (Event event) {
        System.out.println("hide");
      }
      });
    tray_item.addListener (SWT.Selection, new Listener () {
      public void handleEvent (Event event) {
        System.out.println("selection");
      }
    });
    tray_item.addListener (SWT.DefaultSelection, new Listener () {
      public void handleEvent (Event event) {
        System.out.println("default selection");
      }
    });
    
    /*
     * preferences
     */
    final Preferences prefs = Preferences.userRoot().node("/sivantoledo/nxt");
    String default_channel = prefs.get("channel", "not set");
    System.out.println("default channel is: "+default_channel);


    /*
     * tray icon menu
     */
      
    final Menu menu = new Menu (shell, SWT.POP_UP);
    
    MenuItem mi_about = new MenuItem (menu, SWT.PUSH);
    mi_about.setText ("About NXTender");
    mi_about.addListener (SWT.Selection, new Listener () {
      public void handleEvent (Event event) {
        System.out.println("selection " + event.widget);
        MessageBox mb = new MessageBox(shell,SWT.ICON_INFORMATION | SWT.OK);
        mb.setText(shellTitle);
        mb.setMessage("This is NXTender version "+versionMajor+"."+versionMinor
                      +Text.DELIMITER
                      +"by Sivan Toledo");
        
        mb.open();

        //Log.open(image);
        //tray_item.dispose();
      }
    });
    
    new MenuItem (menu, SWT.SEPARATOR);

    MenuItem mi_log = new MenuItem (menu, SWT.PUSH);
    mi_log.setText ("Log Display");
    mi_log.addListener (SWT.Selection, new Listener () {
      public void handleEvent (Event event) {
        System.out.println("selection " + event.widget);
        Log.open(image);
        //tray_item.dispose();
      }
    });

    new MenuItem (menu, SWT.SEPARATOR);

    /*
     * enumerate channels, put into menu, activate the default one.
     */
    
    java.util.Map<String,String> map = SerialInterface.enumerateInterfaces();
    for (String k: map.keySet()) {
      String channel = map.get(k);
      System.out.println(channel);
      
      MenuItem mi_ch = new MenuItem (menu, SWT.RADIO);
      mi_ch.setText(channel);
      mi_ch.setData(channel);
      mi_ch.addListener (SWT.Selection, new Listener () {
        public void handleEvent (Event event) {
          System.out.println("selection " + event.widget.getData()+" "+((MenuItem)(event.widget)).getSelection());
          if (((MenuItem)(event.widget)).getSelection()) {
            try {
              //System.out.println("connecting to channel "+(String) event.widget.getData());
              channel_lock.lock();
              c[0] = new SerialInterface("//./"+(String) event.widget.getData(), l);
              channel_lock.unlock();
              prefs.put("channel",(String) event.widget.getData());
              //System.out.println("Connected to channel "+(String) event.widget.getData());
            } catch (java.io.IOException ioe) {
              MessageBox mb = new MessageBox(shell,SWT.ICON_ERROR | SWT.OK);
              mb.setText("Error");
              mb.setMessage("NXTender failed to open channel "
                            +(String) event.widget.getData()
                            +" ("+ioe.getMessage()+")");            
              mb.open();
              
            }            
          } else {
            //System.out.println("disconnecting from channel "+(String) event.widget.getData());
            channel_lock.lock();
            if (c[0] != null) {
              final CommunicationInterface cc = c[0];
              new Thread() {
                public void run() { cc.close(); }
              }.start();
              //c[0].close();
              c[0] = null;
              //System.out.println("disconnected from channel "+(String) event.widget.getData());
            }
            channel_lock.unlock();
          }
          /*
          */  

          //shell.dispose();
        }
      });

      if (channel.equals(default_channel)) {
        try {
          channel_lock.lock();
          c[0] = new SerialInterface("//./"+channel, l);
          channel_lock.unlock();
          mi_ch.setSelection(true);
        } catch (java.io.IOException ioe) {
          MessageBox mb = new MessageBox(shell,SWT.ICON_ERROR | SWT.OK);
          mb.setText("Error");
          mb.setMessage("NXTender failed to open channel "
                        +channel
                        +" ("+ioe.getMessage()+")");            
          mb.open();          
        }            
      }      
      
    }

    new MenuItem (menu, SWT.SEPARATOR);

    MenuItem mi_exit = new MenuItem (menu, SWT.PUSH);
    mi_exit.setText ("Exit");
    mi_exit.addListener (SWT.Selection, new Listener () {
      public void handleEvent (Event event) {
        System.out.println("selection " + event.widget);
        tray_item.dispose();
      }
    });

    tray_item.addListener (SWT.MenuDetect, new Listener () {
      public void handleEvent (Event event) {
        menu.setVisible (true);
      }
    });

  //final Shell shell = new Shell (display);
  //shell.setLayout(new RowLayout());
  //radar = new RadarWidget (shell);
  //shell.pack();
  //shell.open ();
     
  while (!tray_item.isDisposed ()) {
    if (!display.readAndDispatch ()) display.sleep ();
  }
  channel_lock.lock();
  if (c[0] != null) {
    final CommunicationInterface cc = c[0];
    new Thread() {
      public void run() { cc.close(); }
    }.start();
  }
  channel_lock.unlock();
  Log.close();
  
  // close the plugins
  System.out.println("Shutting down plugins");
  for (String key: Plugin.plugins.keySet()) {
    System.out.println("Shutting down plugin "+key);
    Plugin.plugins.get(key).close();
  }
  
  tray_item.dispose();
  tray.dispose();
  image.dispose ();
  //display.dispose ();

  }
  
}
