//GraphCanvas.java

import java.awt.*;

public class GraphCanvas extends Canvas{
    protected static final Color bgcolor = new Color(216,216,191);
    
    protected Image im;
    protected Graphics buf;
    protected int LeftMargin;
    protected int TopSep;
    protected int BottomSep;
    protected int RightMargin;
    protected int VPos = 0;
    protected String XLabel, YLabel, ZLabel, TITLE;
    protected String X1, X2, X3;
    protected String Y1, Y2, Y3;
    protected int NumPoints;
    protected double x[];
    protected double y[];
    protected double ymin, ymax;
    protected double xmin, xmax;
    protected int xx[];
    protected int yy[];
    protected double xRef;
    protected boolean Should_Plot_Zero_Line;
    protected boolean Should_Plot_Ref_Point;
    protected boolean IsDirectivity;
    protected boolean IsYRangeMinSet, IsYRangeMaxSet;
    protected boolean IsXRangeMinSet, IsXRangeMaxSet;
    protected boolean IsWZero;

    protected Font font1;
    protected Font font2;
    protected Font font3;
    
    NewGuide_State state; 
    
    public GraphCanvas(NewGuide_State state){
	super();
        this.state = state;
        
        LeftMargin = state.s40;
        TopSep = state.s40;
        BottomSep = state.s40;
        RightMargin = state.s40;
        
        font1 = new Font("SanSerif",Font.PLAIN,state.font11);
        font2 = new Font("SanSerif",Font.PLAIN,state.font12);
        font3 = new Font("SanSerif",Font.PLAIN,state.font13);
    
	setBackground(bgcolor);
	XLabel="x-axis";
	YLabel="y-axis";
	TITLE ="Unknown Title";
	Y1 = " ";
	Y2 = " ";
	Y3 = " ";
	X1 = " ";
	X2 = " ";
	X3 = " ";
	NumPoints = 1001;
	x = new double[NumPoints];
	y = new double[NumPoints];
	xx = new int[NumPoints];
	yy = new int[NumPoints];
	for(int i=0;i<NumPoints;i++){
	    x[i]=i;
	    y[i]=0.0;
	}
	IsYRangeMinSet=false;
	IsYRangeMaxSet=false;
	IsXRangeMinSet=false;
	IsXRangeMaxSet=false;
	IsWZero = false;
	
	xRef = 0.0;
	
	Should_Plot_Zero_Line = false;
	Should_Plot_Ref_Point = false;
	IsDirectivity = true;
    }
     
    @Override
    public void paint(Graphics g){
        if(im == null){
            im = createImage(getSize().width,getSize().height);
            buf = im.getGraphics();
            drawGraph(buf);
        }
        drawGraph(buf);
        g.drawImage(im,0,0,null);
    }

    //Addition to reduce flicker new routine
    @Override
    public void update(Graphics g){		// added to avoid clearing
            paint(g);
    }

    public void drawGraph(Graphics g){
        g.clearRect(0,0,getSize().width,getSize().height);
        drawAxis(g);
        if(Should_Plot_Zero_Line){plotZeroLine(g);}
        drawTitle(g);
        plotPoints(g);
        //plotRefPoint(g);
    }   

    protected void ignition(){
        int height=getSize().height;
        int width=getSize().width;
        if(!IsYRangeMinSet){ymin = MaestroA.getMin(y);}
        if(!IsYRangeMaxSet){ymax = MaestroA.getMax(y);}
        if(!IsXRangeMaxSet){xmax = x[x.length-1];}
        if(!IsXRangeMinSet){xmin = x[0];}
        if(IsYRangeMinSet || IsYRangeMaxSet){
            MaestroA.confiner(y,ymax,ymin);
        }
        if(IsXRangeMinSet || IsXRangeMaxSet){
            MaestroA.confiner(x,xmax,xmin);
        }

        if(ymin==ymax){ymax=2*ymin+1;}
        for(int i=0;i<xx.length;i++){
            xx[i]= (int)MaestroA.mapper(x[i],(double)(width-RightMargin),(double)(LeftMargin),xmax,xmin);
            yy[i]= (int)MaestroA.mapper(y[i],(double)TopSep,(double)(height-BottomSep),ymax,ymin);
        } 

        X1=""+MaestroA.rounder(x[0],3);
        X2=""+MaestroA.rounder(x[x.length-1],3);
        X3="|";
        Y1=""+MaestroA.rounder(ymin,3);
        Y2=""+MaestroA.rounder(ymax,3);
        Y3="_";
    }

    protected void plotZeroLine(Graphics g){
        if(ymin*ymax>0){return;}
        FontMetrics fm = g.getFontMetrics();
        g.setFont(font1);
        int yyy;
        yyy = (int)MaestroA.mapper(0.0,(double)TopSep,(double)(getSize().height-BottomSep),ymax,ymin);
        g.setColor(Color.red);
        g.drawLine(LeftMargin,yyy,getSize().width-RightMargin,yyy);
        g.drawString("0",LeftMargin-fm.stringWidth("0"),yyy+fm.getHeight()/3);
    }

    public synchronized void plotZeroLine(boolean Should_Plot_Zero_Line){
        this.Should_Plot_Zero_Line = Should_Plot_Zero_Line;
    }

    protected void plotRefPoint(Graphics g){

        int myX=0, myY=0, i;
        i = (int)((NumPoints-1)*(xRef-x[0])/(x[x.length-1]-x[0]));
        if(i<0) {i = 0;}
        if(i>=NumPoints){i=NumPoints-1;}
        myX = (int)MaestroA.mapper(xRef,(double)(getSize().width-RightMargin),(double)(LeftMargin),xmax,xmin);
        if(y[i]>0.0){
            myY = (int)MaestroA.mapper(y[i],(double)TopSep,(double)(getSize().height-BottomSep),ymax,ymin);
        }
        else{
            myY =getSize().height-BottomSep ;
        }

        g.setColor(Color.white);
        g.drawLine(myX,myY+state.s2,myX,getSize().height-BottomSep+state.s1);
        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);

        g.setColor(Color.red);
        MaestroG.fillCircle(myX,myY,state.s6,g);
        g.setColor(Color.white);

        MaestroG.drawCircle(myX,myY,state.s6,g);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
    }

    public synchronized void plotRefPoint(boolean Should_Plot_Ref_Point){
        this.Should_Plot_Ref_Point = Should_Plot_Ref_Point;
    }

    protected void plotPoints(Graphics g){
        ignition();
        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);

        if(IsDirectivity){g.setColor(Color.yellow);}else{g.setColor(Color.green);}
        //g.drawPolyline(xx,yy,xx.length);
        for(int i=1;i<NumPoints-1;i++){
                g.drawLine(xx[i],yy[i],xx[i+1],yy[i+1]);
        }
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);

    }

    protected void drawTitle(Graphics g){
        Graphics2D g2d = (Graphics2D)g;
        FontMetrics fm = g.getFontMetrics();
        g.setColor(Color.white);
        int ylevel = getSize().height-state.s15;
        int xlevel = (2*getSize().width/3-fm.stringWidth("ra"));

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        
        MaestroG.subscripterSymbol3B("r","a","",g,state.font16,xlevel,ylevel);
        
        g.setFont(font2);

        g.drawString(YLabel,LeftMargin-state.s33,getSize().height/2);
        g.drawString(ZLabel,LeftMargin-state.s26,getSize().height/2+state.s4);
        g.setColor(Color.white);
        g.drawString(TITLE,state.s20,2*TopSep/5);
        
        g.setColor(Color.yellow);
        g.setFont(font1);
        g.drawString(X1,LeftMargin-state.s6,ylevel-state.s5);
        g.drawString(Y1,Math.max(state.s4,LeftMargin-fm.stringWidth(Y1))-state.s7,getSize().height-BottomSep+state.s5);
        g.drawString(Y2,Math.max(state.s4,LeftMargin-fm.stringWidth(Y2))-state.s7,TopSep+state.s4);
        
        g.setFont(font3);
        MaestroG.subscripterSymbol4B("a","","",g,state.font13,getSize().width-RightMargin-fm.stringWidth("a")/2,ylevel-state.s5);
        
        // horizontal tick marks.
        g.drawLine(LeftMargin,getSize().height-BottomSep,LeftMargin,getSize().height-BottomSep+state.s6);
        g.drawLine(LeftMargin+(getSize().width-RightMargin-LeftMargin)/2,getSize().height-BottomSep,
                   LeftMargin+(getSize().width-RightMargin-LeftMargin)/2,getSize().height-BottomSep+state.s6);
        g.drawLine(getSize().width-RightMargin,getSize().height-BottomSep,
                   getSize().width-RightMargin,getSize().height-BottomSep+state.s6);
        g.drawLine(LeftMargin-state.s6,getSize().height-BottomSep,LeftMargin,getSize().height-BottomSep);
        g.drawLine(LeftMargin,TopSep,LeftMargin-state.s6,TopSep);
    }

    protected void drawAxis(Graphics g){
        VPos = getSize().height-BottomSep;
        g.setColor(Color.white);
        
        //Vertical Axis
        g.drawLine(LeftMargin,TopSep-state.s10,LeftMargin,getSize().height-BottomSep);
        //MaestroG.drawArrow(LeftMargin,TopSep-state.s6,5,g);
        MaestroG.drawArrowScaled(LeftMargin,TopSep-state.s6,1,state.sfactor,g);
        //Horizontal Axis
        g.drawLine(LeftMargin,VPos,getSize().width-RightMargin+state.s10,VPos);
        //MaestroG.drawArrow(getSize().width-RightMargin+state.s8,VPos,7,g);
        MaestroG.drawArrowScaled(getSize().width-RightMargin+state.s8,VPos,3,state.sfactor,g);
    }

    public final synchronized void setXLabel(String XLabel){
        this.XLabel=XLabel;
    }

    public final synchronized void setYLabel(String YLabel){
        this.YLabel=YLabel;
    } 

    public final synchronized void setTitle(String TITLE){
        this.TITLE = TITLE;
    }

    public void setWZero(boolean IsWZero){
        this.IsWZero = IsWZero;
    }

    public final synchronized void setLabels(String TITLE, String YLabel, String XLabel, String ZLabel){
        this.TITLE=TITLE;
        this.YLabel = YLabel;
        this.XLabel = XLabel;
        this.ZLabel = ZLabel;
    }

    public void plot(double[] xdata, double[] ydata, boolean IsDirectivity){
        this.IsDirectivity = IsDirectivity;
        if(NumPoints != xdata.length){
            NumPoints = xdata.length;
            x = new double[NumPoints];
            y = new double[NumPoints];
            xx = new int[NumPoints];
            yy = new int[NumPoints];
        }
        for(int i = 0; i < NumPoints; i++){
            x[i] = xdata[i];
            y[i] = ydata[i];
        }
        repaint();
    }

    public void setYRange(double ymax, double ymin){
        IsYRangeMaxSet=true;
        IsYRangeMinSet=true;
        this.ymax=ymax;
        this.ymin=ymin;
    }

    public void setYRangeMin(double ymin){
        IsYRangeMinSet=true;
        this.ymin=ymin;
    }

    public void setYRangeMax(double ymax){
        IsYRangeMaxSet=true;
        this.ymax=ymax;
    }

    public void setAuto(){
        IsYRangeMaxSet=false;
        IsYRangeMinSet=false;
        IsXRangeMaxSet=false;
        IsXRangeMinSet=false;
    }

    public synchronized void setRefPoint(double xRef){
        this.xRef = xRef;
    }
}