/* Minimap.java - JFrame extension for storing and communicating meta-information and about an MinimapPanel.
 * Copyright 2010 Howard Hughes Medical Institute and Nicholas Andrew Swierczek
 * This file is a part of Choreography and is distributed under the
 * terms of the GNU Lesser General Public Licence version 2.1 (LGPL 2.1).
 * For details, see http://www.gnu.org/licences
 */

package mwt;

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

import mwt.numerics.*;


/**
 * Extension of JFrame that contains a view of the DataMapSource and coordinates data map position updates between itself and
 * the DataMapVisualizer.
 * @author Nicholas Andrew Swieczek (swierczekn at janelia dot hhmi dot org)
 */
public class Minimap extends JFrame implements KeyListener {
    /**
     * MinimapPanel which contains this Minimap's view of the DataMapSource.
     */
    public MinimapPanel panel_minimap;

    /**
     * Ratio of this Minimap's MinimapPanel screen width and the DataMapVisualizer's screen width.
     */
    public double width_scale = 1.0f;
    
    /**
     * Ratio of this Minimap's MinimapPanel screen height and the DataMapVisualizer's screen height.
     */
    private double height_scale = 1.0f;

    /**
     * Boolean flag which is true if and only if this Minimap is synced to the DataMapVisualizer.
     */
    public boolean synced = false;
    
    /**
     * Reference to the DataMapVisualizer that created this Minimap.
     */
    private DataMapVisualizer parent;
    
    /**
     * String representing the title for this Minimap's window.
     */
    public String title = "-1";
    
    
    /**
     * Dimension of this Minimap's JFrame, in screen coordinates.
     */
    // Acceptable only because the frame is set to not be resizable. Otherwise I'd never do this.
    private Dimension dim = new Dimension(327,287);
    
    /**
     * Minimap constructor which assumes all specified parameters are valid and non-null.
     * @param i Integer ID from which is created a unique window title.
     * @param xx x-coordinate of the location of this Minimap on the screen.     
     * @param yy y-coordinate of the location of this Minimap on the screen.
     * @param v DataView to base this Minimap's ImagePanel on.
     * @param z index in to DataMapVisualizer's {@code resolution_mapping} ArrayList for this Minimap's DataMapSource view.
     * @param b Synced flag.
     * @param p Reference to creating DataMapVisualizer.
     * @param wl Reference to listener for Window Events generated by this Minimap.
     * @param s Reference to DataMapSource.
     * @param d Dimensions in screen coordinates for the size of this Minimap's MinimapPanel's ImagePanel.
     * @param mf Fraction of ImagePanel screen dimensions with which to bound the ImagePanel's ViewBox.
     * @param ratio Aspect ratio of ImagePanel's ViewBox.
     * @param zoom ArrayList of valid zoom factors, which is used in building the JSlider in this Minimap's MinimapPanel.
     */    
    // TODO Fix Javadoc
    public Minimap(int i, int xx, int yy, DataView v, int z, boolean b, DataMapVisualizer p, WindowListener wl, DataMapSource s, DataView default_view, Dimension d, double mf, double ratio, ArrayList<Double> zoom, String units) {

        parent = p;
        title = Integer.toString(i);
        setSynced(b);
        this.setLocation(xx,yy);
        //System.out.println("MINIMAP CREATION " + v);
        panel_minimap = new MinimapPanel(this,v, s, default_view, d, mf, ratio, zoom, z, units);

        this.addWindowListener(wl);
        
        setContentPane(panel_minimap);
        this.setResizable(false); // YES! I /always/ want to do this.
        //TODO: Remove me when developement is done.
        //this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
   
    /**
     * @return String title, minus any synchronization annotation.
     */
    public String getID() {
        return title;
    }
    
    /**
     * Passes the specified DataView to this Minimap's MinimapPanel.
     * @param v DataView to pass along.
     */
    public void setDataView( DataView v ) {
        //panel_minimap.image.reticle_view.equal(v);
        panel_minimap.setReticleScreenLocation(true);
    }
    
    /**
     * Causes this Minimap to display its frame and calculates the {@code width_scale} and {@code height_scale} ratios.
     */
    public void makeVisible() {
        int component_height = Math.max(panel_minimap.getZoomSlider().getSize().height + panel_minimap.focus.getSize().height, panel_minimap.image.getSize().height) ;        
        this.setSize(new Dimension(this.getWidth(),component_height+this.getInsets().top+this.getInsets().bottom));
        this.addKeyListener(this);
        this.pack();
        this.setVisible(true);
        int h1 = panel_minimap.image.getHeight();
        int h2 = parent.image.getHeight();
        int w1 = panel_minimap.image.getWidth();
        int w2 = parent.image.getWidth();

        height_scale = (double)h1/(double)h2;
        width_scale = (double)w1/(double)w2;

        panel_minimap.width_ratio = 1/width_scale;
        // TODO: Fix construction order given new understanding of pixel size stuff.
        panel_minimap.setView(parent.getMainView());        
        panel_minimap.retrieveImage();
        requestFocus();
    }

    /**
     * Passes along the specified double to this Minimap's MinimapPanel.
     * @param ratio double value to pass.
     */
    // TODO: Fix Javadoc
    public void setAspectRatio( double ratio, DataView v, double mf, Vec2D delta_size ) {
        // TODO: Delete the comment below if it turns out reordering this method removed that transient behavior.
        // The following lines came about because I found getWidth/Height() would occasionally report 0, even though their internal variables would be non-zero.
        int h1 = panel_minimap.image.getHeight();
        int h2 = parent.image.getHeight();
        int w1 = panel_minimap.image.getWidth();
        int w2 = parent.image.getWidth();

        height_scale = (double)h1/(double)h2;
        width_scale = (double)w1/(double)w2;
        panel_minimap.width_ratio = 1/width_scale;        
        panel_minimap.setAspectRatio(ratio,v,delta_size);
        panel_minimap.setZoomLevel(panel_minimap.getZoomLevel());
    }
    
    public Dimension getMainWindowDimension() {
        return parent.dim_panel;
    }
    
    public DataView getMainView() {
        return parent.getMainView();
    }
    
    /**
     * Sets this Minimap's {@code synced} flag to the specified boolean, changing the window title if
     * this is an actual state change.
     * @param b boolean value to which to set {@code synced} flag
     */
    public void setSynced( boolean b ) {
        if( !b ) {
            this.setTitle("Minimap " + title);
        }
        if( !synced && b ) {
            this.setTitle("Minimap " + title + " - Synchronized");			
        }
        synced = b;
        repaint(); 
    }

    /**
     * Passes along the specified Vec2D to this Minimap's DataMapVisualizer parent.
     * @param v Vec2D to pass.
     */
    public void setMainWindowLocation( Vec2D v ) {
        parent.setViewLocation(v);
    }
    
    /**
     * Passes along call to parent DataMapVisualizer.
     * @param n upper left coordinate of DataMapSource view bounding box.
     * @param f lower right coordinate of DataMapSource view bounding box.
     */
    public void setMainViewDataVectors(Vec2D n, Vec2D f) {
        parent.setMainViewDataVectors(n,f);
    }
	

    /**
     * Passes along call to parent DataMapVisualizer.
     * @param l index in to DataMapVisualizer's {@code resolution_mapping} ArrayList.
     */
    public void setMainWindowPixelSize(int l) {
        parent.setMainWindowPixelSize(l);	        
    }
    
    public void setMainWindowZoomLevel( int l) {
        //parent.setMainWindowPixelSize(l);	        
        parent.zoom_index = l;
    }
	
    /**
     * Passes along call to parent DataMapVisualizer.
     */
    public void desyncAllMinimaps() {
        parent.desyncAllMinimaps();
    }

    /**
     * Unpacks the vector representation of the DataMapSource view bounding box from the specified Viewbox and
     * calls a function with those vectors in the parent DataMapVisualizer to update its view of the DataMapSource.
     * @param data
     */
    public void setMainWindowData(DataView data) {
        parent.setMainViewDataVectors(data.pos(),data.size());
    }
	
     /**
      * Causes this Minimap's view of the data to be translated by the specified amount, scaled to the coordinate System
      * for this Minimap.
      * @param v Vector to shift this Minimap's view of the DataMapSource by.
      */
    public void changeLocation( Vec2D v ) {        
        Vec2D tmp = new Vec2D(v.x*width_scale,v.y*height_scale);
        panel_minimap.changeLocation(tmp);
    }
    
    /**
     * Passes along the specified int to this Minimap's MinimapPanel.
     * @param index int to pass.
     */
    public void setZoomIndex( int index ) {
        panel_minimap.setZoomIndex(index);
    }
    
    public double getMainWindowAspectRatio() {
        return parent.getAspectRatio();
    }
    
    /**
     * Passes along the specified DataView to this Minimap's MinimapPanel.
     * @param v DataView to pass.
     */
    public void setView( DataView v ) {
        panel_minimap.setView(v);
    }
	
    /**
     * Causes this Minimap's MinimapPanel to retrieve a new image from the DataMapSource.
     */
    public void updateImage() {
        panel_minimap.retrieveImage();
    }
    
    /**
     * @param i index to retrieve from the parent DataMapVisualizer's {@code resolution_mapping}.
     * @return double value from DataMapVisualizer.
     */
    public double getResolution( int i ) {
        return parent.getResolution(i);
    }       
	
    /**
     * Scales the specified vector from this Minimap's coordinate System to the parent DataMapVisualizer's coordinate System,
     * then causes the parent DataMapVisualizer to change the location of its view of the DataMapSource by that amount.
     * @param ve Vector to shift DataMapVisualizer's view by.
     */
    public void changeView(Vec2I ve) {		
        // Scale the specified Vec2I in to DMV window sizes, then kick the results to the DMV.
        Vec2D tmp = new Vec2D(ve.x/width_scale,ve.y/height_scale);
        parent.shiftViewLocation(new Vec2I((int)Math.round(tmp.x),(int)Math.round(tmp.y)));
    }

    // TODO: Javadoc me
    public void keyTyped(KeyEvent arg0) {
        
    }
    
    // TODO: Javadoc me
    public void keyPressed(KeyEvent arg0) { 
        char c = arg0.getKeyChar();

        if( c >= 48 && c <= 57 ) {
            parent.setFocusedMinimap(c-48);
        }
        else if( c == 43 ) {
            panel_minimap.setZoomLevel(panel_minimap.getZoomSlider().getValue()+1);
        }
        else if( c == 45 ) {
            panel_minimap.setZoomLevel(panel_minimap.getZoomSlider().getValue()-1);
        }
        else {
            switch( arg0.getKeyCode() ) {
                case KeyEvent.VK_A : {
                    if( arg0.isControlDown() ) {
                        parent.createNewMinimap();
                    }
                    break;
                }
                case KeyEvent.VK_Q : {
                    if( arg0.isControlDown() ) {
                        System.exit(0);
                    }
                    break;
                }
                case KeyEvent.VK_F : {
                    panel_minimap.setAndUpdateActive(true);
                    break;
                }
                case KeyEvent.VK_SPACE : {
                    parent.requestFocus();
                    break;
                }
                case KeyEvent.VK_LEFT : {
                    panel_minimap.processMove(new Vec2I(-parent.pixel_step_x,0), arg0.isShiftDown());
                    break;
                }
                case KeyEvent.VK_KP_LEFT : {
                    panel_minimap.processMove(new Vec2I(-parent.pixel_step_x,0), arg0.isShiftDown());
                    break;
                }
                case KeyEvent.VK_RIGHT : {
                    panel_minimap.processMove(new Vec2I(parent.pixel_step_x,0), arg0.isShiftDown());
                    break;
                }
                case KeyEvent.VK_KP_RIGHT : {
                    panel_minimap.processMove(new Vec2I(parent.pixel_step_x,0), arg0.isShiftDown());
                    break;
                }
                case KeyEvent.VK_UP : {
                    panel_minimap.processMove(new Vec2I(0,-parent.pixel_step_y), arg0.isShiftDown());
                    break;
                }
                case KeyEvent.VK_KP_UP : {
                    panel_minimap.processMove(new Vec2I(0,-parent.pixel_step_y), arg0.isShiftDown());
                    break;
                }
                case KeyEvent.VK_DOWN : {
                    panel_minimap.processMove(new Vec2I(0,parent.pixel_step_y), arg0.isShiftDown());                    
                    break;
                }
                case KeyEvent.VK_KP_DOWN : {
                    panel_minimap.processMove(new Vec2I(0,parent.pixel_step_y), arg0.isShiftDown());
                    break;
                }
                case KeyEvent.VK_HOME : {
                    panel_minimap.centerView(parent.home);
                    break;
                }
                default: break;
            }
        }
    }

    // TODO: Javadoc me
    public void keyReleased(KeyEvent arg0) {
        
    }
}

