package visualization;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JPanel;

import visualization.components.*;
import background.systemVariables.ArrayVariable;
import background.systemVariables.SimpleVariable;
import background.systemVariables.TableVariable;


public class VisualPanel extends JPanel {

	private static final long serialVersionUID = 6590843126022881438L;
	private ArrayList<VisualComponent> components;
	private Graphics2D graphics;	
	public boolean[][] tableOfViz;
	public boolean tableIsSet = false;
	public Boolean showGrid = true;
	
	public String defaultSimpleComponentType = "simple";
	public String defaultArrayComponentType = "multiple";
	public String defaultTableComponentType = "table";

	public String defaultDesignType = "default";
	public String defaultColorScheme = "";
	public int defaultMultiplicator = 1;

	public int yBoxSize = 70;
	public int xBoxSize = 70;
	
	private int ySizeInBoxes;
	private int xSizeInBoxes;
		
	public VisualPanel() {
		graphics = (Graphics2D) this.getGraphics();
		reset();
	}
	
	public void reset() {
		components = new ArrayList<VisualComponent>();		
		tableIsSet = false;
		resetDefaults();
	}
	
	public void refresh() {
		revalidate();
		repaint(); 		
	}

	public void resetDefaults() {
		defaultSimpleComponentType = "simple";
		defaultArrayComponentType = "multiple";
		defaultTableComponentType = "table";
		defaultDesignType = "default";		
		defaultColorScheme = "";
		defaultMultiplicator = 1;
	}
	
	public void setDefaults(HashMap<String, String> params) {
		if (params.containsKey("varVisTech")) {defaultSimpleComponentType = params.get("varVisTech");}
		if (params.containsKey("arrayVisTech")) {defaultArrayComponentType = params.get("arrayVisTech");}
		if (params.containsKey("duoArrayVisTech")) {defaultTableComponentType = params.get("duoArrayVisTech");}
		
		if (params.containsKey("design")) {defaultDesignType = params.get("design");}		
		if (params.containsKey("color")) {defaultColorScheme = params.get("color");}
		if (params.containsKey("multiplicator")) {defaultMultiplicator = Integer.parseInt(params.get("multiplicator"));}		
	}
	
	public void setTableOfViz() {
		ySizeInBoxes = (int) Math.floor(getWidth() / this.xBoxSize);
		xSizeInBoxes = (int) Math.floor(getHeight() / this.yBoxSize);		
			
		tableOfViz = new boolean[(int) ySizeInBoxes+1][(int) xSizeInBoxes+1];
		for (int j=1; j<=xSizeInBoxes; j++)
			for (int i=1; i<=ySizeInBoxes; i++) tableOfViz[i][j]=false;		
				
		tableIsSet = true;		
		}
	
		
	public void getGrid() {
		graphics.setColor(new Color(240, 240, 240));
		for (int j=1; j<=Math.floor(getHeight() / this.yBoxSize); j++) graphics.drawLine(10, j*yBoxSize, getWidth()-10, j*yBoxSize);		
		for (int i=1; i<=Math.floor(getWidth() / this.xBoxSize); i++) graphics.drawLine(i*xBoxSize,10, i*xBoxSize, getHeight()-10);
		
		graphics.setColor(Color.BLACK);
	}
	
	public void getNumbers() {
		graphics.setColor(new Color(240, 240, 240));
		graphics.setFont(new Font("Arial", Font.BOLD, 17));

		for (int i=1; i<Math.floor(getWidth() / this.xBoxSize); i++) graphics.drawString(Integer.toString(i), Math.round((i+0.5)*xBoxSize-5-5*Math.floor(i/10)), 10+Math.round(yBoxSize/2));
		for (int j=1; j<Math.floor(getHeight() / this.yBoxSize)-1; j++) graphics.drawString(Integer.toString(j), Math.round(xBoxSize/2), Math.round((j+0.5)*yBoxSize));

		for (int i=1; i<Math.floor(getWidth() / this.xBoxSize); i++) graphics.drawString(Integer.toString(i), Math.round((i+0.5)*xBoxSize-5-5*Math.floor(i/10)), getHeight()-Math.round(yBoxSize/2));
		for (int j=1; j<Math.floor(getHeight() / this.yBoxSize)-1; j++) graphics.drawString(Integer.toString(j), getWidth()-10-Math.round(xBoxSize/2), Math.round((j+0.5)*yBoxSize));
		graphics.setColor(Color.BLACK);
	}
	
		
	public void paintComponent(Graphics g) {
		super.paintComponent(g);		
		graphics = (Graphics2D) g;
		
		if (!tableIsSet) setTableOfViz();
		
		if (showGrid) { getGrid(); getNumbers(); }
				
		for (VisualComponent vc: components) {
			vc.draw(graphics);
		}
    }


	public void addVisualComponent(SimpleVariable var, ArrayVariable array, TableVariable table, HashMap<String, String> params) {
		String type = "";
		String design = "";
		String color = "";
		int multi = defaultMultiplicator;
		if (params.keySet().contains("type")) {type = params.get("type");}
		if (params.keySet().contains("design")) { design = params.get("design"); }
		if (params.keySet().contains("color")) { color = params.get("color"); }
		if (params.keySet().contains("multiplicator")) { multi = Integer.parseInt(params.get("multiplicator")); }
		
		VisualComponent vc;
		String varT;
		if (var!=null) { vc = createVisualComponent(var, type); varT = var.getType();} 
		else if (table!=null) { vc = createVisualComponent(table, type); varT = table.getType();} 
		else { vc = createVisualComponent(array, type); varT = array.getType();}
		postprocessVisualComponent(vc, design, multi, varT, color);
		
		if (params.keySet().contains("x") && params.keySet().contains("y")) {		
			vc.moveTo(Integer.parseInt(params.get("x"))*xBoxSize, Integer.parseInt(params.get("y"))*yBoxSize);
			if (vc!=null) components.add(vc);		
			addToTableOfViz(vc, Integer.parseInt(params.get("x")), Integer.parseInt(params.get("y")));
		} else {
			int[] coordinates = getNextEmptySpace(widthInBoxes(vc), heightInBoxes(vc));				
			vc.moveTo(coordinates[0]*xBoxSize, coordinates[1]*yBoxSize);
			if (vc!=null) components.add(vc);		
			addToTableOfViz(vc, coordinates[0], coordinates[1]);
		}	
	}

	
	public void addToTableOfViz(VisualComponent vc, int x, int y) {
		for (int j=y; j<=y+heightInBoxes(vc); j++)
			for (int i=x; i<=x+widthInBoxes(vc); i++) tableOfViz[i][j]=true;
		
		
		/*for (int j=1; j<=xSizeInBoxes-1; j++) {
			for (int i=1; i<=ySizeInBoxes-1; i++) 
				if (tableOfViz[i][j]) System.out.print("1"); else System.out.print("0");
			System.out.println();
		}*/
	}
	
	public double widthInBoxes(VisualComponent vc) { return Math.ceil(vc.width / this.xBoxSize); }
	public double heightInBoxes(VisualComponent vc) { return Math.ceil(vc.height / this.yBoxSize); }	
	
	public VisualComponent createVisualComponent(SimpleVariable variable, String type) {
		VisualComponent vc = null;
		switch (type) {
			case "simple": vc = new SimpleValueComponent(variable); break; 
			default: vc = createVisualComponent(variable, defaultSimpleComponentType); break;
		}				
		return vc;
	}

	public VisualComponent createVisualComponent(TableVariable variable, String type) {
		VisualComponent vc = null;
		switch (type) {
			case "table": vc = new TableComponent(variable); break; 
			default: vc = createVisualComponent(variable, defaultTableComponentType); break;
		}				
		return vc;
	}
	
	public VisualComponent createVisualComponent(ArrayVariable variable, String type) {
		VisualComponent vc = null;
		switch (type) {
			case "multiple": vc = new MultipleValueComponent(variable); break;
			case "turned": vc = new TurnedMultipleValueComponent(variable); break;
			default: vc = createVisualComponent(variable, defaultArrayComponentType); break;
		}			
		return vc;
	}
	
	public void postprocessVisualComponent(VisualComponent vc, String designString, int multiplicator, String varType, String color) {
		String c = defaultColorScheme;
		if (!color.isEmpty()) c = color;
		
		if (!designString.isEmpty()) vc.setDesign(designString, c);
		else vc.setDesign(defaultDesignType, c);
		
		if (multiplicator>0) vc.setMultiplicator(multiplicator);
		else if (varType.equals("string")) vc.setMultiplicator(3);
		
		vc.initSize(graphics);		
	}

	
	
	
	public void export(String picType, String defFileName) {
        JFileChooser fileChooser = new JFileChooser();
        File defaultFile = new File(defFileName);             
        fileChooser.setSelectedFile(defaultFile);
        
        File file = new File(defFileName);
        if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
        	file = fileChooser.getSelectedFile();
        }
    	
    	Dimension size = this.getSize();
        BufferedImage image = (BufferedImage) this.createImage(size.width, size.height);
        Graphics g = image.getGraphics();
        this.paintComponent(g);
        g.dispose();
        try { ImageIO.write(image, picType, file); }
        catch (IOException e) { e.printStackTrace(); }
	}
	 
	public int[] getNextEmptySpace(double widthIB, double heightIB) {
		int[] coordinates = new int[2];
		coordinates[0] = 0;
		coordinates[1] = 0;
				
		for (int j=1; j<=xSizeInBoxes-1; j++) {
			for (int i=1; i<=ySizeInBoxes-1; i++) 
				if (!tableOfViz[i][j]) {
					Boolean found = true;
					if ((j+heightIB>xSizeInBoxes-1) || (i+widthIB>ySizeInBoxes-1)) found = false;
					else for (int b=j; b<=j+heightIB; b++) {
						for (int a=i; a<=i+widthIB; a++) if (tableOfViz[a][b]) {found = false; break;}
						if (!found) break;
					}
					
					if (found) {
						coordinates[0] = i;
						coordinates[1] = j;
						return coordinates;
					}
				}
		}
		return coordinates;
	}

	
	
	

	
}
