package com.zooracle.view.swing;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;

import javax.imageio.ImageIO;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;

import com.zooracle.model.ColorRange;
import com.zooracle.model.ColorRangePreset;
import com.zooracle.model.ColorRangeYBT;
import com.zooracle.model.MetaData;

import tools.OpenCVUtils;

public class PhotoEditorPanel extends JPanel
{

	private int eW = 720;
	private int eH = 720;
	private static int width = 768;
	private static int height = 720;
	private int margin = 80;

	private BufferedImage currentImage;
	private MetaData currentMetaData;
	public static String moduleTitle = "Image Editor";
	private BufferedImage selectedImage;

	private boolean debugLoad = false;

	private BufferedImage imageBuffer;
	private Graphics2D imageBufferGraphics;
	private int ow;
	private int oh;
	private int ol;
	private int os;
	private int shortSide;
	private int zoom = 100;
	private int angle = 0;
	private int posX = width / 2;
	private int posY = height / 2;

	private int imgOffsetX = 0;
	private int imgOffsetY = 0;
	private int oldImgOffsetX = 0;
	private int oldImgOffsetY = 0;

	private int fw = 300;
	private int fh = 500;
	private int fm = 75;

	private int oldX = 0;
	private int oldY = 0;

	private int hl = 0;
	private int sl = 0;
	private int vl = 0;
	private int hh = 0;
	private int sh = 0;
	private int vh = 0;

	private boolean dragging = false;
	protected int mouseButton;
	protected int oldZoom;
	protected int oldAngle;
	private AffineTransform standardTransform;
	private int zw;
	private int zh;
	protected int oldPosX;
	protected int oldPosY;
	private Color bgcolor = new Color(150, 190, 160);
	protected boolean selected;

	private boolean copyMade = false;
	private String currentFile = "images/bt/2006/Ballertasche2006_1.JPG";

	private JPanel sidePanel;

	private boolean shiftDown = false;

	private File zooFile;
	private boolean listOpened = false;
	public int contrast;

	private Font font = new Font(Font.MONOSPACED, Font.PLAIN, 20);
	public boolean controlDown;
	private PhotoEditorToolPanel colorParameterWindow;
	private boolean blur;
	private ImageEditorKeyListener keyListener;

	private HashMap<Integer, Integer> listID;

	private ColorRangePreset colorRangePreset = new ColorRangeYBT();
	private boolean unsavedChange = false;

	public PhotoEditorPanel(ZooracleContentPanel zooracleContentPanel)
	{
		keyListener = new ImageEditorKeyListener();
//		this.addKeyListener(keyListener);
		this.setLayout(new BorderLayout());

		this.setBackground(bgcolor.darker());
		this.width = eW - margin;
		this.height = eH - margin;
		this.posX = width / 2;
		this.posY = height / 2;
		System.out.println("w:" + width);
		System.out.println("h:" + height);
		this.shortSide = Math.min(width, height);

		updateImageBuffer(width, height);

		this.addMouseListener(new MouseListener()
		{
			public void mouseReleased(MouseEvent arg0)
			{
				dragging = false;
			}

			public void mousePressed(MouseEvent e)
			{
				requestFocus();
				mouseButton = e.getButton();
				dragging = true;
				oldX = e.getX();
				oldY = e.getY();
				oldZoom = zoom;
				oldAngle = angle;
				oldPosX = posX;
				oldPosY = posY;
				oldImgOffsetX = imgOffsetX;
				oldImgOffsetY = imgOffsetY;
			}

			public void mouseExited(MouseEvent arg0)
			{
			}

			public void mouseEntered(MouseEvent arg0)
			{
			}

			public void mouseClicked(MouseEvent arg0)
			{
			}
		});

		this.addMouseMotionListener(new MouseMotionListener()
		{
			public void mouseMoved(MouseEvent e)
			{
			}

			public void mouseDragged(MouseEvent e)
			{
				if (selected)
					return;
				if (dragging)
				{
					if (shiftDown)
					{
						updateOffset(oldImgOffsetX - (oldX - e.getX()), oldImgOffsetY - (oldY - e.getY()));
						repaint();
					} else
					{
						if (mouseButton == MouseEvent.BUTTON3 || controlDown)
						{
							updateZoomAndAngle(oldZoom - (oldY - e.getY()), oldAngle - (oldX - e.getX()));
							repaint();
						} else if (mouseButton == MouseEvent.BUTTON1)
						{
							updatePos(oldPosX - (oldX - e.getX()), oldPosY - (oldY - e.getY()));
							repaint();
						}
					}
					unsavedChange = true;
				}
			}
		});
		zooracleContentPanel.getMainWindow().addKeyListener(keyListener);
		this.addKeyListener(keyListener);
		

		colorParameterWindow = new PhotoEditorToolPanel(this);
		this.add(colorParameterWindow,BorderLayout.EAST);
		
		
		setFocusable(true);
		this.requestFocus();

	}

	protected void updateOffset(int x, int y)
	{
		imgOffsetX = x;
		imgOffsetY = y;
	}

	protected void updatePos(int x, int y)
	{
		posX = x;
		posY = y;

		if (posX < fw / 2)
			posX = fw / 2;
		if (posY < fh / 2)
			posY = fh / 2;

		if (posX > shortSide - fw / 2 - 1)
			posX = shortSide - fw / 2 - 1;
		if (posY > shortSide - fh / 2 - 1)
			posY = shortSide - fh / 2 - 1;

	}

	protected void updateZoomAndAngle(int z, int a)
	{
		zoom = z;
		angle = a;
		if (zoom < 10)
			zoom = 10;
		if (zoom > 330)
			zoom = 330;
		zw = (int) (ow * (double) zoom / 100d);
		zh = (int) (oh * (double) zoom / 100d);
	}

	public void updateImageBuffer(int width, int height)
	{
		this.imageBuffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		this.imageBufferGraphics = (Graphics2D) imageBuffer.getGraphics();

		RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		this.imageBufferGraphics.setFont(font);

		imageBufferGraphics.setRenderingHints(rh);
		standardTransform = imageBufferGraphics.getTransform();
	}

	public boolean setCurrentImage(String imageString)
	{
		File file = new File(imageString);
		System.out.println(imageString);
		try
		{
			currentImage = ImageIO.read(file);
		} catch (Exception e)
		{
			e.printStackTrace();
			this.currentImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
			return false;
		}

		this.ow = this.currentImage.getWidth();
		this.oh = this.currentImage.getHeight();
		this.ol = Math.max(this.ow, this.oh);
		this.os = Math.min(this.ow, this.oh);

		zoom = (int) (((double) shortSide / (double) ow) * 100);
		zw = (int) (ow * (double) zoom / 100d);
		zh = (int) (oh * (double) zoom / 100d);

		return true;

	}

	@Override
	public void paint(Graphics g)
	{
		super.paint(g);

		this.imageBufferGraphics.setColor(bgcolor);
		this.imageBufferGraphics.fillRect(0, 0, this.ow, this.oh);

		if (this.currentMetaData == null)
			return;

		AffineTransform transform = new AffineTransform();
		transform.translate(-zw / 2 + imgOffsetX, -zh / 2 + imgOffsetY);
		transform.rotate(Math.toRadians(angle), shortSide / 2 + zw / 2, shortSide / 2 + zh / 2);
		this.imageBufferGraphics.transform(transform);
		this.imageBufferGraphics.drawImage(currentImage, shortSide / 2, shortSide / 2, zw, zh, null);
		this.imageBufferGraphics.setTransform(standardTransform);

		if (!this.selected)
		{
			this.imageBufferGraphics.setColor(new Color(0, 0, 220));
			this.imageBufferGraphics.setStroke(new BasicStroke(2f));
			this.imageBufferGraphics.drawRect(posX - fw / 2, posY - fh / 2, fw, fh);
			this.imageBufferGraphics.setColor(new Color(0, 122, 220));
			this.imageBufferGraphics.drawRect(posX - fw / 2 + fm / 2, posY - fh / 2 + fm / 2, fw - fm, fh - fm);
		} else
		{
			
			this.imageBufferGraphics.setColor(new Color(0, 0, 0, 150));
			this.imageBufferGraphics.fillRect(0, 0, posX - fw / 2, shortSide);
			this.imageBufferGraphics.fillRect(posX + fw / 2, 0, shortSide - posX, shortSide);

			this.imageBufferGraphics.fillRect(posX - fw / 2, 0, fw, posY - fh / 2);
			this.imageBufferGraphics.fillRect(posX - fw / 2, posY + fh / 2, fw, shortSide - posY);

			if (!copyMade)
			{
				selectedImage = this.imageBuffer.getSubimage(posX - fw / 2, posY - fh / 2, fw, fh);
				Mat img = OpenCVUtils.img2Mat(selectedImage);
				img.convertTo(img, -1, 1.1 + contrast * .015625, 0 - contrast * 3);

				Imgproc.cvtColor(img, img, Imgproc.COLOR_RGB2HSV);
				Mat imgFinal = img.clone();

				int n = 0;
				for (ColorRange colorRange : currentMetaData.getImageSettings().getColorRanges())
				{
					if (colorRange.getMode() > 0)
					{
						Mat imgRange = img.clone();
						Scalar low = new Scalar(colorRange.getHl(), colorRange.getSl(), colorRange.getVl());
						Scalar high = new Scalar(colorRange.getHh(), colorRange.getSh(), colorRange.getVh());
						System.out.println();

						Core.inRange(imgRange, low, high, imgRange);

						if (n == 0)
							imgFinal = imgRange;
						else
						{
							if (colorRange.getMode() > 1)
								Core.subtract(imgFinal, imgRange, imgFinal);
							else
								Core.add(imgFinal, imgRange, imgFinal);
						}

						n++;
					}
				}
				img = imgFinal;

				Imgproc.cvtColor(img, img, Imgproc.COLOR_GRAY2RGB);

				if (blur)
					Imgproc.blur(img, img, new Size(2d, 2d));

				Imgproc.cvtColor(img, img, Imgproc.COLOR_RGB2GRAY);
				Imgproc.threshold(img, img, 127, 255, Imgproc.THRESH_OTSU);

				selectedImage = OpenCVUtils.mat2Img(img);
				this.imageBufferGraphics.drawImage(selectedImage, posX - fw / 2, posY - fh / 2, null);
				
				try
				{
					String outputFileName = currentFile.substring(0, currentFile.length()-4) + ".zoo.png";
					zooFile = new File(outputFileName);
					if (zooFile.exists())
					{
						zooFile.delete();
						if (!zooFile.exists())
							System.out.println("deleted, but now...");
					}
					ImageIO.write(selectedImage, "png", zooFile = new File(outputFileName));
					currentMetaData.setZooName(zooFile.toString());
					
				} catch (Exception exception)
				{
					exception.printStackTrace();
				}

				copyMade = true;
			} else
			{
				if ((this.zooFile != null) && this.zooFile.exists())
					this.imageBufferGraphics.drawImage(selectedImage, posX - fw / 2, posY - fh / 2, null);

			}
		}
		this.imageBufferGraphics.setColor(new Color(0, 0, 0, 130));
		this.imageBufferGraphics.fillRect(0, shortSide - 70, shortSide, 70);
		this.imageBufferGraphics.setColor(Color.WHITE);
		this.imageBufferGraphics.drawString(currentMetaData.getFileName(), 10, shortSide - 47);
		this.imageBufferGraphics.drawString("contrast: " + contrast, 10, shortSide - 25);
		this.imageBufferGraphics.drawString("blur: " + (blur ? "yes" : "no"), 200, shortSide - 25);
		g.drawImage(imageBuffer, 0, 0, shortSide, shortSide, 0, 0, shortSide, shortSide, null);
	}

	public ImageEditorKeyListener getKeyListener()
	{
		return keyListener;
	}

	public void setContrast(int contrast)
	{
		this.contrast = contrast;
	}

	public void updateColorRange(int hl, int sl, int vl, int hh, int sh, int vh)
	{
		this.hl = hl;
		this.sl = sl;
		this.vl = vl;

		this.hh = hh;
		this.sh = sh;
		this.vh = vh;

		copyMade = false;
		repaint();
	}

	public void updateColorRange()
	{
		this.copyMade = false;
		this.repaint();
	}

	class ImageEditorKeyListener implements KeyListener
	{

		public void keyTyped(KeyEvent arg0)
		{
		}

		public void keyReleased(KeyEvent e)
		{
			
			copyMade = false;
			if (e.getKeyCode() == KeyEvent.VK_SPACE)
			{
//				System.out.println("k:"+selected);

				selected = !selected;
				System.out.println("hit space" + selected);
				unsavedChange = true;
				repaint();
			}
			if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
			{
				selected = false;
				repaint();
			}
			if (e.getKeyCode() == KeyEvent.VK_SHIFT)
			{
				shiftDown = false;
			}
			if (e.getKeyCode() == KeyEvent.VK_CONTROL)
			{
				controlDown = false;
			}
			if ((e.getKeyCode() == KeyEvent.VK_PLUS) || (e.getKeyCode() == KeyEvent.VK_ADD))
			{
				contrast++;
				repaint();
				copyMade = false;
			}
			if ((e.getKeyCode() == KeyEvent.VK_MINUS) || (e.getKeyCode() == KeyEvent.VK_SUBTRACT))
			{
				contrast--;
				repaint();
				copyMade = false;
			}
			if (e.getKeyCode() == KeyEvent.VK_MULTIPLY)
			{
				blur = !blur;
				repaint();
				copyMade = false;
			}

		}

		public void keyPressed(KeyEvent e)
		{
			if (e.getKeyCode() == KeyEvent.VK_SHIFT)
			{
				shiftDown = true;
			}
			if (e.getKeyCode() == KeyEvent.VK_CONTROL)
			{
				controlDown = true;
			}

		}
		
	}
	public void setPhoto(MetaData metaData)
	{
			this.currentMetaData = metaData;
			if (metaData.getZooName()==null)
			{
				this.zooFile = null;
				this.selected = false;
			}
			else
			{
				this.zooFile = new File(metaData.getZooName());
			}
			this.currentFile = metaData.getFileName();
//			this.colorParameterWindow.set
			colorParameterWindow.setColorSettings(this.currentMetaData.getImageSettings().getColorRanges(), contrast = this.currentMetaData.getImageSettings().getContrast());
			setCurrentImage(currentMetaData.getFileName());
			updateColorRange();
	}
	

}