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.PluginMapProperty;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import java.util.Optional;
import java.util.Map.Entry;

class PluginMapPropertyConverter extends AbstractPropertyConverter<PluginMapProperty> {
   static final String TAG_PROPERTY_PLUGINMAP = "pluginMap";
   private static final String PROP_KEY = "key";

   @Override
   protected Class<PluginMapProperty> getPropertyClass() {
      return PluginMapProperty.class;
   }

   protected PluginMapProperty createNewProperty(String name, Optional<String> envid, Optional<String> context, Optional<String> variable) {
      return new PluginMapProperty(name, envid, context, variable);
   }

   protected void unmarshalCustomAttributes(PluginMapProperty property, HierarchicalStreamReader reader, UnmarshallingContext context) {
      while (reader.hasMoreChildren()) {
         reader.moveDown();
         if (!"plugin".equals(reader.getNodeName())) {
            throw new ConversionException("Inner tag must be of type plugin and not " + reader.getNodeName());
         }

         String key = XStreamHelper.readMandatoryAttribute(reader, "key");
         if (property.getPluginMap().containsKey(key)) {
            throw new ConversionException("Plugin has already key " + key + " defined. A pluginMap does not allow duplicates.");
         }

         Plugin plugin = (Plugin)context.convertAnother(property, Plugin.class);
         property.putPlugin(key, plugin);
         reader.moveUp();
      }
   }

   protected void marshalCustomAttributes(PluginMapProperty property, HierarchicalStreamWriter writer, MarshallingContext context) {
      for (Entry<String, Plugin> entry : property.getPluginMap().entrySet()) {
         context.convertAnother(entry.getValue(), new PluginMapPropertyConverter.PluginMapConverter(entry));
      }
   }

   @Override
   protected String tagName() {
      return "pluginMap";
   }

   private static final class PluginMapConverter extends PluginConverter {
      private final Entry<String, Plugin> entry;

      private PluginMapConverter(Entry<String, Plugin> entry) {
         this.entry = entry;
      }

      @Override
      protected void writeAdditionalAttriutes(HierarchicalStreamWriter writer, Plugin plugin) {
         writer.addAttribute("key", this.entry.getKey());
      }
   }
}
