package com.airlock.iam.plugin.framework.infrastructure.xstream;

import com.airlock.iam.plugin.framework.domain.model.Plugin;
import com.airlock.iam.plugin.framework.domain.model.property.PluginListProperty;
import com.airlock.iam.plugin.framework.domain.model.property.PluginMapProperty;
import com.airlock.iam.plugin.framework.domain.model.property.PluginProperty;
import com.airlock.iam.plugin.framework.domain.model.property.Property;
import com.airlock.iam.plugin.framework.domain.model.property.SimpleListProperty;
import com.airlock.iam.plugin.framework.domain.model.property.SimpleProperty;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

class PluginConverter implements Converter {
   static final String TAG_PLUGIN = "plugin";
   private static final String PROP_UUID = "uuid";
   private static final String PROP_COMMENT = "comment";
   private static final String PROP_NAME = "id";
   private static final String PROP_UUIDREF = "uuidref";
   private static final String PROP_PARENTREF = "ref";
   private static final String PROP_TYPE = "class";

   public final boolean canConvert(Class type) {
      return Plugin.class.equals(type);
   }

   public final void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
      Plugin plugin = (Plugin)source;
      writer.startNode("plugin");
      this.writeAttributes(writer, plugin);
      this.writeInnerNodes(context, plugin);
      writer.endNode();
   }

   private void writeAttributes(HierarchicalStreamWriter writer, Plugin plugin) {
      this.writeStandardAttributes(writer, plugin);
      this.writeAdditionalAttriutes(writer, plugin);
   }

   protected void writeAdditionalAttriutes(HierarchicalStreamWriter writer, Plugin plugin) {
   }

   private void writeStandardAttributes(HierarchicalStreamWriter writer, Plugin plugin) {
      XStreamHelper.writeAttributeIfValueIsPresent(writer, "class", plugin.getType());
      XStreamHelper.writeAttributeIfValueIsPresent(writer, "id", plugin.getName());
      XStreamHelper.writeAttributeIfValueIsPresent(writer, "uuid", plugin.getUuid());
      XStreamHelper.writeAttributeIfValueIsPresent(writer, "ref", plugin.getTemplate());
      XStreamHelper.writeAttributeIfValueIsPresent(writer, "uuidref", plugin.getUuidRef());
      XStreamHelper.writeAttributeIfValueIsPresent(writer, "comment", plugin.getComment());
   }

   private void writeInnerNodes(MarshallingContext context, Plugin plugin) {
      for (Property property : plugin.getProperties()) {
         context.convertAnother(property);
      }
   }

   public final Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
      XStreamHelper.ensureInNode("plugin", reader);
      Plugin plugin = new Plugin();
      this.unmarshalAttributes(reader, plugin);

      while (reader.hasMoreChildren()) {
         reader.moveDown();
         plugin.addProperty(this.unmarshalProperties(reader, context, plugin));
         reader.moveUp();
      }

      return plugin;
   }

   private Property unmarshalProperties(HierarchicalStreamReader reader, UnmarshallingContext context, Plugin plugin) {
      if ("property".equals(reader.getNodeName())) {
         return (Property)context.convertAnother(plugin, SimpleProperty.class);
      } else if ("propertyList".equals(reader.getNodeName())) {
         return (SimpleListProperty)context.convertAnother(plugin, SimpleListProperty.class);
      } else if ("pluginProperty".equals(reader.getNodeName())) {
         return (PluginProperty)context.convertAnother(plugin, PluginProperty.class);
      } else if ("pluginList".equals(reader.getNodeName())) {
         return (PluginListProperty)context.convertAnother(plugin, PluginListProperty.class);
      } else if ("pluginMap".equals(reader.getNodeName())) {
         return (PluginMapProperty)context.convertAnother(plugin, PluginMapProperty.class);
      } else {
         throw new ConversionException(reader.getNodeName() + " is unknown");
      }
   }

   private void unmarshalAttributes(HierarchicalStreamReader reader, Plugin plugin) {
      plugin.setTemplate(reader.getAttribute("ref"));
      plugin.setUuid(reader.getAttribute("uuid"));
      plugin.setUuidRef(reader.getAttribute("uuidref"));
      plugin.setComment(reader.getAttribute("comment"));
      plugin.setName(reader.getAttribute("id"));
      plugin.setType(reader.getAttribute("class"));
   }
}
