import java.io.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.net.URL;
import java.text.*;
import java.util.Map;
import java.util.Hashtable;
import java.util.jar.Attributes;

// Module 1 Exercise 4
public class Mod1Plot extends Panel {

    Mod1State state;
    Image im;
    Graphics imG;
    Graphics2D g2d;

    Point labelA_pt, labelB_pt, labelC_pt;
    int mainWidth = 700;
    int mainHeight = 500;

    int yMin, yMax, yMid;
    int xMin, xMax, xMid;
    int yBorder, yOffset, xBorder, xOffset;

    //static Color bgColor = new Color(194,235,255);
    //private static final Color bgColor = Color.gray;
    private static final Color bgColor = Color.lightGray;

  static int degreesPerX = 5;

  static double piOver4 = Math.PI/4;
  double deltaY, deltaX;
  FontMetrics fm;
  int fontHeight,ticLength;
    //static Font boldFont = new Font("Serif",Font.BOLD, 18);

    //double actualTheta, xLineLength, yLineLength, redLineLength;
    double xLineLengthA, yLineLengthA,  xLineLengthB, yLineLengthB, 
        redLineLength, blueLineLength, greenLineLength;
    static double lineLengthThreshold = 0.31625;
    int xValA, yValA, xValB, yValB;
    int xValAplusB, yValAplusB, xValAminusB, yValAminusB;
    boolean anyVectors = false;

    static double piOver180 = Math.PI/180.0;

    //int[] xThetaPts = new int[100];
    //int[] yThetaPts = new int[100];
    //int ThetaPtsLength = 0;  // initially

    public Mod1Plot(Mod1State state) {
        super();
        this.state = state;
    }

    public void initialPlot() {
        setLayout(null);
        setBackground(bgColor);
        //im = createImage(700,250);

        //im = createImage(mainWidth,mainHeight);

GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice(); 
GraphicsConfiguration gc = gs.getDefaultConfiguration();

// Create an image that does not support transparency 

im = gc.createCompatibleImage(mainWidth, mainHeight, Transparency.TRANSLUCENT);
//im = gc.createCompatibleImage(mainWidth, mainHeight, Transparency.TRANSLUCENT);
// types: Transparency.OPAQUE, Transparency.BITMASK, Transparency.TRANSLUCENT


        imG = im.getGraphics();   // DOESN'T LIKE!!!!
        g2d = (Graphics2D)imG;

        // where to draw on the screen:
        yOffset = 10;  
        yBorder = 30;

        //yOffset = 90;
        yOffset = 20;

        xOffset = 10;  
        xBorder = 30;

        //xOffset = 50;
        xOffset = 20;

        xMin = xBorder+xOffset;
        //xMax = 365;
        xMax = 440;
        xMid = (int)(0.5*(xMax+xMin));
        yMin = yBorder+yOffset; 
        //yMax = 365;
        yMax = 440;
        yMid = (int)(0.5*(yMax+yMin));
        // END NEW

        //deltaY = (yMax - yMin)/10.0;
        //deltaX = (xMax - xMin)/10.0;
        deltaY = (yMax - yMin)/20.0;
        deltaX = (xMax - xMin)/20.0;

        // Get font sizes:
        setFont(TheFonts.bold16);
        fm = getFontMetrics(getFont());
        fontHeight = fm.getHeight();
        ticLength  = fm.stringWidth("w") - 4; // just a wide letter.

        //paintImage();
        //repaint();
        drawPlot();
  }


    public void drawPlot() {
        double xTheta, yTheta;
        // if both x and y, or both z and theta are non -99 (valid),
        //  then determine xVal and yVal (endpoint of red line), and other
        //  info needed to draw results on the graph.
        xValA = xMid + (int)(state.xValA*deltaX);
        yValA = yMid - (int)(state.yValA*deltaY);
        xValB = xMid + (int)(state.xValB*deltaX);
        yValB = yMid - (int)(state.yValB*deltaY);

        xValAplusB = xMid + (int)((state.xValA+state.xValB)*deltaX);
        yValAplusB = yMid - (int)((state.yValA+state.yValB)*deltaY);
        xValAminusB = xMid + (int)((state.xValA-state.xValB)*deltaX);
        yValAminusB = yMid - (int)((state.yValA-state.yValB)*deltaY);

        xLineLengthA = state.xValA;
        yLineLengthA = state.yValA;
        xLineLengthB = state.xValB;
        yLineLengthB = state.yValB;
        redLineLength = state.zValA/2.0;
        blueLineLength = state.zValB/2.0;
        if (state.outputAplusB) 
            greenLineLength = (state.zValA+state.zValB)/2.0;
        else
            greenLineLength = (state.zValA-state.zValB)/2.0;
        //actualTheta = state.thetaVal;

        // Calculate the poly-line points for the curved theta line
        //double thetaInRadians;
        //int deltaTheta;
        //if (actualTheta < 0) deltaTheta = -2;
        //else deltaTheta = 2;
        //int thetaCtr = deltaTheta;
        //ThetaPtsLength = 0;
        //xThetaPts[0] = (int)(redLineLength*deltaX) + xMid;
        //yThetaPts[0] = yMid;
        //while (Math.abs(thetaCtr-deltaTheta) <= Math.abs(actualTheta)) {
        //    ThetaPtsLength++;
        //    thetaInRadians = thetaCtr*piOver180;
        //    yTheta = redLineLength*Math.sin(thetaInRadians);
        //    xTheta = redLineLength*Math.cos(thetaInRadians);
        //    yThetaPts[ThetaPtsLength] = yMid - (int)(yTheta*deltaY);
        //    xThetaPts[ThetaPtsLength] = (int)(xTheta*deltaX) + xMid;
        //    thetaCtr += deltaTheta;
        //}
        anyVectors = true;
        paintImage();
        repaint();
    }

  public void paintImage() {
      imG.setColor(bgColor);
      imG.fillRect(0,0,mainWidth,mainHeight);

      //imG.clearRect(0,0,mainWidth,mainHeight);
      imG.setFont(TheFonts.bold16);
      // Draw x and y axis:
      drawAxis(imG);
      // Draw axis tick marks:
      drawAxisTicks(imG);

      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                           RenderingHints.VALUE_ANTIALIAS_ON);


      if (anyVectors && !state.outputB) {
          imG.setColor(state.redColor);
          drawLine(imG,xMid,yMid,xValA,yValA);
          labelA_pt = getLabel_pt(xMid,yMid,xValA,yValA);
          imG.drawString("A",labelA_pt.x,labelA_pt.y);
          if (redLineLength*2 > lineLengthThreshold) {  // Draw arrow head
              if (xMid != xValA || yMid != yValA) {
                  Point[] arrowPts = MyArrows.getDblArrowHeadPts(xValA, yValA, 
                                                                 xMid, yMid);
                  int[] arrowXPts = new int[3];
                  int[] arrowYPts = new int[3];
                  arrowXPts[0] = arrowPts[0].x; arrowYPts[0] = arrowPts[0].y;
                  arrowXPts[1] = arrowPts[1].x; arrowYPts[1] = arrowPts[1].y;
                  arrowXPts[2] = arrowPts[2].x; arrowYPts[2] = arrowPts[2].y;
                  imG.fillPolygon(arrowXPts,arrowYPts,3);
              }
          }
      }
          
      if (anyVectors && state.outputB) {
          imG.setColor(state.blueColor);
          drawLine(imG,xMid,yMid,xValB,yValB);
          labelB_pt = getLabel_pt(xMid,yMid,xValB,yValB);
          imG.drawString("B",labelB_pt.x,labelB_pt.y);
          if (blueLineLength*2 > lineLengthThreshold) {  // Draw arrow head
              if (xMid != xValB || yMid != yValB) {
                  Point[] arrowPts = MyArrows.getDblArrowHeadPts(xValB, yValB, 
                                                                 xMid, yMid);
                  int[] arrowXPts = new int[3];
                  int[] arrowYPts = new int[3];
                  arrowXPts[0] = arrowPts[0].x; arrowYPts[0] = arrowPts[0].y;
                  arrowXPts[1] = arrowPts[1].x; arrowYPts[1] = arrowPts[1].y;
                  arrowXPts[2] = arrowPts[2].x; arrowYPts[2] = arrowPts[2].y;
                  imG.fillPolygon(arrowXPts,arrowYPts,3);
              }
          }
      } else if (anyVectors && state.outputAplusB) {
          // Put vector B at the end of A
          imG.setColor(state.blueColor);
          drawLine(imG,xValA,yValA,xValAplusB,yValAplusB);
          labelB_pt = getLabel_pt(xValA,yValA,xValAplusB,yValAplusB);
          imG.drawString("B",labelB_pt.x,labelB_pt.y);
          if (blueLineLength*2 > lineLengthThreshold) {  // Draw arrow head
              //if (xValAplusB != xEnd || yValAplusB != yEnd) {
              Point[] arrowPts = MyArrows.getDblArrowHeadPts(xValAplusB, yValAplusB,
                                                             xValA, yValA);
              int[] arrowXPts = new int[3];
              int[] arrowYPts = new int[3];
              arrowXPts[0] = arrowPts[0].x; arrowYPts[0] = arrowPts[0].y;
              arrowXPts[1] = arrowPts[1].x; arrowYPts[1] = arrowPts[1].y;
              arrowXPts[2] = arrowPts[2].x; arrowYPts[2] = arrowPts[2].y;
              imG.fillPolygon(arrowXPts,arrowYPts,3);
              //}
          }
          
          imG.setColor(state.greenColor);
          drawLine(imG,xMid,yMid,xValAplusB,yValAplusB);
          labelC_pt = getLabel_pt(xMid,yMid,xValAplusB,yValAplusB);
          imG.drawString("C",labelC_pt.x,labelC_pt.y);
          if (greenLineLength*2 > lineLengthThreshold) {  // Draw arrow head
              Point[] arrowPts = MyArrows.getDblArrowHeadPts(xValAplusB, yValAplusB,
                                                             xMid, yMid);
              int[] arrowXPts = new int[3];
              int[] arrowYPts = new int[3];
              arrowXPts[0] = arrowPts[0].x; arrowYPts[0] = arrowPts[0].y;
              arrowXPts[1] = arrowPts[1].x; arrowYPts[1] = arrowPts[1].y;
              arrowXPts[2] = arrowPts[2].x; arrowYPts[2] = arrowPts[2].y;
              imG.fillPolygon(arrowXPts,arrowYPts,3);
          }            
      } else if (anyVectors && state.outputAminusB) {
          // Put (-ve) vector B at the end of A
          imG.setColor(state.blueColor);
          drawLine(imG,xValA,yValA,xValAminusB,yValAminusB);
          labelB_pt = getLabel_pt(xValA,yValA,xValAminusB,yValAminusB);
          imG.drawString("B",labelB_pt.x,labelB_pt.y);
          if (blueLineLength*2 > lineLengthThreshold) {  // Draw arrow head
              //if (xMid != xEnd || yMid != yEnd) {
              Point[] arrowPts = MyArrows.getDblArrowHeadPts(xValAminusB, yValAminusB,
                                                             xValA, yValA);
              int[] arrowXPts = new int[3];
              int[] arrowYPts = new int[3];
              arrowXPts[0] = arrowPts[0].x; arrowYPts[0] = arrowPts[0].y;
              arrowXPts[1] = arrowPts[1].x; arrowYPts[1] = arrowPts[1].y;
              arrowXPts[2] = arrowPts[2].x; arrowYPts[2] = arrowPts[2].y;
              imG.fillPolygon(arrowXPts,arrowYPts,3);
              //}
          }
          
          imG.setColor(state.greenColor);
          drawLine(imG,xMid,yMid,xValAminusB,yValAminusB);
          labelC_pt = getLabel_pt(xMid,yMid,xValAminusB,yValAminusB);
          imG.drawString("C",labelC_pt.x,labelC_pt.y);
          if (greenLineLength*2 > lineLengthThreshold) {  // Draw arrow head
              Point[] arrowPts = MyArrows.getDblArrowHeadPts(xValAminusB, yValAminusB,
                                                             xMid, yMid);
              int[] arrowXPts = new int[3];
              int[] arrowYPts = new int[3];
              arrowXPts[0] = arrowPts[0].x; arrowYPts[0] = arrowPts[0].y;
              arrowXPts[1] = arrowPts[1].x; arrowYPts[1] = arrowPts[1].y;
              arrowXPts[2] = arrowPts[2].x; arrowYPts[2] = arrowPts[2].y;
              imG.fillPolygon(arrowXPts,arrowYPts,3);
          }                    
      }
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                           RenderingHints.VALUE_ANTIALIAS_OFF);
  }
    

    private Point SAVEgetLabel_pt(int x0,int y0,int x1, int y1){
        Point pt = new Point();
        double x,y;
        double dx, dy;
        double theta;

        dx = (x1 - x0)/2;
        dy = (y0 - y1)/2;
        x = x0 + dx;
        y = y0 - dy;

        //System.out.println("x0,y0 = "+x0+","+y0+"  x1,y1="+x1+","+y1);
        //System.out.println("x,y = "+x+","+y);
        
        // Perpendicular line
        theta = Math.atan(-dx/dy);
        //System.out.println("theta = "+theta*180/Math.PI);
        //dx = 40*Math.sin(theta);
        //dy = 40*Math.cos(theta);
        dx = 40*Math.cos(theta);
        dy = 40*Math.sin(theta);
        pt.x = (int)(x+dx);
        pt.y = (int)(y-dy);
        //System.out.println("pt is: x="+pt.x+" y="+pt.y);
        return pt;
    }
    

    private Point getLabel_pt(int x0,int y0,int x1, int y1){
        Point pt = new Point();
        double x,y;
        double dx, dy;
        double theta;

        dx = (x1 - x0)/2;
        dy = (y0 - y1)/2;
        x = x0 + dx;
        y = y0 - dy;

        pt.x = (int)(x);
        pt.y = (int)(y);
        return pt;
    }



  public void update(Graphics g) { paint(g); }


  public void paint(Graphics g) {
    g.drawImage(im,0,0,this);
  }


  private void drawAxis(Graphics g) {
    // Draw x and y axis:
    g.setColor(state.axisColor);
    //drawLine(g,xMid,yBorder,xMid,yMax+yOffset); // the y-axis
    drawLine(g,xMid,yOffset,xMid,yMax+yBorder); // the y-axis


    //drawLine(g,xMid,yOffset,xMid,yMax+yBorder); // the y-axis
    drawLine(g,xBorder,yMid,xMax+xOffset,yMid); // the x-axis
    //MyArrows.drawDblUpArrow(g,xMid,yBorder);
    //MyArrows.drawDblDownArrow(g,xMid,yMax+yOffset);
    MyArrows.drawDblUpArrow(g,xMid,yOffset);
    MyArrows.drawDblDownArrow(g,xMid,yMax+yBorder);

    //MyArrows.drawDblUpArrow(g,xMid,yOffset);
    //MyArrows.drawDblDownArrow(g,xMid,yMax+yBorder);
    MyArrows.drawDblRightArrow(g,xMax+xOffset,yMid);
    MyArrows.drawDblLeftArrow(g,xBorder,yMid);
  }

  private void drawLine(Graphics g, int x1, int y1, int x2, int y2) {
      if (y1 != y2) {
          g.drawLine(x1-1,y1,x2-1,y2);  // NEW
          g.drawLine(x1,y1,x2,y2);
          g.drawLine(x1+1,y1,x2+1,y2);
      } else {
          g.drawLine(x1,y1-1,x2,y2-1);  // NEW
          g.drawLine(x1,y1,x2,y2);
          g.drawLine(x1,y1+1,x2,y2+1);
      }
  }

    private void drawPolyline(Graphics g, int[] xpts, int[] ypts, int len) {
        int x1, y1, x2, y2;
        for (int i = 0; i < (len-1); i++) {
            x1 = xpts[i]; y1 = ypts[i];
            x2 = xpts[i+1]; y2 = ypts[i+1];
            drawLine(g,x1,y1,x2,y2);
        }
    }


  private void drawAxisTicks(Graphics g) {
      //Graphics2D g2d = (Graphics2D)g;
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                           RenderingHints.VALUE_ANTIALIAS_ON);
    // Draw x and y axis:
    g.setColor(state.axisColor);

    for(int i=0;i<21;i+=2){
      int y = (int)(i*deltaY)+yOffset+yBorder;
      g.drawLine(xMid-(ticLength/2),y,xMid+(ticLength/2),y);
      int v = 10-i;
      //if (v == 5 || v == -5) {
      if (v != 0 && v != -1) {
        String vs = String.valueOf(v);
        g.drawString(vs,xMid-(ticLength/2)-fm.stringWidth(vs)-5,
                        y+(int)(0.5*fontHeight));
      }
      //}
    }
    //g.drawString("y",xMid+(ticLength/2),(int)(1.5*fontHeight)+yBorder-10);
    g.drawString("y",xMid+(ticLength/2),(int)(1.5*fontHeight)+yOffset-10);


    // On the x-axis:
    // dots per cm:
    int screenRes = (int) (getToolkit().getScreenResolution()/2.54);
    for(int i=0;i<21;i+=2){
        //int x = (int)((i+2)*screenRes) +xMin - 4;
      int x = (int)(i*deltaX)+xMin;
      if( x > xMax) break;
      int v = i-10;
      if (v != 0) {
          g.drawLine(x,yMid+(ticLength/2),x,yMid-(ticLength/2));
          String xs = String.valueOf(v);
          g.drawString(xs,x-(int)(0.5*fm.stringWidth(xs)),
                       yMid+ticLength+fontHeight);
      }
    }
    g.drawString("x",xMax+xBorder-fm.stringWidth("x"),yMid+fontHeight+5);

    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                         RenderingHints.VALUE_ANTIALIAS_OFF);

  }

    /*
  private void OLDdrawAxisTicks(Graphics g) {
      //Graphics2D g2d = (Graphics2D)g;
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                           RenderingHints.VALUE_ANTIALIAS_ON);
    // Draw x and y axis:
    g.setColor(state.axisColor);

    for(int i=0;i<11;i++){
      int y = (int)(i*deltaY)+yOffset+yBorder;
      g.drawLine(xMid-(ticLength/2),y,xMid+(ticLength/2),y);
      int v = 5-i;
      //if (v == 5 || v == -5) {
      if (v != 0 && v != -1) {
        String vs = String.valueOf(v);
        g.drawString(vs,xMid-(ticLength/2)-fm.stringWidth(vs)-5,
                        y+(int)(0.5*fontHeight));
      }
      //}
    }
    //g.drawString("y",xMid+(ticLength/2),(int)(1.5*fontHeight)+yBorder-10);
    g.drawString("y",xMid+(ticLength/2),(int)(1.5*fontHeight)+yOffset-10);


    // On the x-axis:
    // dots per cm:
    int screenRes = (int) (getToolkit().getScreenResolution()/2.54);
    for(int i=0;i<11;i++){
        //int x = (int)((i+2)*screenRes) +xMin - 4;
      int x = (int)(i*deltaX)+xMin;
      if( x > xMax) break;
      int v = i-5;
      if (v != 0) {
          g.drawLine(x,yMid+(ticLength/2),x,yMid-(ticLength/2));
          String xs = String.valueOf(v);
          g.drawString(xs,x-(int)(0.5*fm.stringWidth(xs)),
                       yMid+ticLength+fontHeight);
      }
    }
    g.drawString("x",xMax+xBorder-fm.stringWidth("x"),yMid+fontHeight+5);

    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                         RenderingHints.VALUE_ANTIALIAS_OFF);

  }
    */
}
