package charactermanaj.model.io;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import charactermanaj.model.CustomLayerOrder;
import charactermanaj.model.CustomLayerOrderKey;
import charactermanaj.model.Layer;
import charactermanaj.model.PartsCategory;

public class CustomLayerOrderXMLWriter {

	/**
	 * WorkingSetのバージョン
	 */
	public static final String VERSION_SIG_1_0 = "1.0";

	/**
	 * WorkingSetのXMLファイルの名前空間
	 */
	public static final String NS = "http://charactermanaj.osdn.jp/schema/customlayerorder.xsd";

	public void write(Map<CustomLayerOrderKey, List<CustomLayerOrder>> map, OutputStream outstm) throws IOException {
		Document doc = createDocument(map);

		// output xml
		TransformerFactory txFactory = TransformerFactory.newInstance();
		txFactory.setAttribute("indent-number", Integer.valueOf(4));
		Transformer tfmr;
		try {
			tfmr = txFactory.newTransformer();
		} catch (TransformerConfigurationException ex) {
			throw new RuntimeException("JAXP Configuration Failed.", ex);
		}
		tfmr.setOutputProperty(OutputKeys.INDENT, "yes");

		// JDK-4504745 : javax.xml.transform.Transformer encoding does not work properly
		// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4504745
		final String encoding = "UTF-8";
		tfmr.setOutputProperty("encoding", encoding);
		try {
			tfmr.transform(new DOMSource(doc), new StreamResult(
					new OutputStreamWriter(outstm, Charset.forName(encoding))));

		} catch (TransformerException ex) {
			IOException ex2 = new IOException("XML Convert failed.");
			ex2.initCause(ex);
			throw ex2;
		}
	}

	public Document createDocument(Map<CustomLayerOrderKey, List<CustomLayerOrder>> map) {
		if (map == null) {
			map = Collections.emptyMap();
		}

		Document doc;
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			factory.setNamespaceAware(true);
			DocumentBuilder builder = factory.newDocumentBuilder();
			doc = builder.newDocument();
		} catch (ParserConfigurationException ex) {
			throw new RuntimeException("JAXP Configuration failed.", ex);
		}

		// ルートの登録
		Element root = doc.createElementNS(NS, "custom-layer-orders");
		root.setAttribute("version", VERSION_SIG_1_0);

		root.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:xsi",
				XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
		root.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:xml",
				XMLConstants.XML_NS_URI);
		root.setAttribute("xsi:schemaLocation", NS + " customlayerorder.xsd");

		for (Map.Entry<CustomLayerOrderKey, List<CustomLayerOrder>> entry : map.entrySet()) {
			CustomLayerOrderKey orderKey = entry.getKey();
			String id = orderKey.getId();
			String displayName = orderKey.getDisplayName();

			Map<String, String> localizedNames = orderKey.getLocalizedNames();
			String defaultName = localizedNames.get(CustomLayerOrderKey.DEFAULT_NAME_KEY);
			if (defaultName == null) {
				defaultName = displayName;
			}

			List<CustomLayerOrder> orders = entry.getValue();

			// パターンの登録
			Element elmPattern = doc.createElementNS(NS, "pattern");
			elmPattern.setAttribute("id", id);
			elmPattern.setAttribute("name", defaultName);
			root.appendChild(elmPattern);

			// ローカライズされた名前
			for (Map.Entry<String, String> localizedEntry : localizedNames.entrySet()) {
				String localizedLang = localizedEntry.getKey();
				String localizedName = localizedEntry.getValue();
				if (!CustomLayerOrderKey.DEFAULT_NAME_KEY.equals(localizedLang)) {
					Element elmLocalizedName = doc.createElementNS(NS, "localized-name");
					elmLocalizedName.setAttribute("lang", localizedLang);
					elmLocalizedName.setAttribute("name", localizedName);
					elmPattern.appendChild(elmLocalizedName);
				}
			}

			// レイヤーマッピング
			for (CustomLayerOrder order : orders) {
				Element elmMapping = doc.createElementNS(NS, "mapping");
				elmPattern.appendChild(elmMapping);
				PartsCategory category = order.getCategory();
				Layer layer = order.getLayer();
				float layerOrder = order.getLayerOrder();

				elmMapping.setAttribute("category", category.getCategoryId());
				elmMapping.setAttribute("layer", layer.getId());
				elmMapping.setAttribute("order", Float.toString(layerOrder));
			}
		}

		doc.appendChild(root);
		return doc;
	}
}
