import java.io.*;
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;
//import javax.swing.*;

// Module 1 Exercise 2
public class Mod2Plot extends Panel implements MouseListener, Runnable {

   Mod2State state;

   Image image;
   int[] xListGreen;
   int[] yListGreen;
   int[] xListRed;
   int[][] yListRed;
   int cols;
   int currentRow = 0;
   int yMin, yMax, xMin, xMax, yMid;
   int yBorder, yOffset, yExtra;

   Image main;
   int mainWidth, mainHeight;
   Graphics imG;

   //MyAnimationTimer timer = new MyAnimationTimer(this, 25); // was 50

   private Thread tron;
   public boolean IsTronRunning;
   public boolean ThreadStarted;
   private int SleepTime = 25;  // was 50

   public static int delayInMsec = 25;
   public static double numberSecondsPerClick = delayInMsec*0.001;
   public static double numberClicksPerSecond = 1.0/numberSecondsPerClick;
   public static double numberSecondsPerWave = 1.0/Mod2State.REDfrequency;
   static int rows = (int) (numberSecondsPerWave/numberSecondsPerClick);

    // private static final Color bgColor = Color.lightGray;
    private static final Color bgColor = new Color(236,236,236);
   Color lineColor;

   boolean anyGreenWave = false;

   double deltaY, deltaX;
   FontMetrics fm;
   int fontHeight,ticLength;

   static Font boldFont = new Font("Serif",Font.BOLD, 18);
   //static Font smallFont = new Font("Serif",Font.PLAIN, 14);
   String red1, red2, red3;
   String green1, green2, green3;
   int endingX;

   DecimalFormat dec = new DecimalFormat("#.###");

   int numberSeconds = 0, numberMinutes = 0, numberClicks = 0;
   Label stopWatch = new Label("0:00  ");
   long totalNumberSeconds = 0;

   static double oneOver30 = 1.0/30.0;  // 30 "clicks" to each second
   static double oneOver180 = 1.0/180;

   public StartStopClock ssc;
    public Button reset;

    Graphics2D g2d;


   public synchronized void initialPlot() {
       state.reset();
       ssc.clockcanvas.setStatus(0,state.NTime,state.dtime);
       ssc.clockcanvas.reset();

       //main = createImage(mainWidth, mainHeight);

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

        main = gc.createCompatibleImage(mainWidth, mainHeight);
        imG = main.getGraphics();

        g2d = (Graphics2D)imG;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_ON);

       paintImage(imG);
       paintAnimationStuff(imG);
   }
   public Mod2Plot(Mod2State state) {
       super();
       this.state = state;
       IsTronRunning = false;
       ThreadStarted = false;
       setLayout(null);
       setBackground(bgColor);

       getRedText();
       getPointsRedwave();
       lineColor = Mod1State.line1Color;

       ssc  = new StartStopClock();
       ssc.button1.setEnabled(true);
       ssc.button2.setEnabled(true);
       add(ssc);

       //start stop clock
       ssc.setBounds(340,10,170,60);
       ssc.button1.addMouseListener(this);
       ssc.button2.addMouseListener(this);

       reset = new Button("Reset");
       reset.setFont(TheFonts.sanSerif12);
       add(reset);
       reset.setBounds(650,10,70,20);

       IsTronRunning = false;
       ThreadStarted = false;


       // Get font sizes:
       setBackground(bgColor);
       setFont(TheFonts.sanSerif12);
       fm = getFontMetrics(getFont());
       fontHeight = fm.getHeight();
       ticLength  = fm.stringWidth("w")/2; // just a wide letter.

       yExtra = 220;  // was 180

       mainWidth = xMax+yBorder;
       mainHeight = yMax+yBorder+yOffset+yExtra;
   }


    public void reset() {
        IsTronRunning = false;
        ssc.button2.setEnabled(false);
        ssc.button1.setEnabled(true);
        ssc.IsStop = true;
        ssc.repaint();
        anyGreenWave = false;
        paintImage(imG);
        paintAnimationStuff(imG);
        repaint();
    }


   public void mouseClicked(MouseEvent evt){mouseHandler(evt);}
   public void mouseEntered(MouseEvent evt){;}
   public void mouseExited(MouseEvent evt){;}
   public void mousePressed(MouseEvent evt){;}
   public void mouseReleased(MouseEvent evt){;}

   private synchronized void mouseHandler(MouseEvent evt){
       if(evt.getSource() == ssc.button1){
           IsTronRunning = true;
           if(!ThreadStarted){
               start();
               ThreadStarted = true;
               ssc.button1.setEnabled(false);
               ssc.button2.setEnabled(true);
           }
           else{
               ssc.button1.setEnabled(false);
               ssc.button2.setEnabled(true);
               notify();
           }
           //c1.setEnabled(false);
           repaint();
       }
       if(evt.getSource() == ssc.button2){
           IsTronRunning = false;
           ssc.button2.setEnabled(false);
           ssc.button1.setEnabled(true);
           //c1.setEnabled(true);
           repaint();
       }
   }


   public void start(){
       if(tron == null){
           tron = new Thread(this);
           tron.start();
       }
   }


   public void run(){

       while(true){
           if(IsTronRunning){
               animate();
               ssc.clockcanvas.increment();
               state.increment();
           }
           try{
               Thread.sleep(SleepTime);
               synchronized(this){
                   while(!IsTronRunning){wait();}
               }
           }
           catch(InterruptedException e){e.printStackTrace();}
       }
   }



   private synchronized void getRedText() {
       red1 = "f = 0.5 [Hz]";
       red2 = Character.toString((char)0x03bb)+" = 2 [cm]";
       red3 = STR.BOLDPHI+Character.toString((char)0x03d5)+
           STR.ENDBOLDPHI+STR.SUB+"0"+STR.ENDSUB+" = 0"+
           Character.toString((char)0x00b0);
   }


   public synchronized void getGreenText() {
       green1 = "f = "+dec.format(state.frequency)+" [Hz]";
       green2 = Character.toString((char)0x03bb)+" = "+
           dec.format(state.lambda)+" [cm]";
       green3 = STR.BOLDPHI+Character.toString((char)0x03d5)+
           STR.ENDBOLDPHI+STR.SUB+"0"+STR.ENDSUB+" = "+
           Integer.toString(state.phi)+Character.toString((char)0x00b0);
   }



 private synchronized void getPointsRedwave() {
   // 72 points for one full cycle
   int ncycles=3;
   int npts = ncycles*72;
   double xPoint, yPoint, xInRadians, xInCm;
   xListRed = new int[npts];
   //yListRed = new int[npts];
   yListRed = new int[rows][npts];

   // create a wave with y in (-100,100), x in (0,600)
   double timeIncrement = numberSecondsPerWave/(double)rows;
   for (int n = 0; n < rows; n++) {
     double xShift = (double)n*timeIncrement;
     double xShiftInRadians = Math.PI*xShift;

     for (int i = 0; i < xListRed.length; i++) {
         xPoint = ((double)i*5)/180;
         // each x point == 5 degrees; 180 degrees = pi(radians)
         xInRadians = xPoint * 3.1415927;
         xInCm = i/36.0;  // 72 points per 2 cm
         yPoint = Math.cos(xShiftInRadians - (Math.PI*xInCm));
         xListRed[i] = (int) (xPoint*100); // scale x so 360 degress spans 200
         yListRed[n][i] = (int) (yPoint*100); // (5 Volts) y from -100 to 100
     }
   }

   // where to draw on the screen:
   //yOffset = 10;
   yOffset = 80;  // was 40
   yBorder = 15;
   //xMin = 35; xMax = (200 * ncycles) + xMin;
   //xMin = 85; xMax = (200 * ncycles) + xMin;
   xMin = 125; xMax = (200 * ncycles) + xMin;
   yMin = yBorder+yOffset;
   yMax = 200+yBorder+yOffset;
   yMid = (int)(0.5*(yMax+yMin));

   // NOW push the sine wave DOWN on the Y-axis AND FLIP, so that the sine
   //  wave appears in the middle of the screen, rather that at the TOP
   for (int i = 0; i < rows; i++)
     for (int j = 0; j < npts; j++) {
         //yListRed[i][j] += yMid;
         yListRed[i][j] = yMid - yListRed[i][j];
       if (i == 0) {
           xListRed[j] += xMin;
       }
     }
 }


   public synchronized void checkGetPointsGreenwave () {
       // change to input values; only need to get new greenWave points
       //  if not currently animated (otherwise will be called automatically
       //   by the animate thread.
       //if (start_stop.getLabel().equals("Start Animation"))
       if (!IsTronRunning)
           getPointsGreenwave();
       else anyGreenWave = true;
   }


 public synchronized void getPointsGreenwave() {
   // Scale the animation time with a "normal" frequency of 0.5 (red line's)
     double time = (double)totalNumberSeconds +
         (numberClicks*numberSecondsPerClick);
   // 72 points for one full cycle
   int ncycles=3;
   int npts = ncycles*72;
   double xPoint, yPoint, xInRadians, xInCm;

   cols = npts;

   xListGreen = new int[cols];
   yListGreen = new int[cols];

   anyGreenWave = false;

   // create a wave with y in (-100,100), x in (0,600)
   for (int i = 0; i < cols; i++) {
       xPoint = ((double)i*5)*oneOver180;
       // each x point == 5 degrees; 180 degrees = pi(radians)
       xInRadians = xPoint * Math.PI;
       xInCm = i/36.0;  // 72 points per 2 cm
       yPoint = Math.cos((2.0*Math.PI*state.frequency*time) -
                         (2.0*Math.PI*xInCm/state.lambda))
           *Math.exp(-state.alpha*xInCm);
       yListGreen[i] = (int) (yPoint*100); // (5 Volts) y from -100 to 100
       xListGreen[i] = (int) ((xPoint)*100); // scale x so 360 degress spans 200
       //yPoint = Math.exp(-0.053*xInRadians);
   }

   // NOW push the sine wave DOWN on the Y-axis, AND FLIP, so that the sine
   //  wave appears in the middle of the screen, rather that at the TOP
   // AND shift the X-axis over by xMin to make room for the vertical
   //  axis line.
   for (int i = 0; i < cols; i++) {
       //yListGreen[i] += yMid;
       yListGreen[i] = yMid - yListGreen[i];
       xListGreen[i] += xMin;
   }
   anyGreenWave = true;
   //if (start_stop.getLabel().equals("Start Animation")) {
   if (!IsTronRunning) {
       paintImage(imG);
       paintAnimationStuff(imG);
       repaint();
   }
 }


 private synchronized void paintImage(Graphics g) {
     //g.clearRect(0,0,mainWidth,mainHeight);
      g.setColor(bgColor);
      g.fillRect(0,0,mainWidth,mainHeight);

   // Set up stuff for drawing tick marks:
   deltaY = (yMax - yMin)/10.0;
   deltaX = (xMax - xMin)/12.0;
   // Get font sizes:
   fm = getFontMetrics(getFont());
   fontHeight = fm.getHeight();
   ticLength  = fm.stringWidth("w"); // just a wide letter.

   // Fill the background
   g.setColor(bgColor);
   g.fillRect(0,0,xMax,yMax+yBorder+yOffset);

   // Draw x and y axis:
   drawAxis(g);

   //Graphics2D g2d = (Graphics2D)g;
   //g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
   //                     RenderingHints.VALUE_ANTIALIAS_ON);     
   // Draw axis tick marks:
   drawAxisTicks(g);

   g.setColor(state.redWaveColor);
   g.setFont(TheFonts.bold14); //new

   endingX = STR.displayString(red1,g,240,50);
   endingX = STR.displayString(red2,g,240,70);
   endingX = STR.displayString(red3,g,240,90);

   if (anyGreenWave) {
       g.setColor(state.greenWaveColor);
       endingX = STR.displayString(green1,g,540,50);
       endingX = STR.displayString(green2,g,540,70);
       endingX = STR.displayString(green3,g,540,90);
   }
 }

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

 public synchronized void paintAnimationStuff(Graphics g) {
     //Graphics2D g2d = (Graphics2D)g;
     //g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
     //                    RenderingHints.VALUE_ANTIALIAS_OFF);     
        
      g2d.setStroke(new BasicStroke(3,BasicStroke.CAP_ROUND,
                                    BasicStroke.JOIN_ROUND));

     //g.drawImage(image,0,0,this);
   g.setColor(state.redWaveColor);
   //drawPolyline(g,xListRed,yListRed[currentRow],xListRed.length);
   g2d.drawPolyline(xListRed,yListRed[currentRow],xListRed.length);
   if (anyGreenWave) {
       g.setColor(state.greenWaveColor);
       //drawPolyline(g,xListGreen,yListGreen,cols);
       g2d.drawPolyline(xListGreen,yListGreen,cols);
   }

    g2d.setStroke(new BasicStroke(1));

 }

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

 private void drawAxis(Graphics g) {
      Graphics2D g2d = (Graphics2D)g;
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                           RenderingHints.VALUE_ANTIALIAS_OFF);     
   // Draw x and y axis:
   g.setColor(state.axisColor);
   drawLine(g,xMin,yOffset,xMin,yMax+yBorder); // the y-axis
   drawLine(g,xMin,yMid,xMax+yBorder,yMid); // the x-axis
   MyArrows.drawDblUpArrow(g,xMin,yOffset);
   MyArrows.drawDblDownArrow(g,xMin,yMax+yBorder);
   MyArrows.drawDblRightArrow(g,xMax+yBorder,yMid);
 }


 private void drawPolyline(Graphics g, int[] x, int[] y, int len) {
      Graphics2D g2d = (Graphics2D)g;
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                           RenderingHints.VALUE_ANTIALIAS_OFF);     
   for (int i = 0; i < (len-1); i++)
     drawLine(g,x[i],y[i],x[i+1],y[i+1]);
 }

 private void drawLine(Graphics g, int x1, int y1, int x2, int y2) {
   int deltaX = Math.abs(x1-x2);
   int deltaY = Math.abs(y1-y2);
   if (deltaY > deltaX) {
     g.drawLine(x1,y1,x2,y2);
     g.drawLine(x1+1,y1,x2+1,y2);
   } else {
     g.drawLine(x1,y1,x2,y2);
     g.drawLine(x1,y1+1,x2,y2+1);
   }
 }

 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);
   g.setFont(TheFonts.sanSerif12);

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


   // On the x-axis:
   // dots per cm:
   //int screenRes = (int) (getToolkit().getScreenResolution()/2.54);
   for(int i=1;i<13;i++){
     int x = (int)(i*deltaX) +xMin;
     if( x > xMax) break;
     g.drawLine(x,yMid+ticLength,x,yMid);
     //drawVerticalDottedLine(g,x);
     //String xs = String.valueOf(i*0.5);
     String xs = dec.format(i*0.5);
     g.drawString(xs,x-(int)(0.5*fm.stringWidth(xs)),
                     yMid+ticLength+fontHeight);
   }
   g.drawString("[cm]",xMax+yBorder-fm.stringWidth("[cm]"),
                yMid+(2*fontHeight)+5);
 }

 private void drawHorizontalDottedLine(Graphics g, int y) {
     //Graphics2D g2d = (Graphics2D)g;
     //g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
     //                     RenderingHints.VALUE_ANTIALIAS_OFF);     
   Color oldColor = g.getColor();
   g.setColor(Color.gray);
   Font oldFont = g.getFont();
   g.setFont(TheFonts.sanSerif10);
   for (int i = xMin; i <= xMax; i+=5)
     g.drawString(".",i,y);
   g.setColor(oldColor);
   g.setFont(oldFont);
 }

 private void drawVerticalDottedLine(Graphics g, int x) {
     //Graphics2D g2d = (Graphics2D)g;
     //g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
     //                     RenderingHints.VALUE_ANTIALIAS_OFF);     
   Color oldColor = g.getColor();
   g.setColor(Color.gray);
   Font oldFont = g.getFont();
   g.setFont(TheFonts.sanSerif10);
   for (int i = yMin; i <= yMax+2; i+=5)
     g.drawString(".",x,i);
   g.setColor(oldColor);
   g.setFont(oldFont);
 }

 public synchronized void animate() {
   // NEW
   numberClicks++;
   if (numberClicks >= numberClicksPerSecond) {
     numberClicks = 0;
     incrementStopwatch();
     //paintStopwatch(imG);
   }
   //repaint();  should i do this?

   // "shift" the x-axis to the left:
   currentRow++;
   if (currentRow >= rows) currentRow = 0;

   if (anyGreenWave) getPointsGreenwave();
   paintImage(imG);

   paintAnimationStuff(imG);
   repaint();
 }


 private synchronized void incrementStopwatch() {
     totalNumberSeconds++;
   numberSeconds++;
   if (numberSeconds > 59) {
     numberSeconds = 0;
     numberMinutes++;
     if (numberMinutes > 9)
       numberMinutes = 0;
   }
 }

 private synchronized void paintStopwatch(Graphics g) {
   String display = String.valueOf(numberMinutes)+":";
   String seconds = String.valueOf(numberSeconds);
   if (seconds.length() == 1) seconds = "0"+seconds;
   display += seconds;
   stopWatch.setText(display);
 }

}
