package sivantoledo.nxt.joystick;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;

import sivantoledo.nxt.Log;
import sivantoledo.nxt.NXTender;
import sivantoledo.nxt.Sender;
import sivantoledo.nxt.NXTender.Plugin;
import sivantoledo.win32.SendInput;

import de.hardcode.jxinput.*;
import de.hardcode.jxinput.event.*;

public class Joystick extends NXTender.Plugin {
  
  private byte commandsMailbox = -1;
  private byte xMailbox        = -1;
  private byte yMailbox        = -1;
  private byte zMailbox        = -1;
  private byte sliderMailbox   = -1;
  
  private int  xstate = 0;
  private int  ystate = 0;
  private int  zstate = 0;
  private int  sstate = 0;

  private static byte parseMailbox(String[] terms) {
    byte mailbox;
    if (terms.length == 3) {
      mailbox = (byte) (Integer.parseInt(terms[2]) - 1);
      if (mailbox < 0 || mailbox > 9) {
        Log.println("incorrect JOYSTICK command (argumnet must be between 1 and 10): "+mailbox);          
        mailbox = -1;
      }
    } else {
      Log.println("incorrect JOYSTICK command");
      mailbox = -1;
    }
    return mailbox;
  }
  
  /*
   * Contruction. 
   * This class is a singleton.
   */
  
  Sender sender;
  
  private Joystick() {
    super("JOYSTICK");
  }

  private static Joystick instance;  
  public static Joystick getInstance(Sender sender) {
    if (instance == null) {
      instance = new Joystick();
      instance.sender = sender;
    }
    return instance;
  }
  
  /*
   * Process NXT Commands
   */
  
  public void close() {
    stop();
  }

  public void process(String command) {
    String[] terms = command.split(" ");

    if (terms.length == 1) {
      Log.println("incorrect JOYSTICK command: "+command);
      return;
    }
    
    Log.println("joystick command <"+terms[1]+">");    

    if (terms[1].equals("CLEAR")) {
      commandsMailbox = -1;
      xMailbox        = -1;
      yMailbox        = -1;
      zMailbox        = -1;
      sliderMailbox   = -1;
      
      xstate          = 0;
      ystate          = 0;
      zstate          = 0;
      sstate          = 0;
    }

    if (terms[1].equals("STOP"))
      stop();

    if (terms[1].equals("START"))
      start();

    if (terms[1].equals("COMMANDS-ON")) {
      commandsMailbox = parseMailbox(terms);
      sender.flushQueue(commandsMailbox);
    }

    if (terms[1].equals("X-ON")) {
      xMailbox = parseMailbox(terms);
      sender.flushQueue(xMailbox);
    }

    if (terms[1].equals("Y-ON")) {
      yMailbox = parseMailbox(terms);
      sender.flushQueue(yMailbox);
    }
    
    if (terms[1].equals("Z-ON")) {
      zMailbox = parseMailbox(terms);
      sender.flushQueue(zMailbox);
    }

    if (terms[1].equals("SLIDER-ON")) {
      sliderMailbox = parseMailbox(terms);
      sender.flushQueue(sliderMailbox);
    }
  }


  /*
   * Joystick Event Handlers
   */
  
  private JXInputDevice joystick;
  
  private void start() {
    Log.println("number of devices = "+JXInputManager.getNumberOfDevices());
    
    if (JXInputManager.getNumberOfDevices()==0) {
      Log.println("Error: The NXT requested joystick input, but no joystick is connected.");

      Display.getDefault().asyncExec(new Runnable() {
        public void run() {
          Display display = Display.getDefault();
          Shell shell = new Shell(display);
          MessageBox mb = new MessageBox(shell,SWT.ICON_ERROR | SWT.OK);
          mb.setText("Error");
          mb.setMessage("The NXT requested joystick input, but no joystick is connecte.");        
          mb.open();
          shell.dispose();
        }
      });

      return;
    }
    
    joystick = JXInputManager.getJXInputDevice(0);
    
    Log.println("Using Joystick: "+joystick.getName());
    
    ButtonListener      bl = new ButtonListener();
    AxisListener        al = new AxisListener();
    DirectionalListener dl = new DirectionalListener();
    
    for (int i=0; i<joystick.getMaxNumberOfButtons(); i++) {
      if (joystick.getButton(i) != null) {
        Log.println("button "+i+": "+joystick.getButton(i).getName());
        JXInputEventManager.addListener(bl,joystick.getButton(i));
      }
    }

    for (int i=0; i<joystick.getMaxNumberOfAxes(); i++) {
      if (joystick.getAxis(i) != null) {
        Log.println("axis "+i+": "+joystick.getAxis(i).getName());
        JXInputEventManager.addListener(al,joystick.getAxis(i),0.1);
      }
    }

    for (int i=0; i<joystick.getMaxNumberOfDirectionals(); i++) {
      if (joystick.getDirectional(i) != null) {
        Log.println("directional "+i+": "+joystick.getDirectional(i).getName());
        JXInputEventManager.addListener(dl,joystick.getDirectional(i));
      }
    }    
    
    JXInputEventManager.setTriggerIntervall( 10 );    
  }
  
  private void stop() {
    JXInputEventManager.setTriggerIntervall( -1 );
    
  }
  
  class AxisListener implements JXInputAxisEventListener {
    public void changed( JXInputAxisEvent ev ) {
      String name  = ev.getAxis().getName();
      double value = ev.getAxis().getValue();
      int    ival;
      Log.println("axis "+name+" value "+value);
      
      if (name.equals("Y Axis")) {
        ival = (int) (-100*value);
        if (yMailbox != -1) sender.send(yMailbox,ival,false);
        if (ival >  50 && ystate != 1) {
          ystate = 1;
          if (commandsMailbox != -1) sender.send(commandsMailbox,"Y +",true);
        }
        if (ival < -50 && ystate != -1) {
          ystate = -1;
          if (commandsMailbox != -1) sender.send(commandsMailbox,"Y -",true);
        }
        if (ival < 25 && ival > -25 && ystate != 0) {
          ystate = 0;
          if (commandsMailbox != -1) sender.send(commandsMailbox,"Y 0",true);
        }
      }

      if (name.equals("X Axis")) {
        ival = (int) (100*value);
        if (xMailbox != -1) sender.send(xMailbox,ival,false);
        if (ival >  50 && xstate != 1) {
          xstate = 1;
          if (commandsMailbox != -1) sender.send(commandsMailbox,"X +",true);
        }
        if (ival < -50 && xstate != -1) {
          xstate = -1;
          if (commandsMailbox != -1) sender.send(commandsMailbox,"X -",true);
        }
        if (ival < 25 && ival > -25 && xstate != 0) {
          xstate = 0;
          if (commandsMailbox != -1) sender.send(commandsMailbox,"X 0",true);
        }
      }
      
      if (name.equals("Z Rotation")) {
        ival = (int) (100*value);
        if (zMailbox != -1) sender.send(zMailbox,ival,false);
        if (ival >  50 && zstate != 1) {
          zstate = 1;
          if (commandsMailbox != -1) sender.send(commandsMailbox,"Z +",true);
        }
        if (ival < -50 && zstate != -1) {
          zstate = -1;
          if (commandsMailbox != -1) sender.send(commandsMailbox,"Z -",true);
        }
        if (ival < 25 && ival > -25 && zstate != 0) {
          zstate = 0;
          if (commandsMailbox != -1) sender.send(commandsMailbox,"Z 0",true);
        }
      }

      if (name.equals("Slider")) {        
        ival = (int) (100-100*value);
        if (sliderMailbox != -1) sender.send(sliderMailbox,ival,false);
        if (ival >  50 && sstate != 1) {
          sstate = 1;
          if (commandsMailbox != -1) sender.send(commandsMailbox,"S +",true);
        }
        if (ival < 25 && sstate != 0) {
          sstate = 0;
          if (commandsMailbox != -1) sender.send(commandsMailbox,"S 0",true);
        }
      }
    }
  };
  
  class ButtonListener implements JXInputButtonEventListener {
    public void changed( JXInputButtonEvent ev ) {
      String  name  = ev.getButton().getName();
      boolean state = ev.getButton().getState();
      Log.println("button "+name+" state "+state);
      if (state) 
        if (commandsMailbox != -1) sender.send(commandsMailbox, name);
    }
  };

  class DirectionalListener implements JXInputDirectionalEventListener {
    public void changed( JXInputDirectionalEvent ev ) {
      String name = ev.getDirectional().getName();
      Log.println("direc "+name+" value "
          +ev.getDirectional().getDirection() 
          +",  "
          +ev.getDirectional().getValue());
    }
  };

  static { 
    java.io.FileOutputStream dll_f;
    java.io.InputStream      dll_r;
    try {
      System.loadLibrary("jxinput"); 
    } catch (UnsatisfiedLinkError ule) {
      try {
        dll_r = SendInput.class.getClassLoader().getResourceAsStream("jxinput.dll"); 
        dll_f = new java.io.FileOutputStream("jxinput.dll");
        byte[] buffer = new byte[8192];
        int    bytes_read = 0;
        do {
          dll_f.write(buffer, 0, bytes_read);
          bytes_read = dll_r.read(buffer);
        } while (bytes_read != -1);
        dll_r.close();
        dll_f.close();
        System.loadLibrary("jxinput");
      } catch (java.io.IOException ioe) {
        System.out.println("Error encountered while trying to copy a DLL to the working directory: "
                           +ioe.getMessage());
      }
    }
  }
  
  
}
