package adecWatt.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.TreeMap;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import misc.Config;
import misc.EasterEgg;
import misc.StateNotifier;
import misc.Util;

import adecWatt.control.AdecWattManager;
import adecWatt.model.AdecWatt;
import adecWatt.model.ImageDB;
import adecWatt.model.PermanentDB;
import adecWatt.model.Prop;
import adecWatt.model.Unit;
import adecWatt.model.UnitNode;
import adecWatt.model.User;
import adecWatt.model.unit.Accessory;
import adecWatt.model.unit.Building;
import adecWatt.model.unit.Lightplot;
import adecWatt.model.unit.NonWorkspace;
import adecWatt.model.unit.Workspace;
import adecWatt.model.xml.XmlUnit;

@SuppressWarnings ("serial") public class JAdecWatt extends JPanel {

    static public final int space = 10;
    static public final String
	BroadcastChangeScale		= "ChangeScale",
    // BroadcastEditableSelection	= "EditableSelection",
	BroadcastSelection		= "Selection",
	BroadcastUnitStory		= AdecWatt.BroadcastUnitStory,
	BroadcastWorkspace		= "Workspace";

    // ========================================
    public StateNotifier stateNotifier = new StateNotifier ();    
    private AdecWatt adecWatt;
    public final Dimension screenSize = Toolkit.getDefaultToolkit ().getScreenSize ();

    private JTabbedPane jTPStorage = new JTabbedPane ();
    private JLabel background = new JLabel ("", SwingConstants.CENTER);
    private JTabbedPane jTPWorkspace = new JTabbedPane ();
    private JTextArea jConsole = new JTextArea ();
    private Workspace currentWorkspace;
    private Font localFont, normalFont, modifiedFont;

    public AdecWatt getAdecWatt () { return adecWatt; }

    public JWorkspaceView<?> getCurrentJWorkspace () {
    	try {
    	    return ((JScrollPaneWorkspace) jTPWorkspace.getComponentAt (jTPWorkspace.getSelectedIndex ())).getJWorkspaceView ();
    	} catch (Exception e) {
    	    return null;
    	}
    }
    public JWorkspaceView<?> getJWorkspace (Workspace workspace) {
    	try {
    	    return (JWorkspaceView<?>) getJScrollPaneWorkspace (workspace).getJWorkspaceView ();
    	} catch (Exception e) {
    	    return null;
    	}
    }
    public Workspace getCurrentWorkspace () {
	return currentWorkspace;
    }
    public void setCurrentWorkspace (Workspace workspace) {
	currentWorkspace = workspace;
	stateNotifier.broadcastUpdate (BroadcastWorkspace);
	adecWatt.broadcastUpdate (AdecWatt.BroadcastTitle);
    }

    // ========================================
    public JAdecWatt (AdecWatt adecWatt) {
	super (new BorderLayout ());
	this.adecWatt = adecWatt;
	normalFont = getFont ();
	modifiedFont = normalFont.deriveFont (Font.BOLD, normalFont.getSize ());
	localFont = normalFont.deriveFont (Font.ITALIC, normalFont.getSize ());
	try {
	    EasterEgg easterEgg = new EasterEgg ("images/adecWatt-anim.gif");
	    easterEgg.addPeriode ("1220", "1231", "images/adecWatt-anim-noel.gif");
	    easterEgg.addPeriode ("0101", "0115", "images/adecWatt-anim-noel.gif");
	    easterEgg.addPeriode ("0517", "0527", "images/2017-festival-adec56.png");
	    easterEgg.addPeriode ("0505", "0516", "images/2017-festival-adec56.gif");
	    background.setIcon (Util.loadImageIcon (Config.dataDirname, easterEgg.getValue ()));
	} catch (Exception e) {
	}
	background.setLayout (new BorderLayout ());
	background.add (jTPWorkspace, BorderLayout.CENTER);
	jConsole.setPreferredSize (new Dimension (400, 50));
	JSplitPane tmp = Util.getJSplitPane (JSplitPane.VERTICAL_SPLIT, background, Util.getJScrollPane (jConsole));
	tmp.setResizeWeight (1);
	add (Util.getJSplitPane (JSplitPane.HORIZONTAL_SPLIT, jTPStorage, tmp), BorderLayout.CENTER);
	adecWatt.addUpdateObserver (this, AdecWatt.BroadcastUnitRoots);
	adecWatt.addMsgObserver (this, AdecWatt.BroadcastConsole, AdecWatt.BroadcastUpdateWorkspace, AdecWatt.BroadcastUpdateUnitTree);
	updateUnitRoots ();

	jTPWorkspace.addChangeListener (new ChangeListener () {
		public void stateChanged (ChangeEvent e) {
		    try {
			setCurrentWorkspace (((JScrollPaneWorkspace) jTPWorkspace.getSelectedComponent ()).getWorkspace ());
		    } catch (Exception e2) {
		    }
		}
	    });

	jTPWorkspace.addMouseListener (new MouseAdapter () {
		public void mousePressed (MouseEvent e) {
		    int idx = jTPWorkspace.indexAtLocation (e.getX (), e.getY ());
		    if (idx < 0) {
			stateNotifier.broadcastDisplay (BroadcastUnitStory, currentWorkspace);
			return;
		    }
		    stateNotifier.broadcastDisplay (BroadcastUnitStory, ((JScrollPaneWorkspace) jTPWorkspace.getComponentAt (idx)).getWorkspace ());
		    // if (idx < 0 || !SwingUtilities.isRightMouseButton (e))
		    // 		return;
		    // JPopupMenu jPopupMenu = new JPopupMenu ();
		    // Util.addMenuItem ("CloseWorkspace", jPopupMenu, new ActionListener () {
		    // 				public void actionPerformed (ActionEvent e) {
		    // 						jTPWorkspace.remove (idx);
		    // 				}
		    // 		});
		    // jPopupMenu.show (jTPWorkspace, e.getX (), e.getY ());
		}
	    });
    }

    // ========================================
    public void closeWorkspaces () {
	jTPWorkspace.removeAll ();
	stateNotifier.broadcastUpdate (BroadcastWorkspace);
    }
    public void closeWorkspace (Workspace workspace) {
	JScrollPaneWorkspace jScrollPaneWorkspace = getJScrollPaneWorkspace (workspace);
	if (jScrollPaneWorkspace == null)
	    return;
	jTPWorkspace.remove (jScrollPaneWorkspace);
	stateNotifier.broadcastUpdate (BroadcastWorkspace);
    }
    public void closeCurrentWorkspace () {
	jTPWorkspace.remove (jTPWorkspace.getSelectedComponent());
	stateNotifier.broadcastUpdate (BroadcastWorkspace);
    }

    public void searchUnit (String text) {
	for (Unit<?> rootUnit : allJTree.keySet ()) {
	    JTree jTree = allJTree.get (rootUnit);
	    jTree.clearSelection ();
	    if (text == null || text.isEmpty ()) {
		for (int row = jTree.getRowCount ()-1; row > 0; row--)
		    jTree.collapseRow (row);
		jTree.expandRow (0);
		jTree.scrollPathToVisible (rootUnit.getPath ());
		continue;
	    }
	    for (int row = jTree.getRowCount ()-1; row > 0; row--)
		jTree.collapseRow (row);
	    for (Unit<?> unit : rootUnit.search (text)) {
		TreePath path = unit.getPath ();
		jTree.expandPath (path);
		jTree.addSelectionPath (path);
		jTree.scrollPathToVisible (path);
	    }
	}
    }

    public void searchComp (String text) {
	for (int i = 0; i < jTPWorkspace.getTabCount (); i++)
	    ((JScrollPaneWorkspace) jTPWorkspace.getComponentAt (i)).getJWorkspaceView ().setSelectedItemsBySearch (text);
    }

    // ========================================
    public void displayOrModify (Unit<?> unit) {
	adecWatt.broadcastDisplay (adecWatt.getUser ().isEditor (unit) ? AdecWattManager.actionModifyUnit : AdecWattManager.actionDisplayUnit,
				   Arrays.asList (unit));
    }
    Hashtable<Unit<?>, JTree> allJTree = new Hashtable<Unit<?>, JTree> ();
    public void updateUnitRoots () {
	closeWorkspaces ();
	jTPStorage.removeAll ();
	allJTree.clear ();
	Hashtable<String, Unit<?>> namedRoots = adecWatt.getNamedRoots ();
	TreeMap<String, Unit<?>> sortedRoots = new TreeMap<String, Unit<?>> ();
	JScrollPane selectedStorage = null;
	for (Unit<?> unit : namedRoots.values ())
	    sortedRoots.put (unit.getLocalName (), unit);
	for (String localName : sortedRoots.descendingKeySet ()) {
	    Unit<?> unit = sortedRoots.get (localName);
	    final JTree jTree = new JTree (unit.getUnitNode (), false);
	    allJTree.put (unit, jTree);
	    jTree.setCellRenderer (xmlTreeRenderer);
	    jTree.setToggleClickCount (0);
	    jTree.setFocusable (false);
	    JScrollPane currentStorage = Util.getJScrollPane (jTree);
	    jTPStorage.add (localName, currentStorage);
	    if (Prop.PropBuilding.equals (unit.getName ()))
		selectedStorage = currentStorage;
	    jTree.addMouseListener (new MouseAdapter () {
		    public void mousePressed (MouseEvent e) {
			int selRow = jTree.getRowForLocation (e.getX (), e.getY ());
			if (selRow < 0) {
			    // stateNotifier.broadcastDisplay (BroadcastEditableSelection, (Unit) null);
			    return;
			}
			TreePath selPath = jTree.getPathForLocation (e.getX (), e.getY ());
			Unit<?> unit = ((UnitNode<?>) selPath.getLastPathComponent ()).getUnit ();
			// stateNotifier.broadcastDisplay (BroadcastEditableSelection, unit);
			stateNotifier.broadcastDisplay (BroadcastUnitStory, unit);
			if (e.getClickCount() == 2) {
			    e.consume ();
			    switch (unit.getTypeToken ()) {
			    case Furniture:
			    case Information:
			    case Line:
			    case Accessory:
				displayOrModify (unit);
				return;
				default:
					break;
			    }
			    if (unit.isUncompleted (null) || isWorkspaceContains (unit, true)) {
				displayOrModify (unit);
				return;
			    }
			    addWorkspace ((Workspace) unit);
			    return;
			}
			if (SwingUtilities.isRightMouseButton (e)) {
			    new JUnitPopup (adecWatt, jTree, unit, e.getPoint ());
			    return;
			}
			Point rowLocation = jTree.getRowBounds (selRow).getLocation ();
			//e.consume ();
			User user = adecWatt.getUser ();
			if (!(user.isDataStructuresManager () || user.isEditor (unit)) && unit.isUncompleted (null))
			    return;
			startDragJDragUnit (new JDragUnit (unit), e, rowLocation);
		    }
		    public void mouseReleased (MouseEvent e) {
			stopJDragUnit (e);
		    }
		});
	    jTree.addMouseMotionListener (new MouseAdapter () {
		    public void mouseDragged (MouseEvent e) {
			dragJDragUnit (e);
		    }
		});
	}
	try {
	    jTPStorage.setSelectedComponent (selectedStorage);
	} catch (Exception e) {
	}
    }

    // ========================================
    DefaultTreeCellRenderer xmlTreeRenderer = new DefaultTreeCellRenderer () {
	    public Component getTreeCellRendererComponent (JTree tree, Object value,
							   boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
		super.getTreeCellRendererComponent (tree, value, selected, expanded, leaf, row, hasFocus);
		Unit<?> unit = ((UnitNode<?>) value).getUnit ();
		setFont (unit.story.isModified () ? modifiedFont : (unit.getRemote () ? normalFont : localFont));
		setText (unit.getLocalName ());
		setIcon (unit.getIcon (16));
		return this;
	    }
	};

    public void updateUnit (Unit<?> unit, boolean structChange) {
	try {
	    DefaultTreeModel model = (DefaultTreeModel) allJTree.get (unit.getUnitRoot ()).getModel();
	    if (structChange)
		model.nodeStructureChanged (unit.getUnitNode ());
	    else
		model.nodeChanged (unit.getUnitNode ());
	} catch (Exception e) {
	}
    }
		

    // ========================================
    public void addWorkspace (Workspace workspace) {
	if (isWorkspaceContains (workspace, true))
	    return;
	JScrollPaneWorkspace jScrollPaneWorkspace = new JScrollPaneWorkspace (JWorkspaceView.getInstance (this, workspace));
	jTPWorkspace.addTab (workspace.toString (), workspace.getIcon (16), jScrollPaneWorkspace);
	jTPWorkspace.setSelectedComponent (jScrollPaneWorkspace);
	stateNotifier.broadcastUpdate (BroadcastWorkspace);
    }
    public boolean hasWorkspace () {
	return jTPWorkspace.getTabCount () > 0;
    }
    public JScrollPaneWorkspace getJScrollPaneWorkspace (Unit<?> unit) {
	for (int i = 0; i < jTPWorkspace.getTabCount (); i++) {
	    JScrollPaneWorkspace jScrollPaneWorkspace = (JScrollPaneWorkspace) jTPWorkspace.getComponentAt (i);
	    if (jScrollPaneWorkspace.getWorkspace () == unit)
		return jScrollPaneWorkspace;
	}
	return null;
    }
    public boolean isWorkspaceContains (Unit<?> unit, boolean select) {
	JScrollPaneWorkspace jScrollPaneWorkspace = getJScrollPaneWorkspace (unit);
	if (jScrollPaneWorkspace == null)
	    return false;
	if (select)
	    jTPWorkspace.setSelectedComponent (jScrollPaneWorkspace);
	return true;
    }

    // ========================================
    JDragUnit selectedJDragUnit;
    Point mousePosOnJDragUnit;
    public void startDragJDragUnit (JDragUnit jDragUnit, MouseEvent e, Point rowLocation) {
	mousePosOnJDragUnit = e.getPoint ();
	mousePosOnJDragUnit.translate (-rowLocation.x, -rowLocation.y);
	jDragUnit.setBackground (Color.lightGray);
	JRootPane jRootPane = getRootPane ();
	Point absPos = jRootPane.getMousePosition (true);
	jDragUnit.setLocation (absPos.x-mousePosOnJDragUnit.x, absPos.y-mousePosOnJDragUnit.y);
	JLayeredPane jLayeredPane = jRootPane.getLayeredPane ();
	if (selectedJDragUnit != null)
	    jLayeredPane.remove (selectedJDragUnit);
	selectedJDragUnit = jDragUnit;
	jLayeredPane.add (jDragUnit);
	jLayeredPane.setLayer (jDragUnit, JLayeredPane.DRAG_LAYER.intValue ());
	repaint ();
    }

    public void dragJDragUnit (MouseEvent e) {
	if (selectedJDragUnit == null)
	    return;
	Point absPos = getRootPane ().getMousePosition (true);
	try {
	    selectedJDragUnit.setLocation (absPos.x-mousePosOnJDragUnit.x, absPos.y-mousePosOnJDragUnit.y);
	} catch (Exception e2) {
	}
    }

    public void stopJDragUnit (MouseEvent e) {
	if (selectedJDragUnit == null)
	    return;
	//selectedJDragUnit.setBackground (UIManager.getColor ("Panel.background"));
	selectedJDragUnit.setVisible (false);
	selectedJDragUnit.getParent ().remove (selectedJDragUnit);
	boolean inWorkspace = false;
	JWorkspaceView<?> jWorkspaceView = null;
	JTree jTree = null;
	try {
	    for (Component
		     component = findComponentAt (getMousePosition (true)),
		     parent = null;
		 component != null;
		 component = parent) {
		parent = component.getParent ();
		if (component == jTPWorkspace) {
		    inWorkspace = true;
		    break;
		}
		if (component instanceof JTree) {
		    jTree = (JTree) component;
		    break;
		}
		if (component instanceof JWorkspaceView) {
		    jWorkspaceView = (JWorkspaceView<?>) component;
		    break;
		}
	    }
	} catch (Exception e2) {
	}
	Unit<?> src = selectedJDragUnit.unit;
	selectedJDragUnit = null;
	if (jTree != null)
	    dropInJTree (src, jTree, e);
	else if (src.isUncompleted (null))
	    ;
	else if (jWorkspaceView != null)
	    dropInWorkspace (src, jWorkspaceView);
	else if (inWorkspace)
	    dropInEmptySpace (src);
	repaint ();
    }
    private void dropInJTree (Unit<?> src, JTree jTree, MouseEvent e) {
	User user = adecWatt.getUser ();
	if (!(user.isDataStructuresManager () || user.isEditor (src)))
	    return;
	int selRow = jTree.getRowForLocation (e.getX (), e.getY ());
	if (selRow < 0) {
	    // stateNotifier.broadcastDisplay (BroadcastEditableSelection, (Unit) null);
	    return;
	}
	TreePath selPath = jTree.getPathForLocation (e.getX (), e.getY ());
	Unit<?> dst = ((UnitNode<?>) selPath.getLastPathComponent ()).getUnit ();
	src.storyTransform (null, dst, null, null);
    }
    private void dropInEmptySpace (Unit<?> src) {
	switch (src.getTypeToken ()) {
	case Furniture:
	case Information:
	case Line:
	case Accessory:
	    return;
	case Building:
	    addWorkspace ((Building) src);
	    break;
	case Lightplot:
	    addWorkspace ((Lightplot) src);
	    break;
	}
    }
    private void dropInWorkspace (Unit<?> src, JWorkspaceView<?> jWorkspaceView) {
	Unit<?> dst = jWorkspaceView.workspace;
	if (src == dst || src.isUncompleted (null))
	    return;
	if (src instanceof NonWorkspace &&
	    !adecWatt.getUser ().isEditor (dst))
	    return;
	switch (src.getTypeToken ()) {
	case Furniture:
	case Information:
	    jWorkspaceView.dropComp ((NonWorkspace) src, jWorkspaceView.getMousePosition (true));
	    return;
	case Line:
	    jWorkspaceView.dropSegm ((NonWorkspace) src, jWorkspaceView.getMousePosition (true));
	    return;
	case Accessory:
	    jWorkspaceView.dropAcc ((Accessory) src, jWorkspaceView.getMousePosition (true));
	    return;
	case Building:
	    if (dst.getTypeToken () == Unit.UnitTypeEnum.Lightplot &&
		adecWatt.getUser ().isEditor (dst)) {
		((Lightplot) dst).storyChangeBuilding ((Building) src);
		break;
	    }
	    addWorkspace ((Building) src);
	    break;
	case Lightplot:
	    if (dst.getTypeToken () == Unit.UnitTypeEnum.Building &&
		adecWatt.getUser ().isEditor (src)) {
		if (!isWorkspaceContains (src, true))
		    addWorkspace ((Lightplot) src);
		((Lightplot) src).storyChangeBuilding ((Building) dst);
		break;
	    }
	    addWorkspace ((Lightplot) src);
	    break;
	}
    }

    // ========================================
    public void displayUpdateUnitTree (Object... objects) {
	Unit<?> unit = (Unit<?>) objects[0];
	updateUnit (unit, true);
    }
    public void displayUpdateWorkspace (Object... objects) {
	Workspace workspace = (Workspace) objects[0];
	JScrollPaneWorkspace jScrollPaneWorkspace = getJScrollPaneWorkspace (workspace);
	if (jScrollPaneWorkspace == null)
	    return;
	Workspace currentWorkspace = this.currentWorkspace;
	closeWorkspace (workspace);
	addWorkspace (workspace);
	setCurrentWorkspace (currentWorkspace);
    }

    public void displayConsole (Object... objects) {
	synchronized (jConsole) {
	    jConsole.invalidate ();
	    String message = (String) objects[0];
	    jConsole.append (message);
	    jConsole.validate ();
	    jConsole.repaint ();
	}
    }

    // ========================================
}
