import java.awt.*;
import java.util.*;
import java.lang.*;
//import maestro.lib.graphics.*;
//import maestro.lib.math.*;

public class GraphCanvasD extends Canvas{
    protected static final Color bgcolor = Color.white;
    protected Image im;
    protected Graphics buf;
    protected int LeftMargin = 60;
    protected int TopSep = 40;
    protected int BottomSep = 40;
    protected int RightMargin = 40;
    protected int VPos=0;
    protected String XLabel, YLabel, TITLE;
    protected String X1, X2, X3;
    protected String Y1, Y2, Y3;
    protected int NumPoints;
    protected double x[];
    protected double y[], y2[];
    protected double ymin, ymax;
    protected double xmin, xmax;
    protected double angle;
    protected int xx[];
    protected int yy[];
    protected int yy2[];
    protected int xminima[];
    protected int xmaxima[];
    protected double xRef;
    protected boolean Should_Plot_Zero_Line;
    protected boolean Should_Plot_Ref_Point;
    protected boolean IsYRangeMinSet, IsYRangeMaxSet;
    protected boolean IsXRangeMinSet, IsXRangeMaxSet, IsH;
    protected Font font1;
    protected Font fontYLabel;
    protected Color ColorHeader = Color.magenta.darker();
    protected Color ColorHeader2 = Color.blue.darker();
    protected double sfactor = 1.25;
    protected int s11;
    protected int s1, s12, s4, s5, s6, s7, s10, s15, s20, s25, s100, s200, s565, s1000;
    
    public GraphCanvasD(){
	super();
	setBackground(bgcolor);
        
        LeftMargin = (int)Math.ceil(60*sfactor);
        TopSep = (int)Math.ceil(40*sfactor);
        BottomSep = (int)Math.ceil(40*sfactor);
        RightMargin = (int)Math.ceil(40*sfactor);
        s1 = (int)Math.ceil(1*sfactor);
        s4 = (int)Math.ceil(4*sfactor);
        s5 = (int)Math.ceil(5*sfactor);
        s6 = (int)Math.ceil(6*sfactor);
        s7 = (int)Math.ceil(7*sfactor);
        s10 = (int)Math.ceil(10*sfactor);
        s11 = (int)Math.ceil(11*sfactor);
        s12 = (int)Math.ceil(12*sfactor);
        s15 = (int)Math.ceil(15*sfactor);
        s20 = (int)Math.ceil(20*sfactor);
        s25 = (int)Math.ceil(25*sfactor);
        s100 = (int)Math.ceil(100*sfactor);
        s200 = (int)Math.ceil(200*sfactor);
        s565 = (int)Math.ceil(565*sfactor);
        s1000 = (int)Math.ceil(1000*sfactor);
        
        font1 = new Font("SanSerif",Font.PLAIN,s11);
        fontYLabel = new Font("Serif",Font.PLAIN,s11);
    
	XLabel="x-axis";
	YLabel="y-axis";
	TITLE ="Unknown Title";
	Y1 = " ";
	Y2 = " ";
	Y3 = " ";
	X1 = " ";
	X2 = " ";
	X3 = " ";
	NumPoints = s565;
        //NumPoints = s100;
        
	x = new double[NumPoints];
	y = new double[NumPoints];
        y2 = new double[NumPoints];
	xx = new int[NumPoints];
	yy = new int[NumPoints];
        yy2 = new int[NumPoints];
        xminima = new int[15];
        xmaxima = new int[15];
	IsH = true;
        
        for(int i=0;i<NumPoints;i++){
	    x[i]=i;
	    y[i]=0.0;
            y2[i]=0.0;
	}
        
        for(int i=0;i<15;i++){
	    xminima[i]=0;
	    xmaxima[i]=0;
	}
        
	IsYRangeMinSet=false;
	IsYRangeMaxSet=false;
	IsXRangeMinSet=false;
	IsXRangeMaxSet=false;
	
	xRef = 0.0;
	angle = 0.0;
	Should_Plot_Zero_Line = false;
	Should_Plot_Ref_Point = false;
    }
     
    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);
    }

    public void drawGraph(Graphics g){
            g.clearRect(0,0,getSize().width,getSize().height);
            drawAxis(g);
            g.draw3DRect(0,0,getSize().width-1,getSize().height-1,false);

            plotPoints(g);
            //plotRefPoint(g);
            drawTitle(g);
            if(ymin >= -0.000001){// || ymax <= 0.00001){

            }
            else{
                if(Should_Plot_Zero_Line){plotZeroLine(g);}
            }
    }

    protected void ignition(){
        int height=getSize().height;
        int width=getSize().width;
        //if(!IsYRangeMinSet){ymin = MaestroA.getMin(y);}
        //if(!IsYRangeMaxSet){ymax = MaestroA.getMax(y);}

        //ymin = MaestroA.getMin(y);
        //ymin = MaestroA.getMax(y);

            double min;
            min = 0.0;
            for(int i=1;i<x.length;i++){
                if(min>y[i]) {min = y[i];}
            }	
            ymin = min;

            double max;
            max = 0.0;
            for(int i=1;i<x.length;i++){
                if(max<y[i]) {max = y[i];}
            }	
            ymax = max;

            if(Math.abs(ymax)< 1.0e-8 && Math.abs(ymin)<1.0e-8 ){
                ymax = 1.0;
                ymin = 0.0;
            }

        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);
        }


        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);
            yy2[i]= (int)MaestroA.mapper(y2[i],(double)TopSep,(double)(height-BottomSep),ymax,ymin);
        } 

        //System.out.println(ymin+"   "+ymax);

        X1=""+MaestroA.rounder(x[0],3)+"\u00ba";
        X2=""+MaestroA.rounder(x[x.length-1]/100,3)+"\u00ba";
        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 yy;
        yy = (int)MaestroA.mapper(0.0,(double)TopSep,(double)(getSize().height-BottomSep),ymax,ymin);
        g.setColor(Color.red);
        g.drawLine(LeftMargin,yy,getSize().width-RightMargin,yy);
        if( ymax <= 0.00001){}
        else{g.drawString("0",LeftMargin-fm.stringWidth("0"),yy+fm.getHeight()/2);
        }
        g.setColor(Color.black);
    }

    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, myY0=0, i;
        i = (int)(NumPoints*xRef*10/(x[x.length-1]-x[0]));
        if(i<0) {i = 0;}
        if(i>=NumPoints){i=NumPoints-1;}
        myX = (int)MaestroA.mapper(xRef*10,(double)(getSize().width-RightMargin),(double)(LeftMargin),xmax,xmin);
        myY = (int)MaestroA.mapper(y[i],(double)TopSep,(double)(getSize().height-BottomSep),ymax,ymin);
        myY0 = (int)MaestroA.mapper(0.0,(double)TopSep,(double)(getSize().height-BottomSep),ymax,ymin);
        g.setColor(Color.red);

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

        g.setColor(Color.white);
        g.drawLine(myX,myY,myX,myY0);
        g.setColor(Color.red);
        MaestroG.fillCircle(myX,myY,s6,g);
        g.setColor(Color.black);
        MaestroG.drawCircle(myX,myY,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);

        int min_count = 0;
        int max_count = 0;

        // Due to discretization - Enforce Zeroes  for better plotting
        for(int i = 1; i < (xx.length-1); i++){
            if(y[i-1]>y[i] && y[i+1]>y[i]){
                yy[i] = getSize().height-BottomSep;
                xminima[min_count] = xx[i];
                min_count+=1;
            }
            if(y2[i-1]>y2[i] && y2[i+1]>y2[i]){yy2[i] = getSize().height-BottomSep;}
        }
        //=================================================================
        if(min_count > 0 && min_count < 7){
            if(angle == 0){
                MaestroG.subscripterSerif("-\u03bb","1","/","2",g, s12, xminima[min_count-1]-s15,getSize().height-s10);
            }
            else{
                if(IsH){
                    MaestroG.subscripterSerif("-\u03bb","1z","/","4",g, s12, xminima[min_count-1]-s20,getSize().height-s10);
                }
                else{
                    MaestroG.subscripterSerif("-\u03bb","1z","/","2",g, s12, xminima[min_count-1]-s20,getSize().height-s10);
                }
            }
        }
        if(min_count > 1){
            if(angle == 0){
                MaestroG.subscripterSerif("-\u03bb","1","","",g, s12, xminima[min_count-2]-s10,getSize().height-s10);
            }
            else{
                if(IsH){
                    MaestroG.subscripterSerif("-3\u03bb","1z","/","4",g, s12, xminima[min_count-2]-s20,getSize().height-s10);
                }
                else{
                    MaestroG.subscripterSerif("-\u03bb","1z","","",g, s12, xminima[min_count-2]-s10,getSize().height-s10);
                }
            }
        }
        if(min_count > 2 && min_count < 7){
            if(angle == 0){
                MaestroG.subscripterSerif("-3\u03bb","1","/","2",g, s12, xminima[min_count-3]-s20,getSize().height-s10);
            }
            else{
                if(IsH){
                    MaestroG.subscripterSerif("-5\u03bb","1z","/","4",g, s12, xminima[min_count-3]-s20,getSize().height-s10);
                }
                else{
                    MaestroG.subscripterSerif("-3\u03bb","1z","/","2",g, s12, xminima[min_count-3]-s20,getSize().height-s10);
                }
            }
        }
        if(min_count > 3){
            if(angle == 0){
                MaestroG.subscripterSerif("-2\u03bb","1","","",g, s12, xminima[min_count-4]-s15,getSize().height-s10);
            }
            else{
                if(IsH){
                    MaestroG.subscripterSerif("-7\u03bb","1z","/","4",g, s12, xminima[min_count-4]-s20,getSize().height-s10);
                }
                else{
                    MaestroG.subscripterSerif("-2\u03bb","1z","","",g, s12, xminima[min_count-4]-s15,getSize().height-s10);
                }
            }
        }
        if(min_count > 4 && min_count < 7){
            if(angle == 0){
                MaestroG.subscripterSerif("-5\u03bb","1","/","2",g, s12, xminima[min_count-5]-s20,getSize().height-s10);
            }
            else{
                if(IsH){
                    MaestroG.subscripterSerif("-9\u03bb","1z","/","4",g, s12, xminima[min_count-5]-s20,getSize().height-s10);
                }
                else{
                    MaestroG.subscripterSerif("-5\u03bb","1z","/","2",g, s12, xminima[min_count-5]-s20,getSize().height-s10);
                }
            }
        }
        if(min_count > 5){
            if(angle == 0){
                MaestroG.subscripterSerif("-3\u03bb","1","","",g, s12, xminima[min_count-6]-s15,getSize().height-s10);
            }
            else{
                if(IsH){
                    MaestroG.subscripterSerif("-11\u03bb","1z","/","4",g, s12, xminima[min_count-6]-s25,getSize().height-s10);
                }
                else{
                    MaestroG.subscripterSerif("-3\u03bb","1z","","",g, s12, xminima[min_count-6]-s15,getSize().height-s10);
                }
            }
        }
        if(min_count > 6 && min_count < 7){
            if(angle == 0){
                MaestroG.subscripterSerif("-7\u03bb","1","/","2",g, s12, xminima[min_count-7]-s20,getSize().height-s10);
            }
            else{
                if(IsH){
                    MaestroG.subscripterSerif("-13\u03bb","1z","/","4",g, s12, xminima[min_count-7]-s25,getSize().height-s10);
                }
                else{
                    MaestroG.subscripterSerif("-7\u03bb","1z","/","2",g, s12, xminima[min_count-7]-s20,getSize().height-s10);
                }
            }
        }
        if(min_count > 7){
            if(angle == 0){
                MaestroG.subscripterSerif("-4\u03bb","1","","",g, s12, xminima[min_count-8]-s15,getSize().height-s10);
            }
            else{
                if(IsH){
                    MaestroG.subscripterSerif("-15\u03bb","1z","/","4",g, s12, xminima[min_count-8]-s25,getSize().height-s10);
                }
                else{
                    MaestroG.subscripterSerif("-4\u03bb","1z","","",g, s12, xminima[min_count-8]-s15,getSize().height-s10);
                }
            }
        }
        if(min_count > 9){
            if(angle == 0){
                MaestroG.subscripterSerif("-5\u03bb","1","","",g, s12, xminima[min_count-10]-s15,getSize().height-s10);
            }
            else{
                if(IsH){
                    MaestroG.subscripterSerif("-17\u03bb","1z","/","4",g, s12, xminima[min_count-10]-s25,getSize().height-s10);
                }
                else{
                    MaestroG.subscripterSerif("-5\u03bb","1z","","",g, s12, xminima[min_count-10]-s15,getSize().height-s10);
                }
            }
        }
        if(min_count > 11){
            if(angle == 0){
                MaestroG.subscripterSerif("-6\u03bb","1","","",g, s12, xminima[min_count-12]-s15,getSize().height-s10);
            }
            else{
                if(IsH){
                    MaestroG.subscripterSerif("-19\u03bb","1z","/","4",g, s12, xminima[min_count-12]-s25,getSize().height-s10);
                }
                else{
                    MaestroG.subscripterSerif("-6\u03bb","1z","","",g, s12, xminima[min_count-12]-s15,getSize().height-s10);
                }
            }
        }
        if(min_count > 13){
            if(angle == 0){
                MaestroG.subscripterSerif("-7\u03bb","1","","",g, s12, xminima[min_count-14]-s15,getSize().height-s10);
            }
            else{
                if(IsH){
                    MaestroG.subscripterSerif("-21\u03bb","1z","/","4",g, s12, xminima[min_count-14]-s25,getSize().height-s10);
                }
                else{
                    MaestroG.subscripterSerif("-7\u03bb","1z","","",g, s12, xminima[min_count-14]-s15,getSize().height-s10);
                }
            }
        }        
        //==========================================================================
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        
        for(int ii = 0; ii < min_count; ii++){
            g.setColor(Color.black);
            g.drawLine(xminima[ii],getSize().height-BottomSep,xminima[ii],getSize().height-BottomSep+s10);
        }
        //g.setColor(ColorHeader);
        //g.drawPolyline(xx,yy,xx.length);
        for(int i = 0; i < xx.length-1; i++){
                MaestroG.drawLineThick(g,xx[i],yy[i],xx[i+1],yy[i+1],1,ColorHeader);
        }
        if(angle == 0.0){
            for(int i = 0; i < xx.length-1; i++){
                MaestroG.drawLineThick(g,xx[i],yy2[i],xx[i+1],yy2[i+1],1,ColorHeader2);
            }
        }
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
    }

    protected void drawTitle(Graphics g){
        Color medium2Color = new Color(200,220,220);
        FontMetrics fm = g.getFontMetrics();

        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        g.setColor(ColorHeader);
        g.setFont(fontYLabel);
        g.drawString(YLabel,s7,s20);

        g.setFont(font1);
        g.setColor(Color.blue.darker());
        g.drawString(XLabel,(getSize().width-fm.stringWidth(XLabel))-s20,getSize().height-BottomSep+3*fm.getHeight()/2);
        //g.setColor(Color.red.darker());
        //g.drawString("interface",3*(getSize().width-fm.stringWidth("Interface")-RightMargin-LeftMargin)/4+LeftMargin,getSize().height-BottomSep+3*fm.getHeight()/2);
        g.drawString("0",3*(getSize().width-RightMargin-LeftMargin)/4+LeftMargin-fm.stringWidth("0")/2,getSize().height-BottomSep+3*fm.getHeight()/2);
        g.setColor(ColorHeader);
        g.drawString(TITLE,(getSize().width-fm.stringWidth(TITLE))/2,TopSep/2);

        g.setColor(Color.black);
        // interface
        int x_interface;
        x_interface = LeftMargin + 3*(getSize().width-RightMargin-LeftMargin)/4;
        g.drawLine(getSize().width-RightMargin,getSize().height-BottomSep,getSize().width-RightMargin,getSize().height-BottomSep+5);

        g.setColor(medium2Color);
        g.fillRect(x_interface,TopSep,getSize().width-x_interface-RightMargin,getSize().height-BottomSep-TopSep);
        MaestroG.drawLineThick(g,x_interface,TopSep,x_interface,getSize().height-BottomSep+s5,s1,Color.black);

        g.setColor(Color.black);
        g.drawString(Y1,Math.max(4,LeftMargin-fm.stringWidth(Y1))-s12,getSize().height-BottomSep+s5);
        //g.drawString(Y2,Math.max(4,LeftMargin-fm.stringWidth(Y2))-s12,TopSep+s4);

        MaestroG.SansNosansSymb2(""+Y2,"","",g,s11,Math.max(4,LeftMargin-fm.stringWidth(Y2))-s12,TopSep+s4);
        if(angle==0.0){MaestroG.SansNosansSymb2("2.0/","","\u03b7",g,s11,Math.max(4,LeftMargin-fm.stringWidth(Y2))-s25,TopSep+4+(getSize().height-TopSep-BottomSep)/4);}
        g.setColor(Color.blue);
        if(angle==0.0){
            g.setFont(fontYLabel);
            g.drawString("| H | / Eo",s7,TopSep-s20+(getSize().height-TopSep-BottomSep)/4);
        }    
        g.drawLine(LeftMargin-5,TopSep,LeftMargin,TopSep);
        g.drawLine(LeftMargin-5,TopSep+(getSize().height-TopSep-BottomSep)/4,LeftMargin,TopSep+(getSize().height-TopSep-BottomSep)/4);

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
        g.setColor(Color.black);
    }

    protected void drawAxis(Graphics g){
        VPos = getSize().height-BottomSep;
        g.setColor(Color.black);
        //Vertical Axis
        g.drawLine(LeftMargin,TopSep/2,LeftMargin,getSize().height-BottomSep+5);
        MaestroG.drawArrow(LeftMargin,TopSep/2,5,g);
        //Horizontal Axis
        g.drawLine(LeftMargin-s5,VPos,getSize().width-RightMargin+s10,VPos);
        MaestroG.drawArrow(getSize().width-RightMargin+s10,VPos,7,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, Color colore, Color colore2){
        //this.TITLE = TITLE;
        this.ColorHeader = colore;
        this.ColorHeader2 = colore2;
    }

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

    public synchronized void setSfactor(double sfactor){
        this.sfactor = sfactor;
        this.LeftMargin = (int)Math.ceil(60*sfactor);
        this.TopSep = (int)Math.ceil(40*sfactor);
        this.BottomSep = (int)Math.ceil(40*sfactor);
        this.RightMargin = (int)Math.ceil(40*sfactor);
        this.s11 = (int)Math.ceil(11*sfactor);
    }

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

    public void setNoRange(boolean decide){
        this.IsYRangeMaxSet=decide;
        this.IsYRangeMinSet=decide;
    }

    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;
    }

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