package ppmrmn;

import java.awt.*;       import java.applet.*;

public class Graph extends Applet {   //Applet instead of Frame
/*
  add(x, y)              Adds a point to a list.
                         Expects points to come in x-order.
  Graph()                Compulsory call
  Graph(graphTitle, xAxisTitle, yAxisTitle)
                         Version with labelling options.
                         Use empty strings if not all titles applicable
  nextGraph()            Starts a new graph on the same axes.
                         One showGraph call can be used for all the graphs.
  setColor(int 0 to 3)   Choice of black, magenta, blue, red
                         (constants are available instead of numbers)
  setSymbol(boolean)     Deduced from the colour
                         (Convenient if colours are being set)
  setSymbol(int 0 to 3)  circle, upside down triangle, triangle, square
                         (Used if colours are not being set)
  setLine(boolean)       Normally on, can be turned off
                         (Remember to turn it on again for the next line)
  setTitle(String)       Will appear on a key alongside the symbol and/or
                         in the chosen colour
  showGraph()            Plot the data curves
*/

  private int cent    = 10;   //position du graph utilisé dans Graph()
  private int largeur = 300;  //largeur de la fenetre
  private int hauteur = 340;  //hauteur de la fenetre

  private String xAxisTitle, yAxisTitle, graphTitle;
  private boolean keys;

  public void Graph () {
    // axes titles
    initializeGraph();     graphTitle = "";
    xAxisTitle = "";       yAxisTitle = "";
  }

  public void Graph (String g, String x, String y) {
    // axes titles
    initializeGraph();    graphTitle = g;
    xAxisTitle = x;       yAxisTitle = y;
  }

  private class Dataset {
    /* Simple class for recording basic info about a set of points.
    Allows more than one graph to be drawn on an axis.
    All initialisation done in nextGraph. */
    int count, plotType;      String title;
    boolean colorRequired, symbolRequired, titleRequired, lineRequired;
  }

  private ListGentle points;
  private ListGentle datasets;

  private void initializeGraph () {
    datasets = new ListGentle ();
    nextGraph();
    points = new ListGentle ();
    xMax = yMax = Double.MIN_VALUE;
    xMin = yMin = Double.MAX_VALUE;
    keys = false;
//    setSize(640, 480);
//    super.setTitle("Graph");
//    addWindowListener(new WindowAdapter(){
//      public void windowClosing(WindowEvent e){
//        dispose();
//      }
//    });
  }

  public void nextGraph () {
    Dataset d = new Dataset ();    d.count = 0;
    d.plotType = black;            d.title = "";
    d.symbolRequired = false;      d.colorRequired = false;
    d.titleRequired = false;       d.lineRequired = true;
    datasets.add (d);
  }

  public void setColor (int c) {
    ((Dataset) datasets.current()).colorRequired = true;
    ((Dataset) datasets.current()).plotType = c;
  }

  public void setSymbol (boolean b) {
    ((Dataset) datasets.current()).symbolRequired = b;
  }

  public void setSymbol (int c) {
    ((Dataset) datasets.current()).symbolRequired = true;
    ((Dataset) datasets.current()).plotType = c;
  }

  public void setTitle (String s) {
    ((Dataset) datasets.current()).titleRequired = true;
    ((Dataset) datasets.current()).title = s;
    keys = true;
  }

  public void setLine (boolean b) {
    ((Dataset) datasets.current()).lineRequired = b;
    if (b==false) ((Dataset) datasets.current()).symbolRequired = true;
  }

  public void add (double x, double y) {
    points.add (new Point (x,y));
    ((Dataset)datasets.current()).count++;
    if (x > xMax) xMax = x;    if (x < xMin) xMin = x;
    if (y > yMax) yMax = y;    if (y < yMin) yMin = y;
  }

  public void showGraph () {
    repaint();
    setVisible(true);
  }

  private class Point {
    /* simple class for an x,y coordinate */
    double xCoord, yCoord;
    Point (double x, double y) {
      xCoord = x;    yCoord = y;
    }
  }

  private void drawTitles (Graphics g) {
    Dataset d;
    int x = xBorder + cent;  //legende
    int y = hauteur-yBorder/2 + cent;  //legende
    datasets.reset();
    boolean lastset = datasets.eol();
    while (!lastset) {
      d = (Dataset) datasets.current();
      if (d.colorRequired) changeColor(g, d.plotType);
      if (d.symbolRequired) {
        drawSymbol(g, d.plotType, x, y-cs);
        x += 4*cs;
      }
      g.drawString(d.title, x, y);
      x += g.getFontMetrics().stringWidth(d.title)+20;
      lastset = datasets.eol();
      if (!lastset) datasets.succ();
    }
    g.setColor(Color.black);
  }

  private void drawAxes (Graphics g) {
    Font plain = g.getFont();
    Font small = new Font(plain.getFamily(), Font.PLAIN,10);
    Font bold  = new Font(plain.getFamily(), Font.BOLD,14);

    g.drawLine(xBorder-5 + cent, yOrigin, xAxisLength+xBorder+5 + cent,
               yOrigin);  //axe x
    g.drawLine(xOrigin, yBorder-5 + cent, xOrigin,
               yAxisLength+yBorder+5 + cent);  //axe y
    g.drawString(xAxisTitle,
      largeur-g.getFontMetrics().stringWidth(xAxisTitle)-xBorder/2 + cent,
      yOrigin-5);  //"x"
    g.drawString(yAxisTitle,
      xOrigin-g.getFontMetrics().stringWidth(yAxisTitle)/2,
      yBorder-8 + cent); //"y"

    g.setFont(bold);
    g.drawString(graphTitle,
      (largeur-g.getFontMetrics().stringWidth(graphTitle))/2,
      yBorder/2 + cent);  //titre
    g.setFont(plain);
    if (keys) drawTitles(g);

    // Tick and Label the four min/max points only
    int scaleXMin = scaleX(xMin);    int scaleXMax = scaleX(xMax);
    int scaleYMin = scaleY(yMin);    int scaleYMax = scaleY(yMax);
    //tick yMax:
    g.drawLine(xOrigin-5, scaleYMax + cent, xOrigin+5, scaleYMax + cent);
    //tick yMin:
    g.drawLine(xOrigin-5, scaleYMin + cent, xOrigin+5, scaleYMin + cent);
    //tick xMax:
    g.drawLine(scaleXMax + cent, yOrigin+5, scaleXMax + cent, yOrigin);
    //tick xMin:
    g.drawLine(scaleXMin + cent, yOrigin+5, scaleXMin + cent, yOrigin);
    g.setFont(small);
    //xMin:
    g.drawString(Double.toString(xMin),scaleXMin-10 + cent,yOrigin+15);
    //xMax:
    g.drawString(Double.toString(xMax),scaleXMax-10 + cent,yOrigin+15);
    //yMin:
    g.drawString(Double.toString(Math.round(100*yMin)/100.0),
                 xOrigin-35, scaleYMin+4 + cent);
    //yMax:
    g.drawString(Double.toString(Math.round(100*yMax)/100.0),
                 xOrigin-35, scaleYMax+4 + cent);
    g.setFont(plain);
  }

  private double xSpread, ySpread, xMin, xMax, yMin, yMax;
  private int xAxisLength, yAxisLength, xOrigin, yOrigin;
  private int xBorder, yBorder;

  public void paint (Graphics g) {
    // calulate length of axes from window size minus a border of 20
    xBorder = 40;    yBorder = 80;
    xAxisLength = (int) largeur - 2*xBorder;
    yAxisLength = (int) hauteur - 2*yBorder;

    // calculate value spreads from mins and maxs
    //which have been recorded as we go
    xSpread = xMax - xMin;    ySpread = yMax - yMin;
    if (xMin > 0) xOrigin = scaleX(xMin) + cent;
      else xOrigin = scaleX(0) + cent;
    if (yMin > 0) yOrigin = scaleY(yMin) + cent;
      else yOrigin = scaleY(0) + cent;
    drawAxes(g);    plotGraphs(g);
  }

  private int scaleX(double x) {
    return (int) ((x-xMin) / xSpread*xAxisLength) + xBorder ;
  }

  private int scaleY(double y) {
    return (int) (yAxisLength - ((y-yMin) / ySpread*yAxisLength)) + yBorder;
  }

  private void changeColor(Graphics g, int c) {
    switch (c) {
      case black :   {g.setColor(Color.black); break;}
      case magenta : {g.setColor(Color.magenta); break;}
      case blue :    {g.setColor(Color.blue); break;}
      case red :     {g.setColor(Color.red); break;}
    }
  }

  private static final int cs = 3; // pixel size of a symbol

  private void drawSymbol (Graphics g, int sy, int x, int y) {
    switch (sy) {
      case black :   {g.drawOval (x-cs, y-cs, 2*cs, 2*cs); break;}
      case magenta : {g.drawPolygon (trix(x),uptriy(y), 3); break;}
      case blue :    {g.drawPolygon (trix(x),triy(y), 3); break;}
      case red :     {g.drawRect (x-cs, y-cs, 2*cs, 2*cs); break;}
    }
  }

  private void plotGraphs (Graphics g) {
    Point p, q;    int x1, y1, x2, y2;
    Dataset d;    boolean lastset;    Color c;

    // Loop through each dataset
    datasets.reset();
    /* The points are in one big list, split by the
    counts recorded in each dataset */
    points.reset();
    lastset = datasets.eol();

    do {
      d = (Dataset) datasets.current();
      if (d.colorRequired) changeColor(g, d.plotType);
      // Start with the first point in the list for this graph
      p = (Point) points.current();
      x1 = scaleX(p.xCoord) + cent;      y1 = scaleY(p.yCoord) + cent;
      if (d.symbolRequired) drawSymbol(g, d.plotType, x1, y1);

      // Loop through the points as stored in the list
      for (int i=1; i<d.count; i++) {
        points.succ();                         q = (Point) points.current();
        x2 = scaleX(q.xCoord) + cent;          y2 = scaleY(q.yCoord) + cent;

        // plot the line and/or point
        if (d.lineRequired)   g.drawLine(x1, y1, x2, y2);
        if (d.symbolRequired) drawSymbol(g, d.plotType, x2, y2);
        x1 = x2; y1 = y2;
      }
      lastset = datasets.eol();
      if (!lastset) {
        datasets.succ();
        points.succ();
      }
    } while (!lastset);
  }

  private int [] trix (int p) {
    int [] a = new int [3];
    a[0] = p-cs;    a[1] = p+cs;    a[2] = p;
    return a;
  }

  private int [] triy (int p) {
    int [] a = new int [3];
    a[0] = p+cs;    a[1] = p+cs;    a[2] = p-cs;
    return a;
  }

  private int [] uptriy (int p) {
    int [] a = new int [3];
    a[0] = p-cs;    a[1] = p-cs;    a[2] = p+cs;
    return a;
  }

  public final int red = 3;       public final int blue = 2;
  public final int magenta = 1;   public final int black = 0;

}