package at.gv.egiz.pdfas.api.commons;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;

import at.gv.egiz.pdfas.api.PdfAs;
import at.knowcenter.wag.egov.egiz.cfg.PropertyTree;
import at.knowcenter.wag.egov.egiz.cfg.SettingsReader;
import at.knowcenter.wag.egov.egiz.sig.SignatureTypes;

// TODO exception types?
/**
 * Implementation class of the {@link DynamicSignatureProfile}. Don't use this class directly. Use {@link PdfAs} to create and the 
 * {@link DynamicSignatureProfile} interface for manipulation.
 * @author exthex
 *
 */
public class DynamicSignatureProfileImpl implements DynamicSignatureProfile {
   private String name;   
   private Properties newProps = new Properties();
   private int dynamicTypeCounter = 0;   
   private static Map profiles = new HashMap();
   private static ThreadLocal localProfiles = new ThreadLocal();
   private DynamicSignatureLifetimeEnum lifeMode; 
   

   private DynamicSignatureProfileImpl(DynamicSignatureLifetimeEnum mode, String name) {
      if (name != null) {
         this.name = name;
      } else {
         this.name = createDynamicTypeName();
      }
      this.lifeMode = mode;
   }
   
   public static DynamicSignatureProfileImpl createFromParent(String myUniqueName, String parentProfile, DynamicSignatureLifetimeEnum mode) {
      DynamicSignatureProfileImpl res = new DynamicSignatureProfileImpl(mode, myUniqueName);
      res.initFromParent(parentProfile);
      return res;
   }
   
   private void store() {
      if (lifeMode.equals(DynamicSignatureLifetimeEnum.MANUAL)) {
         profiles.put(this.getName(), this);
      } else if (lifeMode.equals(DynamicSignatureLifetimeEnum.AUTO)) {
         localProfiles.set(this);
      }
   }
   
   private void remove() {
      if (lifeMode.equals(DynamicSignatureLifetimeEnum.MANUAL)) {
         profiles.remove(this);
      } else if (lifeMode.equals(DynamicSignatureLifetimeEnum.AUTO)) {
         localProfiles.set(null);
      }
   }
   
   public static void disposeLocalProfile() {
      DynamicSignatureProfileImpl profile = (DynamicSignatureProfileImpl) localProfiles.get();
      if (profile != null) {       
         profile.dispose();
      }
   }
   
   public static DynamicSignatureProfileImpl createEmptyProfile(String myUniqueName, DynamicSignatureLifetimeEnum mode) {
      return new DynamicSignatureProfileImpl(mode, myUniqueName);      
   }
   
   public static DynamicSignatureProfileImpl loadProfile(String name) {
      return (DynamicSignatureProfileImpl) profiles.get(name);
   }
   
   private synchronized String createDynamicTypeName() {
      return "dynprofile__#" + this.dynamicTypeCounter++;     
   }

   /* (non-Javadoc)
    * @see at.gv.egiz.pdfas.api.commons.DynamicSignatureProfile#getName()
    */
   public String getName() {
      return name;
   }
   
   /* (non-Javadoc)
    * @see at.gv.egiz.pdfas.api.commons.DynamicSignatureProfile#setName(String)
    */
   public void setName(String uniqueName) {
      this.name = uniqueName;
   }

   public void setPropertyRaw(String key, String val) {
      this.newProps.setProperty(localPropName(key), val);
   }
   
   public String getPropertyRaw(String key) {
      return this.newProps.getProperty(localPropName(key));
   }
   
   private void assertPropExists(String key) {
      if (!this.newProps.containsKey(localPropName(key))) {
         throw new RuntimeException("property '" + key + "'not existing, cannot add one");
      }
   }

   private String localPropName(String key) {
      return "sig_obj." + this.name + "." + key;
   }
   
   /* (non-Javadoc)
    * @see at.gv.egiz.pdfas.api.commons.DynamicSignatureProfile#setFieldValue(java.lang.String, java.lang.String)
    */
   public void setFieldValue(String fieldName, String value) {
      if (SignatureTypes.isRequredSigTypeKey(fieldName)) {
         throw new RuntimeException("cannot set value for pre defined signature field names");
      }
      
      String key = "value." +fieldName;
      assertPropExists(key);
      setPropertyRaw(key, value);
   }
   
   /* (non-Javadoc)
    * @see at.gv.egiz.pdfas.api.commons.DynamicSignatureProfile#getFieldValue(java.lang.String)
    */
   public String getFieldValue(String fieldName) {
      return getPropertyRaw("value."+fieldName);           
   }

   private void initFromParent(String parentProfile) {
      try {
         SettingsReader cfg = null;

         cfg = SettingsReader.getInstance();         

         Properties props = cfg.getProperties();         
         for (Enumeration e = props.keys(); e.hasMoreElements();) {
            String oldKey = (String) e.nextElement();
            if (oldKey.startsWith("sig_obj." + parentProfile + ".")) {
               String newKey = StringUtils.replace(oldKey, parentProfile, name);
               String val = props.getProperty(oldKey);
               this.newProps.put(newKey, val);
            }
         }
         this.newProps.put("sig_obj.types." + name, "on");         
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }
   
   /* (non-Javadoc)
    * @see at.gv.egiz.pdfas.api.commons.DynamicSignatureProfile#register()
    */
   public synchronized void apply() {
      try {
         SettingsReader settings = SettingsReader.getInstance();         

         settings.getProperties().putAll(this.newProps);

         for (Enumeration e = newProps.keys(); e.hasMoreElements();) {
            String key = (String) e.nextElement();
            settings.getPTree().setKeyValue(key, newProps.getProperty(key));
         }
     
         SignatureTypes.getInstance().addSignatureType(this.name);
         store();
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }
   
   /* (non-Javadoc)
    * @see at.gv.egiz.pdfas.api.commons.DynamicSignatureProfile#dispose()
    */
   public synchronized void dispose() {
      try {
         SettingsReader.getInstance().getProperties().keySet().removeAll(newProps.keySet());
         
         PropertyTree root = SettingsReader.getInstance().getPTree();
         root.getSubTree("sig_obj").removeEntry(this.name);         
         
         SignatureTypes.getInstance().removeSignatureType(this.name);
         remove();
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }
   
}