package Division.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.text.DecimalFormat;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.io.File;

import javax.swing.*;
import javax.imageio.ImageIO;

import Division.Claimer;

/**
 * Show the connected vessels such as the talmud calculates. 
 * It makes no sense to use this panel to display anything else but the Talmud division.
 * 
 * @author robbert
 *
 */

public class TalmudConnectedVesselsDisplay extends JPanel{
    private BufferedImage buffer;
    private ReentrantLock lock;

	private static final long serialVersionUID = -2362070428066200583L;

	/** the total vessel height of each player */
	private static final int TOTAL_VESSEL_HEIGHT = 250;
	
	/** the width of each vessel */
	private static final int VESSEL_WIDTH = 120;
	
	/** the horizontal space between each vesel */
	private static final int HORIZONTAL_VESSEL_PADDING = 50;
	
	/** the internal padding in the paintable area */
	private static final int INTERNAL_PADDING = 10;
	
	/** the minimal space between the top vessel and the bottom one */
	private static final int VESSEL_SEPARATION = 20; 
	
	private List<Claimer> claimers;
	
	public TalmudConnectedVesselsDisplay(List<Claimer> claimers){
		this.claimers = claimers;
        lock=new ReentrantLock();
		setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder()));
        addMouseListener(new Mouse(this));
	}
	
	@Override
	public Dimension getPreferredSize(){
		return new Dimension((VESSEL_WIDTH + HORIZONTAL_VESSEL_PADDING) * claimers.size(), TOTAL_VESSEL_HEIGHT + INTERNAL_PADDING + 105);
	}
	
	@Override
	public void paintComponent(Graphics g2){
        if (lock.isLocked())return;
        lock.lock();
        try{
            // clear the whole area

            if (buffer==null||buffer.getWidth()!=getWidth()||buffer.getHeight()!=getHeight())buffer=new BufferedImage(getWidth(),getHeight(),BufferedImage.TYPE_INT_RGB);
            Graphics g=buffer.getGraphics();
            super.paintComponent(g);
            double scale = ((claimers.get(claimers.size() - 1).getClaimed() / 2) / ((TOTAL_VESSEL_HEIGHT - VESSEL_SEPARATION) / 2));

            // the vessel height of the current player
            int vesselHeight;
            int bottomFilled, topFilled;

            int bottomFillLimit = -1;
            int topFillLimit  = -1;

            for(Claimer p : claimers){
                if(p.getReceived() > p.getClaimed() / 2){
                    topFillLimit = (int) ((p.getClaimed() - p.getReceived()) / scale);
                    break;
                }
            }

            for(int i = 0; i < claimers.size(); i++){
                vesselHeight = (int)(claimers.get(i).getClaimed() / 2 / scale);

                // check bottom vessels
                if(claimers.get(i).getClaimed() / 2 > claimers.get(i).getReceived()){
                    // check if we already have a fill limit for the bottom
                    if(bottomFillLimit == -1){
                        // create a new limit
                        bottomFillLimit = (int) (vesselHeight * (claimers.get(i).getReceived() / (claimers.get(i).getClaimed() / 2)));
                    }
                    bottomFilled = bottomFillLimit;
                }
                else{
                    bottomFilled = vesselHeight;
                }

                if(topFillLimit >= 0 && vesselHeight > topFillLimit){
                    topFilled = topFillLimit;
                }
                else{
                    topFilled = -1;
                }

                paintVessel(g, INTERNAL_PADDING + VESSEL_WIDTH*i + HORIZONTAL_VESSEL_PADDING * i, INTERNAL_PADDING, vesselHeight, bottomFilled, topFilled);
                paintPlayerInfo(g, i, INTERNAL_PADDING + VESSEL_WIDTH * i + HORIZONTAL_VESSEL_PADDING * i, INTERNAL_PADDING + TOTAL_VESSEL_HEIGHT + 20);
            }
            g.setColor(Color.BLACK);
            // connect the vessels
            g.drawLine(INTERNAL_PADDING,
                        INTERNAL_PADDING + TOTAL_VESSEL_HEIGHT,
                        INTERNAL_PADDING + (VESSEL_WIDTH + HORIZONTAL_VESSEL_PADDING) * (claimers.size() - 1) + VESSEL_WIDTH,
                        INTERNAL_PADDING + TOTAL_VESSEL_HEIGHT);
            g2.drawImage(buffer,0,0,null);
        }
        catch(Exception e){}

        lock.unlock();
	}
	
	/**
	 * Paint the vessels for this player
	 * @param g the graphics to paint on
	 * @param x the x insets
	 * @param y the y insets
	 * @param vesselHeight the height of one half of the vessels
	 * @param bottomFilled size in pixels to fill in the bottom of this vessel
	 * @param topFilled size in pixels from what point on to fill the remaining vessel (if -1 the top container is empty
	 */
	private void paintVessel(Graphics g, int x, int y, int vesselHeight, int bottomFilled, int topFilled){
        g.setColor(Color.BLACK);
		g.drawRect(x, y,VESSEL_WIDTH, vesselHeight);
		g.drawLine(x + VESSEL_WIDTH / 2, y + vesselHeight, x + VESSEL_WIDTH / 2, y + TOTAL_VESSEL_HEIGHT - vesselHeight );
		g.drawRect(x, y + (TOTAL_VESSEL_HEIGHT - vesselHeight), VESSEL_WIDTH, vesselHeight);
		
		// fill the vessels
		g.setColor(new Color(0, 191, 255));
		
		// the bottom
		g.fillRect(x + 1,
				   y + (TOTAL_VESSEL_HEIGHT - vesselHeight) + 1 + (vesselHeight - bottomFilled),
				   VESSEL_WIDTH -1,
				   bottomFilled);
		
		// the top
		if(topFilled != -1){
			g.fillRect(x + 1,
					   y + topFilled + 1,
					   VESSEL_WIDTH - 1,
					   vesselHeight - topFilled -1
					   );
		}
		
		// reset the color to black
		g.setColor(Color.BLACK);
	}
	
	/**
	 * Paint the player information (the claimed and the got)
	 * @param g the graphics to paint on
	 * @param playerId the id of the player
	 * @param x the x insets
	 * @param y the y insets
	 */
	private void paintPlayerInfo(Graphics g, int playerId, int x, int y){
		DecimalFormat df = new DecimalFormat("#.###");
		g.drawString("Player " + playerId + ":", x , y);
		g.drawString("Claimed: " + df.format(claimers.get(playerId).getClaimed()), x, y + 14);
		g.drawString("Received: " + df.format(claimers.get(playerId).getReceived()), x, y + 28);
		g.drawString("Received in %: " + df.format((claimers.get(playerId).getReceived() / claimers.get(playerId).getClaimed()) * 100), x, y + 42);
		g.drawString("Loss: " + df.format(claimers.get(playerId).getClaimed() - claimers.get(playerId).getReceived()), x, y + 56);
		g.drawString("Loss in %: " + df.format(((claimers.get(playerId).getClaimed() - claimers.get(playerId).getReceived()) / claimers.get(playerId).getClaimed()) * 100), x, y + 70);
	}

    class Mouse implements MouseListener {
        private JComponent frame;
        Mouse(JComponent 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){}
    }

}
