package sivantoledo.nxt.radar;

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

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

import java.util.LinkedList;
import java.util.concurrent.locks.*;

public class Radar extends NXTender.Plugin {
  private Radar() {
    super("RADAR");
  }
  
  public void close() {
    Display.getDefault().asyncExec(new Runnable() {
      public void run() { 
        if (shell != null) shell.dispose();
      }
    });
  }

  public void process(String command) {
    Log.println("radar command <"+command+">");

    String[] terms = command.split(" ");

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

    
    if (terms[1].equals("CLEAR"))
      Display.getDefault().asyncExec(new Runnable() {
        public void run() { 
          if (shell != null) radar.removeHits();
        }
      });

    if (terms[1].equals("OPEN")) {
      Log.println("In Radar Open command");
      Display.getDefault().asyncExec(new Runnable() {
        public void run() { 
          Log.println("In Radar runnable");
          open();
        }
      });

    }
    
    if (terms[1].equals("CLOSE"))
      Display.getDefault().asyncExec(new Runnable() {
        public void run() { 
          if (shell != null) shell.dispose();
        }
      });


    if (terms[1].equals("HIT")) {
      if (terms.length < 4) {
        Log.println("Error: RADAR HIT command requires two arguments");
      } else {
        final int theta = Integer.parseInt(terms[2]);
        final int rho   = Integer.parseInt(terms[3]);
        Display.getDefault().asyncExec(new Runnable() {
          public void run() { 
            if (radar != null) radar.addHit(theta,rho);
          }
        });
      }
    }
  }

  private static Radar instance;  
  public static Radar getInstance(Sender sender) {
    if (instance == null) instance = new Radar();
    return instance;
  }
  
  Shell shell;
  RadarWidget radar;

  // open must run in the UI thread
  
  private void open() {
    Log.println("In Radar Open");
    if (shell != null) return;
    Log.println("In Radar Open (2)");
    Display display = Display.getDefault();
    shell = new Shell (display);
    shell.setText("NXT Radar Display");
    shell.setLayout(new RowLayout());
    radar = new RadarWidget (shell);
    
    shell.addListener (SWT.Dispose, new Listener () {
      public void handleEvent (Event event) {
        shell = null; // so that we open another shell next time
        radar = null; // so that we open another shell next time
      }
    });

    
    shell.pack();
    shell.open ();
    Log.println("In Radar Open (3)");
  }
  
}

class RadarWidget extends Canvas {

  Color white;
  Color black;
  
  private class Hit {
    //int    timestamp;
    double x;
    double y;
  }
  
  Lock            list_lock;
  LinkedList<Hit> list;
  
  public RadarWidget(Composite parent) {
    super(parent, SWT.BORDER);
    System.out.println("constructor1");
    
    list_lock = new ReentrantLock();
    list = new LinkedList<Hit>();
    
    white = new Color(null, 255, 255, 255);
    black = new Color(null, 0, 0, 0);
    setBackground(white);

    System.out.println("constructor2");
    addDisposeListener(new DisposeListener() {
        public void widgetDisposed(DisposeEvent e) {
          System.out.println("disposing");
          white.dispose();
          black.dispose();
        }
    });
    
    System.out.println("constructor3");
    addPaintListener(new PaintListener() {
      public void paintControl(PaintEvent e) {
        System.out.println("paint listener");
         RadarWidget.this.paintControl(e);
      }
    });
    System.out.println("constructor4");
  }
  
  public void addHit(int theta, int rho) {
    if (rho == 255) return; // invalid reading
    Hit h = new Hit();
    //h.timestamp = timestamp;
    double theta_in_radians = (double) theta * (Math.PI / 180.0); 
    h.x = ((double) rho) * Math.cos( theta_in_radians );
    h.y = ((double) rho) * Math.sin( theta_in_radians );
    System.out.println("raw (theta,r) = ("+theta+","+rho+"), x,y = "+(int) h.x + ", "+ (int) h.y);
    list_lock.lock();
    list.add(h);
    list_lock.unlock();
    redraw();
    //this.getDisplay().asyncExec(new Runnable() {
    //  public void run() { RadarWidget.this.redraw(); }
    //});
  }
  
  public void removeHits() {
    list_lock.lock();
    list.clear();
    list_lock.unlock();
    System.out.println("removing all the hits");
    this.getDisplay().asyncExec(new Runnable() {
      public void run() { RadarWidget.this.redraw(); }
    });
  }

  void paintControl(PaintEvent e) {
    GC gc = e.gc;
    
    gc.setBackground(black);

    gc.drawLine(255,260,265,260);      
    gc.drawLine(260,255,260,265);      

    list_lock.lock();
    for (Hit h: list) {
      gc.fillOval(260 + (int) h.x, 260 - (int) h.y, 4, 4);      
      //System.out.println("drawing hit at "+(int) h.x + ", "+ (int) h.y);
    }
    list_lock.unlock();
 }

  public Point computeSize(int wHint, int hHint, 
                           boolean flushCache) {
    return new Point(520,520);
  }
  
  public Point computeSize(int wHint, int hHint) {
    return computeSize(wHint, hHint, true);
  }
  
  public static void main (String [] args) {
    Display display = new Display ();
    final Shell shell = new Shell (display);
    shell.setText("NXT Radar Display");
    shell.setLayout(new RowLayout());
    final RadarWidget canvas = new RadarWidget (shell);
    //canvas.setSize(200,200);
    //canvas.setLayoutData(new RowData(200,200));
    shell.pack();
    shell.open ();
    while (!shell.isDisposed ()) {
        if (!display.readAndDispatch ()) display.sleep ();
    }
    display.dispose ();
  }
}
