package nxm.ucl.libg;

import java.awt.Color;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import nxm.sys.inc.Drawable;
import nxm.sys.lib.Convert;
import nxm.sys.lib.Table;
import nxm.sys.lib.Time;
import nxm.sys.libg.Layer;
import nxm.sys.libg.Line;
import nxm.sys.libg.MPlot;
import nxm.sys.libg.MPoint;

/** This class is a small class to plot Aircraft onto a Map
 *  @author Jim Bauer
 *  @since NeXtMidas 2.9.2
 */
public class LayerAircraft extends Layer {

  private String colors[] = {"BRIGHTGREEN","BRIGHTMAGENTA","ORANGE","PINK","BRIGHTRED",
                             "BRIGHTYELLOW","LIGHTTEAL","CYAN"};
  private int                           currentColor = 0;
  private Hashtable<String,AircraftLoc> aircrafts = new Hashtable<>();
  private Vector<ACTracks>              acTracks  = new Vector<>();

  /** Flag for showing protected Aircraft on the Map */
  public boolean showProt = false;
  /** Flag for drawing Aircraft objects on the Map ??? */
  public boolean showAH   = true;

  /** LayerAircraft Default Constructor */
  public LayerAircraft () {
    super();
    name = "Aircraft";
    enable &= ~MPlot.EN_BSTORE;
  }

  /** Add points to Aircraft tracks based on movement of Aircraft
   *  @param tracksIn Input Aircraft previous track values ???
   *  @param lat      Latitude of new tracks
   *  @param lon      Longitude of new tracks
   *  @param heading  Cardinal heading (0=360=North,90=East,180=South,270=West)
   *  @param actname  Name for Aircraft track
   */
  public void addPoints (Vector<Vector<Double>> tracksIn,double lat, double lon,
                         double heading,String actname) {
    ACTracks act = new ACTracks();
    for (int i=0;i< tracksIn.size();i++) {
      Vector<Double> v2 = tracksIn.get(i);
      act.tracks[i][0] = v2.get(1);
      act.tracks[i][1] = v2.get(0);
    }
    act.arwc[0] = lon;
    act.arwc[1] = lat;
    act.myline.setSymbolHeading((float)heading);
    act.name = actname;
    synchronized (acTracks) { acTracks.add(act); }
    refresh();
  }

  /** Reset the Map's Aircraft tracks */
  public void resetTracks () {
    synchronized (acTracks) { acTracks.removeAllElements(); }
    refresh();
  }

  /** Draw all Aircrafts onto the Map */
  public void draw2 () {
    synchronized (aircrafts) {
      Enumeration<AircraftLoc> enum1 = aircrafts.elements();
      while (enum1.hasMoreElements()) {
        AircraftLoc acl = enum1.nextElement();
        if (acl != null) {
          if (acl.dp==0) continue;
          if ((acl.protect==1)&&(!this.showProt)) continue;

          MP.gc.setColor(Color.cyan);
          for (int i=0; i<acl.MAXPTS; i++) {
            try {
              acl.rwcTrac[0] = acl.tracks[i][0];
              acl.rwcTrac[1] = acl.tracks[i][1];
              int j = i+1;
              if (j >= acl.MAXPTS) j = 0;
              acl.rwc2Trac[0] = acl.tracks[j][0];
              acl.rwc2Trac[1] = acl.tracks[j][1];
              if ((acl.rwcTrac[0] > 0) && (acl.rwc2Trac[0] > 0)) {
                MP.viewer.rwc2pix(acl.rwcTrac, acl.mptTrac);
                MP.viewer.rwc2pix(acl.rwc2Trac, acl.mpt2Trac);
                MP.gc.drawLine(acl.mptTrac.x, acl.mptTrac.y, acl.mpt2Trac.x, acl.mpt2Trac.y);
              }
            } catch (Exception e) {
              System.out.println("LayerAircraft: excpetion when drawing ["+i+"].");
            }
          }
          MP.gc.setColor(acl.myline.getColor());
          MP.viewer.rwc2pix(acl.rwc, acl.mpt);
          acl.myline.getSymbol().draw(acl.mpt.x,acl.mpt.y,MP.gc);

          //MP.drawCircleLonLat(acl.lon,acl.lat,4,-1,line);
          MP.drawEllipse(acl.lon, acl.lat, 0, acl.radius, acl.radius, 0, -1, line);
          MP.gc.drawString(acl.name, acl.mpt.x+4, acl.mpt.y-4);
        }
      }
    }
  }

  private void draw3 () {
    synchronized (acTracks) {
      Enumeration<ACTracks> e1 = acTracks.elements();
      while (e1.hasMoreElements()) {
        ACTracks act = e1.nextElement();
        MP.gc.setColor(Color.cyan);
        for (int i=0; i < act.MAXPTS; i++) {
          act.rwc[0] = act.tracks[i][0];
          act.rwc[1] = act.tracks[i][1];
          int j = i+1;
          if (j >= act.MAXPTS) j = 0;
          act.rwc2[0] = act.tracks[j][0];
          act.rwc2[1] = act.tracks[j][1];
          if ((act.rwc[0] > 0)&& (act.rwc2[0] > 0)) {
            MP.viewer.rwc2pix(act.rwc, act.mpt);
            MP.viewer.rwc2pix(act.rwc2, act.mpt2);
            MP.gc.drawLine(act.mpt.x, act.mpt.y, act.mpt2.x, act.mpt2.y);
          }
        }
        if (act.myline != null) {
          MP.viewer.rwc2pix(act.arwc, act.ampt);
          act.myline.getSymbol().draw((int) act.ampt.getX(),(int) act.ampt.getY(), MP.gc);
          MP.gc.drawString(act.name, (int)act.ampt.getX()+4, (int)act.ampt.getY()-4);
        }
      }
    }
  }

  public synchronized void draw (@SuppressWarnings("unused")int mode) {
    try {
      if ((aircrafts.size()>0)&&(showAH)) draw2();
      if (acTracks.size()>0)              draw3();
      clearNeedRefresh();
    } catch (Exception e) {
      System.out.println("LayerAircraft: Exception in draw: "+e);
    }
  }

  /** Create a new Aircraft or update an existing Aircraft
   *  @param tbl The table containing input Aircraft data
   */
  public void updateAircraft (Table tbl) {
    String chanID = tbl.getS("CHANNEL");
    AircraftLoc acl = null;
    synchronized (aircrafts) {
      if (!aircrafts.containsKey(chanID)) {
        acl = new AircraftLoc();
        aircrafts.put(chanID, acl);
      } else {
        acl = aircrafts.get(chanID);
      }
    }
    if (acl != null) {
      if (acl.needUpdate()) {
        acl.name = tbl.getS("GCS","NONE");
        acl.dp   = tbl.getL("DATAPRESENT",1);
        acl.lat  = tbl.getD("LAT",0.0);
        acl.lon  = tbl.getD("LON",0.0);
        acl.protect = tbl.getL("PROT",0);

        String user = tbl.getS("USERS","NONE");
        String users[] = user.split("/");
        //System.out.println(users.length+" User = :"+user);

        if (users.length > 1) {
          int idx = user.indexOf("MAIN/");
          int idx2 = user.indexOf("/MAIN");
          if (idx > -1) {
            user = user.replace("MAIN/", "");
          } else if (idx2 > -1) {
            user = user.replace("/MAIN", "");
          }
          acl.name = tbl.getS("GCS", "NONE")+"_"+user+"_"+(tbl.getD("FREQ",0.0)/1e6);
        } else {
          acl.name = tbl.getS("GCS","NONE")+"_"+(tbl.getD("FREQ",0.0)/1e6);
        }

        String color = tbl.getS("COLOR");
        if ((color.length()==7) && (acl.c==null)) {
          char[] array = color.toCharArray();
          int val1 = Convert.o2l("0x"+array[1]+array[2]);
          int val2 = Convert.o2l("0x"+array[3]+array[4]);
          int val3 = Convert.o2l("0x"+array[5]+array[6]);
          acl.c = new Color(val1,val2,val3);
          acl.myline.setColor(acl.c);
          acl.myline.setColor(acl.c);
        }
        acl.rwc[0] = acl.lon;
        acl.rwc[1] = acl.lat;
        acl.rwc[2] = 0;
        double x1 = (nxm.sys.inc.Constants.EARTH_EQUATORIAL_RADIUS * tbl.getD("ALT"))*2;
        acl.radius = Math.sqrt(x1);
        acl.radius = acl.radius * 0.3;
        acl.myline.setSymbolHeading(tbl.getF("HEAD"));
        if (acl.dp > 0) acl.addPoint(acl.lat,acl.lon);
        //MP.refresh(Drawable.DATA);
        refresh(Drawable.DATA);
      } else {
        // no need for an update, previous update was within tolerance (1 sec)
      }
    }
  }

  /** Remove a single Aircraft with a given key
   *  @param key the given key
   */
  public void removeAC (String key) {
    aircrafts.remove(key);
    refresh();
  }

  /** Remove all Aircrafts */
  public void removeAC () {
    Enumeration<String> enum1 = aircrafts.keys();
    while (enum1.hasMoreElements()) {
      aircrafts.remove(enum1.nextElement());
    }
    refresh();
  }

  private class ACTracks {
    protected int        MAXPTS  = 300;
    protected double[][] tracks  = new double[MAXPTS][2];
    protected double[]   arwc    = {0,0,0};
    protected Line       myline  = null;
    private double[]     rwc     = {0,0,0};
    private double[]     rwc2    = {0,0,0};
    private MPoint       mpt     = new MPoint();
    private MPoint       mpt2    = new MPoint();
    private MPoint       ampt    = new MPoint();
    public String        name    = "NA";

    public ACTracks () {
      myline = line.copy();
      myline.setSymbol("Aero");
      myline.setSymbolSize(20);
      myline.setSymbolFill(true);
      myline.setColor(Color.green.darker());
    }
  }

  private class AircraftLoc {
    //public double   lastUpdateTime = 0.0;
    public String   name     = null;
    public double   lat      = 0.0;
    public double   lon      = 0.0;
    public double   radius   = 0.0;
//    public double   freq     = 0.0;
//    public float    heading  = 0;
//    public String   label    = "-";
//    public boolean  display  = false;
    public int       protect  = 0;
    public int       dp       = 0;
    public Line      myline   = null;
    public MPoint    mpt      = new MPoint();
    public double[]  rwc      = new double[3];
    public double[]  rwc2     = new double[3];

    protected int        ptIdx    = 0;
    protected int        MAXPTS   = 300;
    protected double[][] tracks   = new double[MAXPTS][2];
    protected double[]   rwcTrac  = {0,0,0};
    protected double[]   rwc2Trac = {0,0,0};
    protected MPoint     mptTrac  = new MPoint();
    protected MPoint     mpt2Trac = new MPoint();
    private   double     nextTime = -1;

    public Color c = null;

    public AircraftLoc() {
      myline = line.copy();
      myline.setSymbol("Aero");
      myline.setSymbolSize(20);
      myline.setSymbolFill(true);
      //myline.setColor(Color.green.darker());
      myline.setColor(colors[currentColor+1]);
      if (currentColor>=colors.length) currentColor=0;
    }

    public boolean needUpdate () {
      double d = Time.current();
      if (d > nextTime) {
        nextTime = d +1;
        return true;
      }
      return false;
    }

    public void addPoint (double lat, double lon) {
      tracks[ptIdx][0] = lon;
      tracks[ptIdx][1] = lat;
      ptIdx++;
      if (ptIdx >= MAXPTS) ptIdx = 0;
      tracks[ptIdx][0] = 0;
      tracks[ptIdx][1] = 0;
    }

    public void resetTrack () {
      for (int i=0; i<MAXPTS; i++) {
        tracks[i][0] = 0;
      }
    }

  }  // end nested class AircraftLOC

}  // end class LayerAircraft