/**
  * Navigation Information Display Panel for Moving Map package
  * @author Robert J Morton YE572246C
  * @version 16 December 1997
*/

import javax.swing.*;  // Java Swing GUI utilities
import java.awt.*;     // Java Abstract Windowing Toolkit

class navpanel extends JPanel implements navconst {

  // what the hell this is for, I don't know!
  private static final long serialVersionUID = 2007L;

  private static navpanel np;  // reference to this nav panel's own object
  private Font font;           // reference to a text font (or typeface)
  private FontMetrics fm;      // type-face dimensions etc.

  private loader ld;    // reference to the loader object
  private aircraft ac;  // reference to (current) aircraft object
  private int fh;       // font height (height of a line of text)

  private int ne;      // end position of numeric field (excluding sign)
  private int VY = 3;  // top margin for start of text
  private int vy;      // start height of first numeric value
  private int fn;      // field number index for the above array
  private String
    wpname = "",      // waypoint name buffer        
    sign = "";        // sign of the displayed field value
  private movmap MM;  // reference to the movmap applet class
  private Color fg;   // foreground colour for printing
  private boolean 
    podePaint = false;  // can paint/repaint nav data when true
  private String
    VALS[] = new String[11],  // array for field values
    SIGN[] = new String[11];  // array for corresponding signs

  navpanel(movmap MM, Color fg, aircraft ac) {
    this.MM = MM;  // reference to current instance of movmap applet
    this.fg = fg;  // foreground colour
    this.ac = ac;  // reference to (current) aircraft object
    np = this;     // reference to nav panel object

    // Create a font for screen lettering and get its letter dimensions etc
    font = new Font("Serif",Font.BOLD,12);
    fm = getFontMetrics(font);

    ne = fm.stringWidth("000:00.00")  // width of numeric display string
       + fm.stringWidth("+")  // width of + sign (assumed wider than - sign)
       + 15;  // inset
    fh = 18;  // [fm.getHeight();] get the full height of a line of text
  }

  void setLoader(loader ld) {this.ld = ld;}


  /* RE-DRAW THE PANEL IN THE EVENT OF IT BEING ECLIPSED OR UPDATED 
  The navigational data can be painted only after the background image,
  the route names and the waypoints of the default route have all been
  loaded. Painting before this time would mean painting data from vari-
  ables within class instances that had not yet been created. 

  DefaultsLoaded() returns true when both route names and a full set of
  default waypoints exist within the system. The local flag podePaint is
  set true once DefaultsLoaed() has returned true for the first time. If
  it goes false later during the loading of another route's waypoints,
  repainting of the navigational data can still take place without any
  problems. */

  public void paint(Graphics g) {
    if(!podePaint) {
      if(!ld.DefaultsLoaded())
        return;
      podePaint = true;
    }
    g.drawImage(movmap.IMG3,0,0,this);  // draw the panel's background photo

    vy = VY + fh + fh;           // go to line height of first field value
    g.setFont(font);             // activate the required font
    g.setColor(fg);              // and set text colour
    g.drawString(wpname,15,vy);  // display the name of the current waypoint
    vy += fh;                    // advance down a line

    for(int i = 0; i < 11; i++) {  // for each of the nav data fields
      if(i == 5)
        vy += fh + fh +2;  // miss 2 lines before line 5
      String v = VALS[i];  // value for the nav data field

      /* Display the value and its sign right justified in
      the numeric field at height 'vy' of the display line. */

      g.drawString(v + SIGN[i],ne - fm.stringWidth(v),vy );

      vy += fh;  // advance downwards to the next line
    }
  }
   

  /* It is necessary to update the nav data on the run() thread,
  independently of graphics because otherwise it will upset the
  repainting of the route selector drop-down list. */

  void atualizar() {
    waypnt wp = waypnt.getCurrent();  // reference of current waypoint object
    wpname = wp.getName();            // name of current waypoint
    fn = 0;                           // field number index

    // Display the waypoint data:
    VALS[0] = convert(wp.getLat(), true);     SIGN[0] = sign;  // latitude
    VALS[1] = convert(wp.getLng(), true);     SIGN[1] = sign;  // longitude
    VALS[2] = convert(wp.getDst(), false);    SIGN[2] = sign;  // distance
    VALS[3] = convert(wp.getrBrg(), false);   SIGN[3] = sign;  // bearing

    // Display the selected outbound radial.
    VALS[4] = convert(wp.getOutRad(), false); SIGN[4] = sign;

    // Display the aircraft data:
    VALS[5] = convert(ac.getLat(), true);     SIGN[5] = sign;  // latitude
    VALS[6] = convert(ac.getLng(), true);     SIGN[6] = sign;  // longitude
    VALS[7] = convert(ac.getSpd(), false);    SIGN[7] = sign;  // speed
    VALS[8] = convert(ac.getHdg(), false);    SIGN[8] = sign;  // heading

    // Display the required heading and rate of turn.
    VALS[9] = convert(ac.getRqH(), false);    SIGN[9] = sign;
    VALS[10] = convert(ac.getROT(), false);   SIGN[10] = sign;
    repaint();
  }


  private String convert(double c, boolean b) {
    String v = "";           // display string
    if(fn == 2)
      v = getDist(c);        // field #2 is 'waypoint distance'
    else if(fn == 7)
      v = getSpeed(c);       // field #7 is 'aircraft speed'
    else if(b)
      v = getLatLng(c, fn);  // if flag set it's a lat or lng
    else
      v = getBrg(c, fn);     // else it must be a 'bearing'
    fn++;
    return v;
  }

  // CONVERT AIRCRAFT SPEED FROM DOUBLE TO STRING
  private String getSpeed(double s) {
    sign = "";  // speed is always positive
    // Return the rounded whole number of kilometres per hour
    return "" + (int)Math.floor(s * KphRps);
  }

  // CONVERT the WAYPOINT DISTANCE FROM DOUBLE TO STRING
  private String getDist(double c) { 
    double x;

    // If aircraft is receding from the waypoint:
    if(wpenc.getCurrent().getToFlag()) {
      x = -c;      // reverse the sign of the distance
      sign = "-";  // and display it as negative
    } else {       // else aircraft is receding from the waypoint
      x = +c;      // so display the distance as positive
      sign = "+";
    }
    int d = (int)Math.floor(c *= KPR);  // find number of whole kilometres 

    // Return it as a display string and tack on the tenths of a kilometre.
    return "" + d + "." + (int)Math.floor(10 * (c - d));
  }


  // CONVERT A LATITUDE OR LONGITUDE INTO A DISPLAY STRING
  private String getLatLng(double c, int fn) {

    boolean sc = true;  // the sign of c
    if(c < 0) {         // if c is negative
      c = -c;           // make it positive 
      sc = false;       // and set sign negative
    }
    int d = (int)Math.floor(c *= DPR);  // find the integral number of units

    if(fn == 0 || fn == 5) {  // if it is a latitude
      if(sc)         // if it has a positive magnitude
        sign = "N";  // it is North
      else           // else it has a negative magnitude
        sign = "S";  // so it is South
    } else {         // else it must be a longitude    
      if(sc)         // if it has a positive magnitude
        sign = "E";  // it is East
      else           // else it has a negative magnitude
        sign = "W";  // so it is West
    }
    int m = (int)Math.floor(c = 60 * (c - d));  // whole number of minutes

    String ms = ms = ":" + m;  // assume initially that it needs no padding
    if(m < 10) ms = ":0" + m;  // pad it with a '0' if it's less than 10

    int f = (int)Math.floor(100 * (c - m));  // hundredths of a minute of arc

    String fs = "." + f;       // prefix it with a decimal point
    if(f < 10) fs = ".0" + f;  // pad it with a 0 if it's less than 10
    return "" + d + ms + fs;   // form it all into a string
  }


  private String getBrg(double c, int fn) {  // STRING OF A BEARING FIELD
    if(fn == 10) {   // if displaying rate of turn
      c *= 60;       // convert to degrees per minute
      if(c < 0) {    // if 'c' is negative
        sign = "-";  // make its sign a '-'
        c = -c;      // make 'c' itself positive
      } else         // else
        sign = "+";  // make the sign a '+'
    } else {         // else, all other bearings have no sign (0-360)
      sign = "";     // so kill its sign
      if(c < 0)      // if it is negative
        c += Twoπ;   // rationalise it as a 0 to 360 value
    }
    int d = (int)Math.floor(c *= DPR);  // find the integral number of units

    /* Return the bearing in the form of a string
    with the tenths of a degree tacked on the end. */

    return "" + d + "." + (int)Math.floor(10 * (c - d));
  }    
}