/* D-RexGxt - D-RexGxt - GUI Design Tool for GWT
 * Copyright (c) 2008,  KPS Corporation
 * All rights reserved.
 *
 * Open Source License
 * ------------------------------------------------------------------------------------------
 * D-RexGxt is licensed under the terms of the Open Source GPL 3.0 license.
 *
 * http://www.gnu.org/licenses/gpl.html
 *
 * --
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */
package jp.co.kpscorp.gwt.client.design;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import jp.co.kpscorp.gwt.client.design.gxt.GxtUtil;
import jp.co.kpscorp.gwt.client.design.gxt.widget.ColumnModelWidget;
import jp.co.kpscorp.gwt.client.design.gxt.widget.ConfigWidget;

import com.extjs.gxt.ui.client.Events;
import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.Style.LayoutRegion;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.util.Margins;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.Container;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.layout.BorderLayout;
import com.extjs.gxt.ui.client.widget.layout.BorderLayoutData;
import com.extjs.gxt.ui.client.widget.layout.CardLayout;
import com.extjs.gxt.ui.client.widget.layout.FlowLayout;
import com.extjs.gxt.ui.client.widget.tree.Tree;
import com.extjs.gxt.ui.client.widget.tree.TreeHelper;
import com.extjs.gxt.ui.client.widget.tree.TreeItem;
import com.extjs.gxt.ui.client.widget.tree.TreeItemUI;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.RootPanel;

public class PanelWithTree extends LayoutContainer implements TreeMaker,
		Listener<ComponentEvent> {

	private ContentPanel gxCTP_2;

	private Tree tree = new Tree();

	private Map<Component, MyTreeItem> tiMap = new HashMap<Component, MyTreeItem>();

	public PanelWithTree() {
		super();
		init();
	}

	private void init() {
		BorderLayoutData gxBLOD_1;
		BorderLayoutData gxBLOD_2;
		FlowLayout gxFLLO_2;
		BorderLayout gxBLO_1;
		DesignPanel gxCTP_1 = DesignPanel.getInstance();
		setWidth(1200);
		setHeight(800);
		gxBLOD_1 = new BorderLayoutData(LayoutRegion.CENTER);
		gxBLOD_1.setMargins(new Margins(5, 0, 5, 0));
		add(gxCTP_1, gxBLOD_1);
		gxCTP_2 = new ContentPanel();
		gxCTP_2.setHeading("Component Tree");
		gxBLOD_2 = new BorderLayoutData(LayoutRegion.CENTER);
		gxBLOD_2.setCollapsible(true);
		gxBLOD_2.setMargins(new Margins(5, 5, 5, 5));
		gxBLOD_2.setRegion(LayoutRegion.WEST);
		gxBLOD_2.setSplit(true);
		add(gxCTP_2, gxBLOD_2);
		gxFLLO_2 = new FlowLayout();
		gxCTP_2.setLayout(gxFLLO_2);
		gxBLO_1 = new BorderLayout();
		setLayout(gxBLO_1);
		gxCTP_1.setTreeMaker(this);
		Component comp = DesignPanel.getInstance().getBasePanel();
		regist(comp);
		gxCTP_2.add(tree);
		gxCTP_2.addListener(Events.Render, this);
	}

	// @Override
	// protected void afterRender() {
	// super.afterRender();
	// layout();
	// // gxCTP_2.collapse();
	// el().setElementAttribute("align", "left");
	// }

	Map map = new HashMap();

	public void makeTree(LayoutContainer base) {
		makeTreeRec(tree.getRootItem(), base);
		gxCTP_2.removeAll();
		gxCTP_2.add(tree);
	}

	public TreeItem regist(Component comp) {
		if (comp instanceof ConfigWidget) {
			return null;
		}
		MyTreeItem ti = tiMap.get(comp);
		if (ti != null) {
			return ti;
		}
		WidgetService ws = WidgetServiceFactory.getService(comp);
		ti = new MyTreeItem(ws.getLabel() + ":" + comp.getId(), comp);
		TreeHelper.setTree(ti, tree);
		ti.addListener(Events.Render, DesignPanel.getInstance());
		tiMap.put(comp, ti);
		comp.addListener(Events.Attach, this);
		comp.addListener(Events.Orphan, this);
		if (DesignPanel.getInstance().getBasePanel() == comp) {
			tryToAdd(tree.getRootItem(), ti, (Component) comp
					.getParent(), comp);
		} else {
			Component parent = ws.getParent();
			TreeItem pti = tiMap.get(parent);
			if (pti == null) {
				pti = regist(parent);
			}
			tryToAdd(pti, ti, parent, comp);
		}
		return ti;
	}

	private void tryToAdd(TreeItem pti, TreeItem ti, Component parent,
			Component comp) {
		if (pti.indexOf(ti) != -1) {
			return;
		}
		if (parent instanceof Container) {
			Container<Component> con = (Container<Component>) parent;
			int i = con.indexOf(comp);
			if (i < 0 || i > pti.getItemCount()) {
				i = pti.getItemCount();
			}
			pti.add(ti, i);
		} else {
			pti.add(ti);
		}

	}

	private void makeTreeRec(TreeItem parent, Component comp) {
		WidgetService ws = WidgetServiceFactory.getService(comp);
		TreeItem ti = new MyTreeItem(ws.getLabel() + ":" + comp.getTitle(),
				comp);
		TreeHelper.setTree(ti, tree);
		parent.add(ti);
		Iterator it = GxtUtil.childrenIterator(comp);
		while (it.hasNext()) {
			Component ch = (Component) it.next();
			makeTreeRec(ti, ch);
		}
	}

	class MyTreeItem extends TreeItem {
		private Component comp;

		public MyTreeItem(String text, Component comp) {
			super(text);
			this.comp = comp;
			tiMap.put(comp, this);
			setUI(new MyTreeItemUI(this));
		}

		@Override
		protected void onRightClick(final ComponentEvent ce) {
			if (ce.type == (GXT.isSafari && GXT.isMac ? Event.ONMOUSEDOWN
					: Event.ONMOUSEUP)
					&& ce.isRightClick()) {
				// 選択状態CSSを解除
				if (el() != null) {
					getUI().onOverChange(false);
				}
			}
			super.onRightClick(ce);
		}

		@Override
		public void onBrowserEvent(Event event) {
			super.onBrowserEvent(event);
			ComponentEvent ce = createComponentEvent(event);
			ce.event = event;
			if (DOM.eventGetType(ce.event) == Event.ONMOUSEDOWN
					&& DOM.eventGetButton(ce.event) == Event.BUTTON_RIGHT) {
				// 選択状態CSSを解除
				if (el() != null) {
					getUI().onOverChange(false);
				}
			}
		}

		public Component getComp() {
			return comp;
		}

	}

	class MyTreeItemUI extends TreeItemUI {
		private El proxyEl;

		private Element body = RootPanel.getBodyElement();

		public MyTreeItemUI(MyTreeItem item) {
			super(item);
		}

		@Override
		public void onOverChange(boolean over) {
			super.onOverChange(over);
			MyTreeItem ti = (MyTreeItem) item;
			// CardLayout ActiveItem切り替え
			if (over && ti.getComp().getParent() instanceof LayoutContainer) {
				LayoutContainer lc = (LayoutContainer) ti.getComp().getParent();
				if (lc.getLayout() instanceof CardLayout) {
					CardLayout cl = (CardLayout) lc.getLayout();
					cl.setActiveItem(ti.getComp());
				}
			}
			// CSS切り替え
			El el = ti.getComp().el();
			if (el != null) {
				if (over) {
					if (proxyEl == null) {
						proxyEl = new El(DOM.createDiv());
						proxyEl.setStyleName("x-component-tree");
						proxyEl.disableTextSelection(true);
						proxyEl.makePositionable(true);
						proxyEl.setLeftTop(el.getX(), el.getY());
						proxyEl.setSize(el.getSize());
						proxyEl.setVisible(true);
						DOM.appendChild(body, proxyEl.dom);
					}
				} else {
					if (proxyEl != null) {
						DOM.removeChild(body, proxyEl.dom);
						proxyEl = null;
					}
				}
			}
		}

	}

	public void handleEvent(ComponentEvent ce) {
		if (ce.type == Events.Orphan) {
			MyTreeItem ti = tiMap.get(ce.component);
			if (ti != null) {
				ti.getParentItem().remove(ti);
				removeTiRec(ti);
			}
		} else if (ce.type == Events.Attach) {
			regist(ce.component);
			// Grid Attach時には ColumModel Configをregist
			if (ce.component instanceof Grid) {
				Grid grid = (Grid) ce.component;
				ColumnModelWidget cmw = grid.getData(ColumnModelWidget.class
						.getName());
				regist(cmw);
				Iterator<Component> it = WidgetServiceFactory.getService(cmw)
						.childrenIterator();
				while (it.hasNext()) {
					Component ccw = it.next();
					regist(ccw);
				}
			}
		} else if (ce.type == Events.Render) {
			// 左パネル左寄せ
			ce.component.el().setElementAttribute("align", "left");
		}
	}

	private void removeTiRec(MyTreeItem item) {
		tiMap.remove(item.getComp());
		// 選択状態CSSを解除
		if (item.el() != null) {
			item.getUI().onOverChange(false);
		}
		Iterator it = item.getItems().iterator();
		while (it.hasNext()) {
			removeTiRec((MyTreeItem) it.next());
		}
	}

}
