package Common;

import javax.swing.*;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;
import java.io.File;

/**
 * Author: Max Buegler - max@sarstech.com
 * Date: Sep 28, 2009
 * Time: 11:41:57 PM
 */
public class GraphFrame extends JFrame {
    private final Color[] colors=new Color[]{Color.RED,Color.GREEN,Color.BLUE, Color.PINK,Color.ORANGE,Color.BLACK,Color.MAGENTA,Color.CYAN,Color.YELLOW,Color.DARK_GRAY};
    private ArrayList<Data> data;
    private int steps;
    private BufferedImage buffer;
    private String title;
    private ReentrantLock lock;
    public GraphFrame(String title, int steps){
        super(title+" (Right click to save)");
        this.lock=new ReentrantLock();
        super.setSize(600,300);
        super.setVisible(true);
        this.title=title;
        //super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.data=new ArrayList<Data>();
        this.steps=steps;
        super.addMouseListener(new Mouse(this));

    }
    public void addData(double[] d, double divisor){
        double[] nd=new double[d.length];
        for (int x=0;x<d.length;x++)nd[x]=d[x]/divisor;
        data.add(new Data(nd));
        repaint();
    }

    class Mouse implements MouseListener {
        private JFrame frame;
        Mouse(JFrame frame) {
            this.frame = frame;
        }
        public void mouseClicked(MouseEvent e) {
            if (e.getButton()==MouseEvent.BUTTON3){
                lock.lock();
                try{
                    final JFileChooser fc = new JFileChooser();
                    fc.setName("Save Graph");
                    int retVal = fc.showSaveDialog(frame);
                    if (retVal==JFileChooser.APPROVE_OPTION){
                        File f=fc.getSelectedFile();
                        if (f.exists()){
                            retVal=JOptionPane.showConfirmDialog(frame,"Overwrite "+f.getName()+"?");
                            if (retVal!=JOptionPane.OK_OPTION)return;
                        }
                        int i=f.getName().lastIndexOf('.');
                        if (i>0){
                            String format=f.getName().substring(i+1);
                            ImageIO.write(buffer,format,f);
                        }
                        else{
                            ImageIO.write(buffer,"bmp",f);
                        }
                    }
                }
                catch(Exception ex){
                    JOptionPane.showMessageDialog(frame,"Could not save Image.");
                }
                lock.unlock();
                repaint();
            }
        }
        public void mousePressed(MouseEvent e){}
        public void mouseReleased(MouseEvent e){}
        public void mouseEntered(MouseEvent e){}
        public void mouseExited(MouseEvent e){}
    }

    public void addData(double[] d){
        data.add(new Data(d));
    }
    public void paint(Graphics g2){
        if (lock==null||lock.isLocked())return;
        lock.lock();
        try{
            if (buffer==null||buffer.getWidth()!=getWidth()||buffer.getHeight()!=getHeight()){
                buffer=new BufferedImage(getWidth(),getHeight(),BufferedImage.TYPE_BYTE_INDEXED);
            }
            Graphics g=buffer.getGraphics();
            int h=getHeight()-100;
            int w=getWidth()-50;
            g.setColor(Color.WHITE);
            g.fillRect(0,0,getWidth(),getHeight());
            h-=10;
            g.setColor(Color.BLACK);
            g.drawRect(35,43,w,h+4);
            FontMetrics fM=g.getFontMetrics();
            int fh=fM.getHeight();
            int fx=(int)(getWidth()-fM.getStringBounds(title,g).getWidth())/2;
            g.drawString(title,fx,40);
            if (data!=null&&data.size()>0){
                int cw=w/(data.get(0).getData().length+1);
                int ty=getHeight()-15;
                for (int x=0;x<data.get(0).getData().length;x++){



                    g.setColor(Color.BLACK);
                    String t="Action "+(x+1);
                    int tx=25+(x+1)*cw-(int)((fM.getStringBounds(t,g).getWidth()+40)/2);
                    g.drawString(t,tx,ty);

                    g.setColor(colors[x]);
                    tx-=30;
                    int by=ty-fh-((20-fh)/2);
                    g.fillRect(tx,by,25,25);
                    g.setColor(Color.BLACK);
                    g.drawRect(tx,by,25,25);
                }
            }
            g.setColor(Color.BLACK);
            int cw=w/10;
            int ch=h/10;
            for (int x=0;x<=10;x++){
                int tx=35+x*cw;
                g.drawLine(tx,h+45,tx,h+52);
                String t=(x*steps/10)+"";
                tx=tx-(int)(fM.getStringBounds(t,g).getWidth()/2);
                if (tx+fM.getStringBounds(t,g).getWidth()>getWidth())
                    tx=(int)(getWidth()-fM.getStringBounds(t,g).getWidth()-5);
                g.drawString(t,tx,h+55+fh);
                int ty=45+x*ch;
                g.drawLine(32,ty,37,ty);
                t=((10-x)/10.0)+"";
                ty+=fh/2;
                tx=30-(int)fM.getStringBounds(t,g).getWidth();
                g.drawString(t,tx,ty);
            }
            double pw=(double)w/steps;
            if (data!=null){
                for (int d=1;d<data.size();d++){
                    int x1=(int)(35+(d-1)*pw);
                    int x2=(int)(35+d*pw);
                    for (int x=0;x<data.get(d).getData().length;x++){
                        int y1=(int)(45+(1-data.get(d-1).getData()[x])*h);
                        int y2=(int)(45+(1-data.get(d).getData()[x])*h);
                        g.setColor(colors[x]);
                        g.drawLine(x1,y1,x2,y2);
                    }
                }
            }
            g2.drawImage(buffer,0,0,null);
        }
        catch(Exception ex){}
        lock.unlock();
    }
    class Data{
        private double[] data;
        public Data(double[] data) {
            this.data = data;
        }

        public double[] getData() {
            return data;
        }
    }
}
