/*
Copyright (C) 2000-2010  Ministere de la culture et de la communication (France), AJLSM
See LICENCE file
*/
package fr.gouv.culture.sdx.utils.database;

import fr.gouv.culture.sdx.exception.SDXException;
import fr.gouv.culture.sdx.exception.SDXExceptionCode;
import fr.gouv.culture.sdx.utils.Utilities;
import org.apache.avalon.framework.logger.LogEnabled;

import java.io.PrintStream;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;

/**
 * An entity from a _database, basically a list of properties.
 *
 * <p>
 * The entity is the database basic unit of management. It can have properties,
 * all properties have a unique name but a property may have multiple values.
 * <p>
 * TODO?: this class is not completely implemented.
 */
public class DatabaseEntity implements LogEnabled {

    /** Avalon super.getLog() to write information. */
    private org.apache.avalon.framework.logger.Logger logger;

    /**The table of properties for an entity*/
    private Hashtable properties;

    /**The id for the database entity*/
    private String id;

    /**Creates an instance with the specified id
     *
     * @param id    The id to assign to this entity
     */
    public DatabaseEntity(String id) throws SDXException {
        if (!Utilities.checkString(id)) {
            //we don't have a good id and need one
            String[] args = new String[1];
            args[0] = this.toString();
            throw new SDXException(null, SDXExceptionCode.ERROR_INVALID_STRING, args, null);
        } else {
            setId(id);
            properties = new Hashtable();
        }
    }

    /**Returns the id for this entity*/
    public String getId() {
        return id;
    }

    /**Modifies the id for this entity*/
    public void setId(String id) {
        this.id = id;
    }

    /**
     * Sets the super.getLog().
     *
     * @param   logger      The super.getLog() to use.
     */
    public void enableLogging(org.apache.avalon.framework.logger.Logger logger) {
        this.logger = logger;
    }

    public void addProperty(String name, String value) {
        //i think here we need to check if the prop exists, if so then we get the values and and build a new Props object including the new value?
        if (!Utilities.checkString(name)) return;
        if (properties.containsKey(name)) {
            //getting the exising _context object from the hashtable
            Property prop = (Property) properties.get(name);
            //retrieving the _context values
            String[] oldValues = prop.getValues();
            //adding the old values
            HashSet newValues = new HashSet();
            for (int i = 0; i < oldValues.length; i++) {
                String oldValue = oldValues[i];
                newValues.add(oldValue);
            }
            //adding the new value
            newValues.add(value);
            //adding the new values to the property
            prop.setValues((String[]) newValues.toArray(new String[0]));
            //adding property to the properties hashtable
            properties.put(name, prop);
        } else
            properties.put(name, new Property(name, value));
    }

    /**Adds or updates a property with a list of values to a DatabaseEntity.
     * <p>If a property by the name specified already exists,
     * the values are added to the property's list of values, if one wishes to replace
     * the existing property, the property should be deleted first using the deleteProperty(String name)
     * in this class, and then re-add the property
     *
     * @param name  The name to assign to the Property
     * @param values An array of String values for the property
     *
     */
    public void addProperties(String name, String[] values) {
        if (properties.containsKey(name)) {
            //getting the exising _context object from the hashtable
            Property prop = (Property) properties.get(name);
            //retrieving the _context values
            HashSet newValues = new HashSet();
            for (int i = 0; i < values.length; i++) {
                String value = values[i];
                newValues.add(value);
            }
            String[] oldValues = prop.getValues();
            //adding the new value to the array
            for (int i = 0; i < oldValues.length; i++) {
                String oldValue = oldValues[i];
                newValues.add(oldValue);
            }
            //adding the new values to the property
            prop.setValues((String[]) newValues.toArray(new String[0]));
            //adding property to the properties hashtable
            properties.put(name, prop);
        } else {
            Property prop = new Property();
            prop.enableLogging(logger);
            prop.setName(name);
            prop.setValues(values);
            properties.put(name, prop);
        }
    }

    /**Get's the first value of the Property specified by a name
     *
     * @param name  The name of an existing the Property
     * @return      The first value of the specified property
     */
    public String getProperty(String name) {

        if (!Utilities.checkString(name)) return null;
        Object o = properties.get(name);
        if (o == null) return null;

        if (o instanceof String)
            return (String) o;
        else {
            // if (((Enumeration)o).hasMoreElements())
            if (o instanceof Property)
            //return (String)((Enumeration)o).nextElement();
                return ((Property) o).getValue();
            else
                return null;
        }
    }

    /**Get's the values of a of the Property specified by a name
     *
     * @param name  The name of an existing the Property
     * @return      A String array of the values of the specified property
     */
    public String[] getPropertyValues(String name) {
        if (name == null) return new String[0];
        Property prop = (Property) properties.get(name);
        if (prop == null)
            return new String[0];
        else
            return prop.getValues();
    }

    /**Get's all the ContextKeys of this Database Entity
     *
     * @return  An array of Property objects
     */
    public Property[] getProperties() {
        //TODO?
        Enumeration values = properties.elements();
        Property[] props = new Property[properties.size()];
        while (values.hasMoreElements()) {

            for (int i = 0; i < properties.size(); i++) {
                props[i] = (Property) values.nextElement();
            }
        }
        return props;
/*
        Object o = properties.get(name);
        if (o instanceof Enumeration)
            return (Enumeration)o;
        else {
            Vector v = new Vector();
            v.add(o);
            return v.elements();
        }
*/

    }

    /**Deletes a properties object
     *
     * @param name  The name of the property to be deleted
     * @throws SDXException If the name is null or an empty String.
     */
    public void deleteProperty(String name) throws SDXException {
        if (!Utilities.checkString(name)) {
            //we don't have an name and need one
            String[] args = new String[1];
            args[0] = this.toString();
            throw new SDXException(logger, SDXExceptionCode.ERROR_INVALID_STRING, args, null);
        }

        if (properties.containsKey(name))
        /*we should never have a null properties as it is created in the constructor,
        and we have a name and want to delete the entrie if it exists*/
            properties.remove(name);
    }

    /**Delete a valud from a property of this entity
     *
     * @param propertyName  The propertyname
     * @param value         The value to delete
     */
    public void deleteValue(String propertyName, String value) throws SDXException {
        if (Utilities.checkString(value)) {
            if (properties.containsKey(propertyName)) {
                /*we should never have a null properties as it is created in the constructor,
                and we have a name and want to delete the entrie if it exists*/
                Property prop = (Property) properties.get(propertyName);
                if (prop != null)
                    prop.deleteValue(value);

            }
        }
    }

    /**
     * Dumps a debug info of this object.
     */
    public void dump(PrintStream ps) {
        if (ps != null) {
            ps.println("");
            ps.println("Dumping debug information for entity id = " + this.getId());
            Property[] props = this.getProperties();
            for (int i = 0; i < props.length; i++) {
                ps.print(" " + props[i].getName() + " = ");
                String[] vals = props[i].getValues();
                for (int j = 0; j < vals.length; j++) ps.print("//" + vals[j]);
                ps.println();
            }
        }
    }

    public boolean containsProperty(String name) {
        return properties.containsKey(name);
    }

    public boolean containsValue(String propertyName, String propertyValue) {
        if (!properties.containsKey(propertyName))
            return false;
        Property prop = (Property) properties.get(propertyName);
        return prop.containsValue(propertyValue);

    }


}
