/*
 * ConfigurationRecord.java
 *
 * Copyright 2011 John W Dawson
 *
 * This code is distributed under the terms of the GNU General Public License, version 3
 *
 * This class represents a record within the configuration data
 */

import org.jdom2.*;
import org.jdom2.output.*;
import org.jdom2.input.*;
import org.jdom2.filter.*;
import java.util.*;
import java.util.logging.*;
public class ConfigurationRecord
{
  static Logger log = Logger.getLogger ("LdapAddressBook");
  private Element base;
  protected EncryptorDecryptor encryptorDecryptor;
  
  private class ElementList extends ArrayList<Element>
  {}
    
  protected void setBase (Element base)
  {
    this.base = base;
  }
  
  public ConfigurationRecord ()
  {}
  
  public ConfigurationRecord (Element base, EncryptorDecryptor encryptorDecryptor)
  {
    setBase (base);
    this.encryptorDecryptor = encryptorDecryptor;
  } 
  
  public ConfigurationRecord (ConfigurationRecord record, EncryptorDecryptor encryptorDecryptor)
  {
    this (record.base, encryptorDecryptor);
  } 
  
  public String getAttributeValue (String name)
  {
    return this.base.getAttributeValue (name);
  }
  
  public String getElementValue (String key)
  {
    List list = this.base.getContent (new ElementFilter (key)); 

    if (list.isEmpty())
    {
      return null;
    }
    else
    {
      return ((Element) list.get(0)).getText();
    }
  }
  
  public String getEncryptedValue (String key)
  {
    return encryptorDecryptor.decrypt (getElementValue (key));
  }
  
  public String getElementValue (String key, String attributeName, String attributeValue)
  {
    List list = this.base.getContent (new ElementAttributeFilter (key, attributeName, attributeValue));
    if (list.isEmpty())
    {
      return null;
    }
    else
    {
      return ((Element) list.get(0)).getText();
    }
  }
  
  public String getElementValue (String recordKey, String attributeName, String attributeValue, String valueKey)
  {
    Iterator recordList = this.base.getDescendants (new ElementAttributeFilter (recordKey, attributeName, attributeValue));
    if (recordList.hasNext())
    {
      List valueList = ((Element) recordList.next()).getContent (new ElementFilter (valueKey)); 
      if (valueList.isEmpty())
      {
        return null;
      }
      else
      {
        return ((Element) valueList.get(0)).getText();
      }
    }
    else
    {
      return null;
    }
  }
  
  public boolean getBooleanValue (String key)
  {
    List list = this.base.getContent (new ElementFilter (key));

    if (list.isEmpty())
    {
      return false;
    }
    else
    {
      return ((Element) list.get(0)).getText().equals("1");
    }
  }
  
  public long getIntegerValue  (String key)
  {
    String value = this.getElementValue (key);
    return value != null ? Long.parseLong (value) : 0;
  } 
  
  public String getValue ()
  {
    return base.getText ();
  }
  
  public void addOrReplaceElement (String key, String attributeName, String attributeValue, String value)
  {
    // Get element, if it exists  
    List list = this.base.getContent (new ElementAttributeFilter (key, attributeName, attributeValue));
    Element element = list.isEmpty() ? null : (Element) list.get(0);
    
    // Check if null or empty value
    if (value == null || value.isEmpty())
    {
      // If so remove any existing element
      if (element != null)
      {
        base.removeContent (element);
      }
    }
    else
    {
      // Create element if it does not already exist and set attribute
      if (element == null)
      {
        element = new Element (key);
        element.setAttribute (attributeName, attributeValue);
        this.base.addContent (element); 
      }
      
      // Set the element's value
      element.setText (value);
    }
  }  
    
  public void addOrReplaceElement (String key, String value)
  {
    // Check if null or empty value
    if (value == null || value.isEmpty())
    {
      // If so remove any existing element
      base.removeChild (key);
    }
    else
    {
      // Get element if it exists
      List list = this.base.getContent (new ElementFilter (key)); 
      if (list.isEmpty())
      {
        // Create and add new element   
        Element element = new Element (key);
        element.setText (value);
        this.base.addContent (element); 
     }
      else
      {
        // Change value for existingelement
        ((Element) list.get(0)).setText (value);
      }
    }
  }
  
  public void addOrReplaceEncryptedElement (String key, String value)
  {
    addOrReplaceElement (key, encryptorDecryptor.encrypt (value));
  }

  public void updateElements (String key, String oldValue, String newValue)
  {
    List<Element> list = this.base.getContent (new ElementValueFilter (key, oldValue));
    for (Element element : list)
    { 
          
      // If new value is null delete element
      if (newValue == null)
      {
        element.getParent().removeContent (element);
      }
      // Otherwise update value
      else
      {
        element.setText (newValue);
      }
    }
  }  

  public List<ConfigurationRecord> getElementRecords (String key) 
  {
    List<ConfigurationRecord> records = new ArrayList<ConfigurationRecord> ();
    Iterator list = this.base.getDescendants (new ElementFilter (key)); 
    while (list.hasNext())
    {
      records.add (new ConfigurationRecord ((Element) list.next(), encryptorDecryptor));
    }
    return records;
  }
  
  public ConfigurationRecord addElementRecord (String key, String name)
  {
    // Create the element
    Element element = new Element (key);
    
    // Set the name attribute
    element.setAttribute ("name", name);
    
    // Add element to document
    this.base.addContent (element); 
    
    // Wrap element in a ConfigurationRecord & return
    return new ConfigurationRecord (element, encryptorDecryptor);
  }
  
  public void changeParent (ConfigurationRecord toParent)
  {
    // Remove from existing parent
    this.remove ();
    
    // Insert under new parent
    toParent.base.addContent (this.base);
  }
  
  // Find a record with given name and particular value of name attribute
  public ConfigurationRecord getElementRecord (String key, String name)
  {
    Iterator list = this.base.getDescendants (new ElementNameFilter (key, name));
    return list.hasNext() ? new ConfigurationRecord ((Element) list.next(), encryptorDecryptor) : null;
  }
 
  public ConfigurationRecord getParentRecord ()
  {
    Element parent = this.base.getParentElement ();
    if (parent != null)
    {
      return new ConfigurationRecord (parent, encryptorDecryptor);
    }
    else
    {
      return null;
    }
  }
  
  public boolean isParentRecord (ConfigurationRecord child)
  {
    return (this.base.isAncestor (child.base));
  }
  
  public void setName (String name)
  {
    base.setAttribute ("name", name);
  }
  
  public void remove ()
  {
    base.getParent().removeContent (this.base);
  }
  
  public String toString ()
  {
    return this.base.getAttributeValue ("name");
  }
  
  public boolean equals (ConfigurationRecord record)
  {
    // Two records are equal if their base element is the same object
    return record != null ? this.base.equals (record.base) : false;
  } 
}    
  