/*
 * Copyright (c) 2006-2008 Maskat Project.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Maskat Project - initial API and implementation
 */
package org.maskat.ui.editors.layout;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;

import org.maskat.core.betwixt.MaskatBeanIO;
import org.maskat.core.event.Component;
import org.maskat.core.event.Event;
import org.maskat.core.event.EventDef;
import org.maskat.core.event.Param;
import org.maskat.core.event.Result;
import org.maskat.core.layout.Layout;
import org.maskat.core.layout.LayoutDef;

/**
 * The state of layout and event definition. MaskatDef and IEventDefSource's
 * state.
 */
public class MaskatResources {

	private LayoutDef layoutDef = null;

	private EventDef eventDef;

	private IFile layoutXMLFile;

	private IFile eventXMLFile;

	MaskatResources(IFile layoutXMLFile) {
		this.layoutXMLFile = layoutXMLFile;

		// look for link.xml in the same directory as layoutFile.
		IContainer folder = layoutXMLFile.getParent();
		String eventXMLFileName = layoutXMLFile.getLocation().removeFileExtension()
				.lastSegment()
				+ "_e.xml";
		this.eventXMLFile = folder.getFile(new Path(eventXMLFileName));
	}

	public IFile getLayoutXMLFile() {
		return layoutXMLFile;
	}

	public IFile getEventXMLFile() {
		return eventXMLFile;
	}
	
	Layout getLayout() {
		return (Layout) layoutDef.getChildByTypeIdx(Layout.class, 0);
	}

	public void load() throws Exception {
		loadLayout();
		loadEvent();
		connect();
	}
	
	void connect() {
		layoutDef.setEventDef(eventDef);
	}

	void loadLayout() throws Exception {
		InputStream in = null;
		try {
			if (layoutXMLFile.exists()) {
				// contentsからデータを読み込む
				in = layoutXMLFile.getContents();
//				layoutDef = LayoutDefParser.parse(in);
				layoutDef = MaskatBeanIO.readLayoutDef(in);
				if (layoutDef != null) {
					Iterator it = layoutDef.getTypedChildren(Layout.class);
					while (it.hasNext()) {
						((Layout) it.next()).unpackRadioGroup();
					}
				}
			}
		} finally {
			IOUtils.closeQuietly(in);
		}
	}

	void loadEvent() throws Exception {
		// XXX temporarily, assume that files is not empty or null
		eventDef = new EventDef();
		InputStream in = null;
		try {
			eventXMLFile.refreshLocal(IResource.DEPTH_ONE, null);
			if (eventXMLFile.exists()) {
				in = eventXMLFile.getContents();
//				eventDef = EventDefParser.parse(in);
				eventDef = MaskatBeanIO.readEventDef(in);
			}
		} finally {
			IOUtils.closeQuietly(in);
		}
	}

	public void save() throws Exception {
		writeLayout();
		writeEvent();
	}
	
	private void writeLayout() throws Exception {
		// XXX パフォーマンス
//		StringWriter writer = new StringWriter(1024);
//
//		DefXmlSerializer serializer = new LayoutDefXmlSerializer((LayoutDef) layoutDef
//				.clone(), writer);
//		serializer.write();
		Iterator it = layoutDef.getTypedChildren(Layout.class);
		while (it.hasNext()) {
			((Layout) it.next()).packRadioGroup();
		}
		//why use clone()?, clone() will cause the propertychange.  
		String output = MaskatBeanIO.writeLayoutDef((LayoutDef)layoutDef/*.clone()*/);

//		byte[] bytes = writer.toString().getBytes("UTF-8");
		byte[] bytes = output.getBytes("UTF-8");
		layoutXMLFile.setContents(new ByteArrayInputStream(bytes), false, false, null);
	}

	private void writeEvent() throws Exception {
		if (eventDef == null) {
			return;
		}

		//use betwix's suppress to do this.
//		MaskatResources.clearDisabledDefElement(eventDef);
//		MaskatResources.clearBlankComponent(eventDef);

		eventDef.shrinkEvent();

		MaskatResources.marshallObj(eventDef, eventXMLFile, null);

		// event is shrinked so expand them
		eventDef.prepareEvent();
	}

	/**
	 * イベントのないコンポーネントは出力しない
	 * 
	 * @param layout
	 */
	public static void clearBlankComponent(EventDef layout) {
		List compsToDel = new ArrayList();
		for (Iterator it = layout.getAllComponents(); it != null && it.hasNext();) {
			Component comp = (Component) it.next();
			if (!comp.hasEvents()) {
				compsToDel.add(comp);
			}
		}
		for (int i = 0; i < compsToDel.size(); i++) {
			layout.removeChild(compsToDel.get(i));
		}
	}

	/**
	 * enable=falseのevent,param,resultは出力しないために、ここでLayoutオブジェクトからそういったイベントを削除する
	 * 
	 * @param layout
	 */
	public static void clearDisabledDefElement(EventDef layout) {
		for (Iterator it = layout.getAllComponents(); it != null && it.hasNext();) {
			Component comp = (Component) it.next();
			if (comp.hasEvents()) {
				for (Iterator it2 = comp.getAllEvents(); it2 != null && it2.hasNext();) {
					Event event = (Event) it2.next();
					if (!event.isEnable()) {
						it2.remove();
					} else {
						if (!event.getParam().isEnable()) {
							event.removeAllByType(Param.class);
						}
						if (!event.getResult().isEnable()) {
							event.removeAllByType(Result.class);
						}
					}
				}
			}
		}
		for (Iterator it = layout.getAllEvents(); it != null && it.hasNext();) {
			Event event = (Event) it.next();
			if (!event.isEnable()) {
				it.remove();
			} else {
				if (!event.getParam().isEnable()) {
					event.removeAllByType(Param.class);
				}
				if (!event.getResult().isEnable()) {
					event.removeAllByType(Result.class);
				}
			}
		}
	}

	public static String marshallObj(Object obj, IFile file, IProgressMonitor monitor)
			throws Exception {

//		StringWriter strWriter = new StringWriter(1024);
//
//		DefXmlSerializer serializer = new EventDefXmlSerializer(obj, strWriter);
//		serializer.write();
//		String result = strWriter.toString();
		String result = MaskatBeanIO.writeEventDef((EventDef)obj);

		if (!file.exists()) {
			file.create(new ByteArrayInputStream(new byte[0]), false, null);
		}
		file.setContents(new ByteArrayInputStream(result.getBytes("UTF-8")), false,
				false, monitor);
		return result;
	}

}
