package org.bench4q.master.domain.factory;

import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.bench4q.master.domain.entity.plugin.BehaviorInfo;
import org.bench4q.master.domain.entity.plugin.CheckBoxType;
import org.bench4q.master.domain.entity.plugin.ChoiceType;
import org.bench4q.master.domain.entity.plugin.ComboType;
import org.bench4q.master.domain.entity.plugin.DateType;
import org.bench4q.master.domain.entity.plugin.FieldType;
import org.bench4q.master.domain.entity.plugin.FileType;
import org.bench4q.master.domain.entity.plugin.Group;
import org.bench4q.master.domain.entity.plugin.NFieldType;
import org.bench4q.master.domain.entity.plugin.ParamInfo;
import org.bench4q.master.domain.entity.plugin.ParamType;
import org.bench4q.master.domain.entity.plugin.ParamsContainer;
import org.bench4q.master.domain.entity.plugin.PluginInfo;
import org.bench4q.master.domain.entity.plugin.PluginUI;
import org.bench4q.master.domain.entity.plugin.RadioButtonType;
import org.bench4q.master.domain.entity.plugin.SelectType;
import org.bench4q.master.domain.entity.plugin.TableType;
import org.bench4q.master.exception.Bench4QRunTimeException;
import org.bench4q.master.exception.ExceptionLog;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.stereotype.Component;

@Component
public class PluginEntityFactory {
	private Logger logger = Logger.getLogger(PluginEntityFactory.class);
	private final String[] behaviorTypes = { "behavior", "timer", "control" };

	private Document getDocument(String pluginUI) {
		try {
			return DocumentHelper.parseText(pluginUI);

		} catch (DocumentException e) {
			logger.info(ExceptionLog.getStackTrace(e));
			return null;
		}
	}

	public PluginUI createPluginUIWithOutId(String pluginUIContent) {
		Element root = getDocument(pluginUIContent).getRootElement();
		PluginUI pluginUI = new PluginUI();
		pluginUI.setPluginInfo(createPluginInfoWithOutId(XmlParseHelper
				.getOneChildElementByName(root, "plugin")));
		pluginUI.setBehaviorInfos(createBehaviorInfosWithOutId(
				XmlParseHelper.getChildElements(root, behaviorTypes), pluginUI));
		pluginUI.setCreateDateTime(new Date());
		return pluginUI;
	}

	private Set<BehaviorInfo> createBehaviorInfosWithOutId(
			List<Element> elements, PluginUI pluginUI) {
		Set<BehaviorInfo> behaviorInfos = new HashSet<BehaviorInfo>();
		for (Element element : elements) {
			behaviorInfos.add(createBehaviorInfoWithOutId(element, pluginUI));
		}
		return behaviorInfos;
	}

	private BehaviorInfo createBehaviorInfoWithOutId(Element element,
			PluginUI pluginUI) {
		BehaviorInfo behaviorInfo = new BehaviorInfo();
		behaviorInfo.setBehaviorType(element.getName());
		behaviorInfo.setName(XmlParseHelper.getAttribute(element, "name"));
		behaviorInfo.setNameCN(XmlParseHelper.getAttribute(element, "nameCN"));
		behaviorInfo.setPluginUI(pluginUI);
		Element paramsElement = XmlParseHelper.getOneChildElementByName(
				element, "params");
		behaviorInfo.setParams(createParamInfos(
				XmlParseHelper.getChildElementsByName(paramsElement, "param"),
				behaviorInfo));
		behaviorInfo.setGroups(createGroups(
				XmlParseHelper.getChildElementsByName(paramsElement, "group"),
				behaviorInfo));
		return behaviorInfo;
	}

	private PluginInfo createPluginInfoWithOutId(Element element) {

		PluginInfo pluginInfo = new PluginInfo();
		pluginInfo.setName(XmlParseHelper.getAttribute(element, "name"));
		pluginInfo.setNameCN(XmlParseHelper.getAttribute(element, "nameCN"));
		Element paramsElement = XmlParseHelper.getOneChildElementByName(
				element, "params");
		pluginInfo.setGroups(createGroups(
				XmlParseHelper.getChildElementsByName(paramsElement, "group"),
				pluginInfo));
		pluginInfo.setParams(createParamInfos(
				XmlParseHelper.getChildElementsByName(paramsElement, "param"),
				pluginInfo));
		return pluginInfo;

	}

	private Set<Group> createGroups(List<Element> groupElements,
			ParamsContainer baseContainer) {
		if (groupElements.size() == 0) {
			return null;
		}
		Set<Group> groups = new HashSet<Group>();
		for (Element element : groupElements) {
			groups.add(createGroup(element, baseContainer));
		}
		return groups;
	}

	private Group createGroup(Element groupElement,
			ParamsContainer fatherParamsContainer) {
		if (!groupElement.getName().equals("group")) {
			logger.info("element is not a group:" + groupElement.getName());
			return null;
		}
		Group paramsContainer = new Group();
		paramsContainer.setName(XmlParseHelper.getAttribute(groupElement,
				"name"));
		paramsContainer.setNameCN(XmlParseHelper.getAttribute(groupElement,
				"nameCN"));

		List<Element> paramElements = XmlParseHelper.getChildElementsByName(
				groupElement, "param");

		if (paramElements.size() > 0) {
			paramsContainer.setParams(createParamInfos(paramElements,
					paramsContainer));
		}
		paramsContainer.setParamsContainer(fatherParamsContainer);
		return paramsContainer;
	}

	private Set<ParamInfo> createParamInfos(List<Element> paramElements,
			ParamsContainer paramsContainer) {
		if (paramElements.size() == 0) {
			return null;
		}
		Set<ParamInfo> paramInfos = new HashSet<ParamInfo>();
		for (Element paramElement : paramElements) {
			paramInfos.add(createParamInfoWithOutId(paramElement,
					paramsContainer));
		}
		return paramInfos;
	}

	private ParamInfo createParamInfoWithOutId(Element paramElement,
			ParamsContainer paramsContainer) {
		if (!paramElement.getName().equals("param")) {
			logger.info("element is not a param:" + paramElement.getName());
			return null;
		}
		if (XmlParseHelper.getAttribute(paramElement, "name") == null) {
			return null;
		}
		ParamInfo paramInfo = new ParamInfo();
		paramInfo.setLable(XmlParseHelper.getAttribute(paramElement, "label"));
		paramInfo.setName(XmlParseHelper.getAttribute(paramElement, "name"));
		paramInfo.setRequired(XmlParseHelper.getAttribute(paramElement,
				"required"));
		paramInfo.setParamType(ParamTypeFactory
				.createParamTypeInfo(XmlParseHelper
						.getSingleChild(paramElement)));
		paramInfo.setParamsContainer(paramsContainer);
		return paramInfo;
	}

	private static class ParamTypeFactory {
		public static ParamType createParamTypeInfo(Element element) {
			String type = element.getName();
			if (type.equals("field")) {
				return createFieldType(element);
			} else if (type.equals("nfield")) {
				return createNFieldType(element);
			} else if (type.equals("table")) {
				return createTableType(element);
			} else if (type.equals("radiobutton")) {
				return createRadioButtonType(element);
			} else if (type.equalsIgnoreCase("checkbox")) {
				return createCheckBoxType(element);
			} else if (type.equals("combo")) {
				return createComboType(element);
			} else if (type.equals("file")) {
				return createFileType(element);
			} else if (type.equals("date")) {
				return createDateType(element);
			} else if (type.equals("dropdownbox")) {
				return createSelectType(element);
			} else {
				throw new Bench4QRunTimeException("no such type:" + type);
			}
			// switch (type) {
			// case "field":
			// return createFieldType(element);
			// case "nfield":
			// return createNFieldType(element);
			// case "table":
			// return createTableType(element);
			// case "radiobutton":
			// return createRadioButtonType(element);
			// case "checkbox":
			// return createCheckBoxType(element);
			// case "combo":
			// return createComboType(element);
			// case "file":
			// return createFileType(element);
			// case "date":
			// return createDateType(element);
			// case "dropdownbox":
			// return createSelectType(element);
			// default:
			// throw new Bench4QRunTimeException("no such type:" + type);
			// }

		}

		private static FieldType createFieldType(Element element) {
			FieldType fieldType = new FieldType();
			fieldType.setType("field");
			fieldType.setSize(Integer.parseInt(XmlParseHelper.getAttribute(
					element, "size")));
			fieldType.setText(XmlParseHelper.getAttribute(element, "text"));
			return fieldType;
		}

		private static NFieldType createNFieldType(Element element) {
			NFieldType nFieldType = new NFieldType();
			nFieldType.setType("nfield");
			if (XmlParseHelper.getAttribute(element, "size") != null)
				nFieldType.setSize(Integer.parseInt(XmlParseHelper
						.getAttribute(element, "size")));
			nFieldType.setText(XmlParseHelper.getAttribute(element, "text"));
			return nFieldType;
		}

		private static TableType createTableType(Element element) {
			TableType tableType = new TableType();
			tableType.setType("table");
			tableType.setCols(XmlParseHelper.getAttribute(element, "cols"));
			return tableType;
		}

		private static RadioButtonType createRadioButtonType(Element element) {
			RadioButtonType radioButtonType = new RadioButtonType();
			radioButtonType.setType("radioButton");
			radioButtonType.setChoices(createChoiceTypes(
					XmlParseHelper.getChildElements(element), radioButtonType));
			return radioButtonType;
		}

		private static CheckBoxType createCheckBoxType(Element element) {
			CheckBoxType checkBoxType = new CheckBoxType();
			checkBoxType.setType("checkBox");
			checkBoxType.setChoiceList(createChoiceTypes(
					XmlParseHelper.getChildElements(element), checkBoxType));
			return checkBoxType;
		}

		private static ComboType createComboType(Element element) {
			ComboType comboType = new ComboType();
			comboType.setType("combo");
			comboType.setChoiceList(createChoiceTypes(
					XmlParseHelper.getChildElements(element), comboType));
			return comboType;
		}

		private static List<ChoiceType> createChoiceTypes(
				List<Element> elements, ParamType parentContainer) {
			List<ChoiceType> choiceTypes = new LinkedList<ChoiceType>();
			for (Element element : elements) {
				choiceTypes.add(createChoiceType(element, parentContainer));
			}
			return choiceTypes;

		}

		private static ChoiceType createChoiceType(Element element,
				ParamType parentContainer) {
			ChoiceType choiceType = new ChoiceType();
			choiceType.setValue(XmlParseHelper.getAttribute(element, "value"));
			choiceType.setDefaultValue(Boolean.parseBoolean(XmlParseHelper
					.getAttribute(element, "default")));
			choiceType.setParentContainer(parentContainer);
			return choiceType;

		}

		private static FileType createFileType(Element element) {

			FileType fileType = new FileType();
			fileType.setText(XmlParseHelper.getAttribute(element, "text"));
			fileType.setType("file");
			return fileType;

		}

		private static DateType createDateType(Element element) {
			DateType dateType = new DateType();
			if (XmlParseHelper.getAttribute(element, "size") != null) {
				dateType.setSize(Integer.parseInt(XmlParseHelper.getAttribute(
						element, "size")));
			}
			dateType.setType("date");
			return dateType;
		}

		private static SelectType createSelectType(Element element) {
			SelectType selectType = new SelectType();
			if (XmlParseHelper.getAttribute(element, "size") != null) {
				selectType.setSize(Integer.parseInt(XmlParseHelper
						.getAttribute(element, "size")));
			}
			String optionsString = XmlParseHelper.getAttribute(element, "rows");
			if (optionsString == null) {
				Logger.getLogger(ParamTypeFactory.class).info(
						"rows is null in dropdownbox");
				return null;
			}
			String[] options = optionsString.split(";");
			selectType.setOptions(new HashSet<String>(Arrays.asList(options)));
			selectType.setType("select");
			return selectType;
		}
	}

}

class XmlParseHelper {
	public static Element getSingleChild(Element element) {
		List<Element> choiceElements = new LinkedList<Element>();
		for (@SuppressWarnings("rawtypes")
		Iterator iterator = element.elementIterator(); iterator.hasNext();) {
			choiceElements.add((Element) iterator.next());
		}
		if (choiceElements.size() == 0)
			return null;
		else
			return choiceElements.get(0);
	}

	public static String getAttribute(Element element, String attributeName) {
		for (@SuppressWarnings("rawtypes")
		Iterator iterator = element.attributeIterator(); iterator.hasNext();) {
			Attribute attribute = (Attribute) iterator.next();
			if (attribute.getName().equals(attributeName)) {
				return attribute.getValue();

			}

		}
		return null;
	}

	public static List<Element> getChildElements(Element element) {
		List<Element> choiceElements = new LinkedList<Element>();
		for (@SuppressWarnings("rawtypes")
		Iterator iterator = element.elementIterator(); iterator.hasNext();) {
			choiceElements.add((Element) iterator.next());
		}
		return choiceElements;
	}

	public static Element getOneChildElementByName(Element element,
			String elementName) {
		for (@SuppressWarnings("rawtypes")
		Iterator iterator = element.elementIterator(); iterator.hasNext();) {
			Element childElement = (Element) iterator.next();
			if (childElement.getName().equals(elementName))

				return childElement;
		}
		return null;
	}

	@SuppressWarnings("rawtypes")
	public static List<Element> getChildElementsByName(Element element,
			String elementName) {

		List<Element> elements = new LinkedList<Element>();
		for (Iterator iterator = element.elementIterator(); iterator.hasNext();) {
			Element childElement = (Element) iterator.next();
			if (childElement.getName().equals(elementName))
				elements.add(childElement);
		}
		return elements;

	}

	public static List<Element> getChildElements(Element element,
			String[] elementNames) {
		List<Element> elements = new LinkedList<Element>();
		for (String elementName : elementNames) {
			elements.addAll(getChildElementsByName(element, elementName));
		}
		return elements;
	}

}
