/*
 * Copyright (c) 2011-2019, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boofcv.gui.d2;

import boofcv.gui.BoofSwingUtil;
import georegression.struct.point.Point2D_F64;
import georegression.struct.se.Se2_F64;
import georegression.transform.se.SePointOps_F64;
import org.ddogleg.struct.FastQueue;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

/**
 * Used to display a 2D point cloud.
 *
 * @author Peter Abeles
 */
public class PlaneView2D extends JPanel implements MouseMotionListener, MouseListener, MouseWheelListener, KeyListener {

	FastQueue<Point2D_F64> points = new FastQueue<>(Point2D_F64.class, true);

	Se2_F64 transform = new Se2_F64();
	double scale = 1;

	Point2D_F64 a = new Point2D_F64();

	// previous mouse location
	int prevX;
	int prevY;

	double pixelToUnit;

	public PlaneView2D( double pixelToUnit ) {
		this.pixelToUnit = pixelToUnit;

		addKeyListener(this);
		addMouseListener(this);
		addMouseMotionListener(this);
		addMouseWheelListener(this);
	}

	public void reset() {
		SwingUtilities.invokeLater( new Runnable() {
			@Override
			public void run() {
				points.reset();
			}
		});
	}

	public void addPoint( final double x , final double y ) {
		SwingUtilities.invokeLater( new Runnable() {
			@Override
			public void run() {
				points.grow().set(x, y);
			}
		});
	}

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

		Graphics2D g2 = (Graphics2D)g;

		int offsetX = getWidth()/2;
		int offsetY = getHeight()/2;

		int H = getHeight()-1;

		int r = 2;
		int w =2*r+1;

		for( Point2D_F64 p : points.toList() ) {
			SePointOps_F64.transform(transform,p,a);
			a.x *= scale/pixelToUnit;
			a.y *= scale/pixelToUnit;

			g2.fillOval(offsetX+(int)a.x,H-(offsetY+(int)a.y),w,w);
		}
	}

	@Override
	public void mouseWheelMoved(MouseWheelEvent e) {
		double scale = BoofSwingUtil.mouseWheelImageZoom(this.scale,e);
		if( scale == this.scale )
			return;

		if( scale < 0.001 )
			scale = 0.001;
		else if( scale > 1000 )
			scale = 1000;

		this.scale = scale;
		repaint();
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		grabFocus();
	}

	@Override
	public void mousePressed(MouseEvent e) {
		prevX = e.getX();
		prevY = e.getY();
	}

	@Override
	public void mouseReleased(MouseEvent e) {}

	@Override
	public void mouseEntered(MouseEvent e) {}

	@Override
	public void mouseExited(MouseEvent e) {}

	@Override
	public void mouseDragged(MouseEvent e) {
		final int deltaX = e.getX()-prevX;
		final int deltaY = e.getY()-prevY;

//		if( SwingUtilities.isRightMouseButton(e) ) {
//			offsetZ -= deltaY*pixelToDistance;
//		} else if( e.isShiftDown() || SwingUtilities.isMiddleMouseButton(e)) {
//			tiltAngle += deltaY;
//		} else {
		transform.T.x += deltaX*pixelToUnit/scale;
		transform.T.y -= deltaY*pixelToUnit/scale;
//		}

		prevX = e.getX();
		prevY = e.getY();

		repaint();
	}

	@Override
	public void mouseMoved(MouseEvent e) {}

	@Override
	public void keyTyped(KeyEvent e) {
		if( e.getKeyChar() == 'w' ) {
			transform.T.y -= pixelToUnit/scale;
		} else if( e.getKeyChar() == 's' ) {
			transform.T.y += pixelToUnit/scale;
		} else if( e.getKeyChar() == 'a' ) {
			transform.T.x += pixelToUnit/scale;
		} else if( e.getKeyChar() == 'd' ) {
			transform.T.x -= pixelToUnit/scale;
		} else if( e.getKeyChar() == 'q' ) {
			scale *= 1.05;
		} else if( e.getKeyChar() == 'e' ) {
			scale *= 0.95;
		} else if( e.getKeyChar() == 'h' ) {
			transform.reset();
			scale = 1;
		}

		if( scale < 0.001 )
			scale = 0.001;
		else if( scale > 1000 )
			scale = 1000;

		repaint();
	}

	@Override
	public void keyPressed(KeyEvent e) {
		//To change body of implemented methods use File | Settings | File Templates.
	}

	@Override
	public void keyReleased(KeyEvent e) {
		//To change body of implemented methods use File | Settings | File Templates.
	}
}
