/* ===================================================================== */
/** Interactive aircraft performance chart.
    @author Emmanuel Gustin, gustin@uia.ua.ac.be
    @version 1.1 of 17 October 1997
*/   
/* ===================================================================== */
import java.applet.*;
import java.awt.*;
import java.io.*;
import java.util.*;
import java.net.*;
import PlotCanvas;
import AuxMath;

/* ===================================================================== */

public class FighterPerf extends Applet {

  // --- constants ---
  public final static Color LabelColors[] = {
    Color.red,   Color.blue,   Color.yellow, Color.green, Color.magenta,
    Color.white, Color.orange, Color.cyan,   Color.pink,  Color.black
  };
  // --- status variables ---
  private boolean initialised = false;
  private boolean dataread = false;
  private int NrLabels = 4;
  // --- GUI members ---
  private PlotCanvas can = null;
  private Label nms[] = null;
  private Button btn[] = null;
  private List lst = null;
  // --- data members ---
  private Vector airc = null;
  private Aircraft active[];
  private double x0, x1, y0, y1;
  private double xtick, ytick;
  private int xstick, ystick;

  // --- init -------------------------------------------------------
  // Is used to set up the screen elements and read the data file. 
  public void init() {
    int i;
    // --- some parameters ---
    GetLimits();
    // --- prepare screen elements ---
    if (! initialised) {
      SetUpScreen();
      initialised = true;
    }
    // --- Now read data ---
    if (! dataread) {
      airc = new Vector();
      i = ReadData(airc, true);
      dataread = true;
    }
  }

  // --- set up screen ---------------------------------------------
  public void SetUpScreen() {
    GridBagLayout gbl;
    GridBagConstraints cns;
    Graphics gh;
    Insets ins;
    int i;
    // --- prepare screen elements ---
    setBackground(Color.lightGray);
    // set Layout 
    ins = new Insets(4, 4, 4, 4);
    cns = new GridBagConstraints();
    gbl = new GridBagLayout();
    setLayout(gbl);
    // set up windows
    // 1. canvas
    cns.insets    = ins;
    cns.weightx   = 1.0;
    cns.weighty   = 1.0;
    cns.gridx     = 0;
    cns.gridy     = 0;
    cns.ipadx     = 4;
    cns.ipady     = 4;
    cns.fill      = cns.BOTH;
    cns.gridwidth = cns.REMAINDER;
    can = new PlotCanvas();
    can.resize(600, 300);
    gbl.setConstraints(can, cns);
    add(can);
    // 2. text labels
    cns.gridx    = 1;
    cns.weightx  = 0.6;
    cns.fill     = cns.BOTH;
    cns.gridwidth = 1;
    nms = new Label[NrLabels];
    for (i = 0; i < NrLabels; i++) {
      cns.gridy = 1 + i;
      nms[i] = new Label("Label " + cns.gridy);
      gbl.setConstraints(nms[i], cns);
      add(nms[i]);
    }
    // 3. buttons
    cns.gridx   = 2;
    cns.fill    = cns.BOTH;
    cns.weightx = 0.1;
    btn = new Button[NrLabels];
    for (i = 0; i < NrLabels; i++) {
      cns.gridy = 1 + i;
      btn[i] = new Button(" Set " + cns.gridy + " ");
      gbl.setConstraints(btn[i], cns);
      add(btn[i]);
    }
    // 4. list
    lst = new List(NrLabels, false);
    cns.gridx      = 3;
    cns.gridy      = 1;
    cns.weightx    = 0.6;
    cns.gridheight = cns.REMAINDER;
    gbl.setConstraints(lst, cns);
    add(lst);
    // show 
    resize(600, 480);
    validate();
  }

  // --- start -----------------------------------------------------
  public void start() {
    Aircraft a = new Aircraft();
    int i, l;
    if (initialised && dataread) {
      // alloc
      active = new Aircraft[NrLabels];
      // labels
      l = NrLabels;
      if (l < airc.size()) {l = airc.size();}
      for (i = 0; i < NrLabels; i++) {
        active[i] = (Aircraft) airc.elementAt(i);
        nms[i].setText(active[i].name);
      }
      // MinMax();
      // list
      lst.clear();
      for (i = 0; i < airc.size(); i++) {
        a = (Aircraft) airc.elementAt(i);
        if (a.name != null) {lst.addItem(a.name);}
      }
    }
  }

  // --- get limits from parameters ---------------------------------
  public void GetLimits() {
    String str;
    str = getParameter("ncurves");
    if (str != null) {
      NrLabels = Integer.valueOf(str).intValue();
    } else {
      NrLabels = 4;
    }
  }

  // --- paint ------------------------------------------------------
  public void paint(Graphics g) {
    int i;
    Rectangle r;
    if (initialised) {
      // --- borders round labels ---
      for (i = 0; i < NrLabels; i++) {
        r  = nms[i].bounds();
        g.setColor(LabelColors[i]);
        g.drawRect(r.x-1, r.y-1, r.width + 2, r.height + 2);
        g.drawRect(r.x-2, r.y-2, r.width + 4, r.height + 4);
      }
      // --- set up PlotCanvas ---
      MinMax();
      can.clear();
      can.SetBorder(can.DEFAULT | can.GRID, can.DEFAULT | can.GRID);
      can.SetViewport(0.10, 0.95, 0.05, 0.90);
      can.SetWindow(x0, x1, y0, y1);
      can.SetTicks(5.0, 4.0);
      can.SetColor(Color.black);
      can.SetWidth(3);
      can.PlotBorder();
      // --- data ---
      if (dataread) {
        can.SetStyle(can.LINE);
        can.SetWidth(3);
        for (i = 0; i < NrLabels; i++) {
          can.SetColor(LabelColors[i]);
          active[i].PlotCanvas(can);
        }
      }
    }
    // --- validate ---
    validate();
  }

  // --- Event Handler ----------------------------------------------
  public boolean handleEvent(Event evt) {
    Aircraft a = new Aircraft();
    String itm;
    int i, j, n;
    boolean fnd;
    switch (evt.id) {
    case Event.ACTION_EVENT:
      if (evt.target instanceof Button) {
        i = 0;
        while (i < NrLabels) {
          if (evt.target == btn[i]) {
            itm = lst.getSelectedItem();
            if (itm == null) {return(true);}
            n = airc.size();
            j = 0;
            fnd = false;
            while ((! fnd) && (j < n)) {
              a = (Aircraft) airc.elementAt(j);
              fnd = a.name.equals(itm);
              if (fnd) {
                active[i] = a;
                nms[i].setText(a.name);
                repaint(250);
                return(true);
              } else {
                j++;
              }
            }
          }
          i++;
        } 
      }
    case Event.WINDOW_EXPOSE:
      repaint(250);
      return(true);
    case Event.WINDOW_DESTROY:
      System.exit(0);
      return(true);
    }
    return(false);
  }

  // --- Read Data -----------------------------------------------------
  public int ReadData(Vector vec, boolean FromNet) {
    Aircraft arc;
    FileInputStream fin;
    URL uin;
    DataInputStream din;
    String fname;
    boolean ok;
    int count;
    // read from net. Source is Applet paramater
    if (FromNet) {
      fname = getParameter("Source");
      try {
        uin = new URL(getDocumentBase(), fname);
        din = new DataInputStream(uin.openStream());
      }
      catch(MalformedURLException e) {return 0;}
      catch(IOException e) {return 0;}
    // read from local disk. 
    } else {
      try {
        fin = new FileInputStream("FighterPerf.data");
        din = new DataInputStream(fin);
      }
      catch(FileNotFoundException e) {return 0;}
      catch(IOException e) {return 0;}
    }
    // --- read records ---
    ok = true;
    while (ok) {
      arc = new Aircraft();
      ok = arc.read(din);
      if (ok) {
        vec.addElement(arc);
      }
    }
    return vec.size();
  }

  // --- MinMax --------------------------------------------------------
  public void MinMax() {
    int i;
    double r, xmin, xmax, ymin, ymax;
    // --- init ---
    xmin = active[0].MinSpeed();
    xmax = active[0].MaxSpeed();
    ymin = active[0].MinAltitude();
    ymax = active[0].MaxAltitude();
    // --- loop ---
    for (i = 1; i < NrLabels; i++) {
      r = active[i].MinSpeed();
      if (r < xmin) xmin = r;
      r = active[i].MaxSpeed();
      if (r > xmax) xmax = r;
      r = active[i].MinAltitude();
      if (r < ymin) ymin = r;
      r = active[i].MaxAltitude();
      if (r > ymax) ymax = r;
    }
    // --- copy ---
    x0 = xmin;
    x1 = xmax;
    y0 = 0.0;
    y1 = ymax;
    // --- done ---
    return;
  }

  // --- This also an application --------------------------------------
  public static void main(String argv[]) {
    int i;
    // allocate
    FighterPerf myPerf = new FighterPerf();
    // replace init
    myPerf.SetUpScreen();
    myPerf.airc = new Vector();
    i = myPerf.ReadData(myPerf.airc, false);
    // call start
    myPerf.start();
    // display
    myPerf.show();
  }

  // --- Parameter Info ------------------------------------------------
  public String[][] getParameterInfo() {
    String Info[][] = {{"Source",   "Name of file with data"},
                       {"NCurves",  "Number of curves to display <= 10"},
                      };
    return Info;
  }

}
/* ===================================================================== */

class Aircraft {

  // --- data members -----------------------------------------------
  public String name;
  public int NrCurves;
  public int Curves[];
  public double x[][], y[][];

  // --- pseudo constructor -----------------------------------------
  public Aircraft () {
    name     = "";
    NrCurves = 0;
    Curves   = null;
    x        = null;
    y        = null;
  }

  // --- read from a file -------------------------------------------
  public boolean read(DataInputStream di) {
    String str;
    StringTokenizer stk;
    int k, i, j;
    char c;
    // --- try to read ---
    try {
      // first loop to skip over all comment lines
      do  {
        str = di.readLine();
        if (str.length() == 0) {return(false);}
        c = str.charAt(0);
      } while (c == '#');
      // then check whether it starts with a number
      while (! Character.isLetter(c)) {
        str = di.readLine();
        if (str.length() == 0) {return(false);}
        c = str.charAt(0);
      }
      // If ok, assign it as name of this record
      name = str;
      // Read descriptive line
      str = di.readLine();
      if (str == null) {return(false);}
      stk = new StringTokenizer(str);
      if (stk.countTokens() < 2) {return(false);}
      NrCurves = Integer.valueOf(stk.nextToken()).intValue();
      if (NrCurves < 1) {return false;}
      Curves = new int[NrCurves];
      x      = new double[NrCurves][];
      y      = new double[NrCurves][];
      for (i = 0; i < NrCurves; i++) {
        if (! stk.hasMoreTokens()) {return(false);}
        k = Integer.valueOf(stk.nextToken()).intValue();
        Curves[i] = k;
      }
      // read data lines
      for (i = 0; i < NrCurves; i++) {
        str = di.readLine();
        stk = new StringTokenizer(str);
        if (stk.countTokens() != 2 * Curves[i]) {return(false);}
        x[i] = new double[Curves[i]];
        y[i] = new double[Curves[i]];
        for (j = 0; j < Curves[i]; j++) {
          y[i][j] = Double.valueOf(stk.nextToken()).doubleValue();
          x[i][j] = Double.valueOf(stk.nextToken()).doubleValue();
        }
      }
      // done
      return(true);
    }
    // --- catch blocks ---
    // for the moment, just stop reading the record. No clean up.
    // Caller should not try to store the results, and they will
    // go away... 
    catch(IOException e) {
      System.out.println("  ! IO " + e);
      return(false);
    }
    catch(NumberFormatException e) {
      System.out.println("  ! NumberFormat " + e);
      return(false);
    }
    catch(StringIndexOutOfBoundsException e) {
      System.out.println("  ! StringIndexOutOfBounds " + e);
      return(false);
    }
  }

  // --- plot on canvas -----------------------------
  public void PlotCanvas(PlotCanvas can) {
    int i;
    for (i = 0; i < NrCurves; i++) {
      can.Plot2Arrays(x[i], y[i], Curves[i]);
    }
  }

  // --- return max altitude ------------------------
  public double MaxAltitude() {
    int i, j;
    double m = 0.0;
    for (i = 0; i < NrCurves; i++) {
      for (j = 0; j < Curves[i]; j++) {
        if ((i == 0) && (j == 0)) {
          m = y[i][j];
        } else if (y[i][j] > m) {
          m = y[i][j];
        }
      }
    }
    return(m);
  }

  // --- return min altitude ------------------------
  public double MinAltitude() {
    int i, j;
    double m = 0.0;
    for (i = 0; i < NrCurves; i++) {
      for (j = 0; j < Curves[i]; j++) {
        if ((i == 0) && (j == 0)) {
          m = y[i][j];
        } else if (y[i][j] < m) {
          m = y[i][j];
        }
      }
    }
    return(m);
  }

  // --- return max speed -----------------------------
  public double MaxSpeed() {
    int i, j;
    double m = 0.0;
    for (i = 0; i < NrCurves; i++) {
      for (j = 0; j < Curves[i]; j++) {
        if ((i == 0) && (j == 0)) {
          m = x[i][j];
        } else if (x[i][j] > m) {
          m = x[i][j];
        }
      }
    }
    return(m);
  }

  // --- return min speed -----------------------------
  public double MinSpeed() {
    int i, j;
    double m = 0.0;
    for (i = 0; i < NrCurves; i++) {
      for (j = 0; j < Curves[i]; j++) {
        if ((i == 0) && (j == 0)) {
          m = x[i][j];
        } else if (x[i][j] < m) {
          m = x[i][j];
        }
      }
    }
    return(m);
  }

}

/* ===================================================================== */

