package org.jcon.param.tree;

import org.jcon.param.Param;
import org.jcon.param.StringVector;
import java.util.Vector;

/**
 * Converts a PropList to and from a Param. Usually this
 * is the root PropList. All static methods. Converter pattern.
 * <p>
 * Internal note - Comments in Element lists are stored as
 * a String in the Vector and in the Comment. *** ???
 *
 * @author Jack Harich
 */
public class PropListConverter {

//---------- Private Fields ------------------------------
private static final String COMMENT = "//";

//---------- Public Methods ------------------------------
/**
 * Converts the Param to a PropList. The param must be a
 * PropList, which all root Params are. Note the PropList
 * name is initially null. The param should have a
 * BranchName fact if validation is wanted. RECURSIVE
 */
 // Similar to ParamConverter.convertToString() 
public static PropList toPropList(Param param) {
    PropList propList = new PropList();
    Vector keys = param.getAllKeys();
    for (int i = 0; i < keys.size(); i++) {
        String key = (String)keys.elementAt(i);
        Object parameter = param.get(key);
        addPropListProperty(propList, key, parameter);
// *** Need to avoid reserved words like "BranchName"
// Or adopt standard, such as whats in XML
        if (key.equals("BranchName"))
            propList.setBranchName((String)parameter);
    }
    return propList;
}
// Similar to ParamConverter.getParamText().
// Note Param structure is not OO, so long if series.
// This indicates redesign needed.
private static void addPropListProperty(PropList propList,
        String key, Object parameter) {

    if (key.startsWith(COMMENT)) {
        Comment comment = new Comment();
        comment.setName(key);
        comment.setParentDatum(propList);
        comment.setValue((String)parameter);
        propList.insertDatum(key, comment, null);

    } else if (parameter instanceof String) {
        Attribute attribute = new Attribute();
        attribute.setName(key);
        attribute.setParentDatum(propList);
        attribute.setValue((String)parameter);
        propList.insertDatum(key, attribute, null);

    } else if (parameter instanceof Param) {
        // RECURSE
        PropList childPropList = toPropList((Param)parameter);
        childPropList.setName(key);
        childPropList.setParentDatum(propList);
        propList.insertDatum(key, childPropList, null);

    } else if (parameter instanceof StringVector) {
        LineList lineList = new LineList();
        lineList.setName(key);
        lineList.setParentDatum(propList);
        lineList.setLines((StringVector)parameter);
        propList.insertDatum(key, lineList, null);

    } else if (parameter instanceof Vector) {
        ElementList elementList = new ElementList();
        elementList.setName(key);
        elementList.setParentDatum(propList);
        propList.insertDatum(key, elementList, null);
        
        // Insert each. Name is set internally by ElementList.
        Vector items = (Vector)parameter;
        int size = items.size();
        for (int i = 0; i < size; i++) {
            Object item = items.elementAt(i);
            if (item instanceof Param) {
                PropList childPropList = toPropList(
                    (Param)item); // RECURSE                
                childPropList.setParentDatum(elementList);
                elementList.insertDatum(null, childPropList, null);
                
            } else if (item instanceof String) {
                Comment comment = new Comment();
                comment.setParentDatum(elementList);
                comment.setValue((String)item);
                elementList.insertDatum(null, comment, null);
            
            } else {
                throw new IllegalArgumentException(
                    "Unknown type in Vector " + item);
            }
        }
    } else {
        throw new IllegalArgumentException("key: '" + key + "' contains unsupported type: "
            + parameter.getClass().getName());
    }
}
/**
 * Converts a PropList to a Param.
 */
// RECURSIVE
public static Param toParam(PropList propList) {
    Param param = new Param();
    int commentCounter = 0;

    Vector properties = propList.getDatums();
    for (int i = 0; i < properties.size(); i++) {
        Datum datum = (Datum)properties.elementAt(i);
        //print(".toParam() - adding: " + datum);
        addParamProperty(param, datum, commentCounter);
    }

    return param;
}
private static void addParamProperty(Param param,
        Datum datum, int commentCounter) {

    if (datum instanceof PropList) {
        PropList propList = (PropList)datum;
        Param childParam = toParam(propList); // RECURSE
        param.put(propList.getName(), childParam);

    } else if (datum instanceof Attribute) {
        Attribute attribute = (Attribute)datum;
        param.put(attribute.getName(), attribute.getValue() );

    } else if (datum instanceof Comment) {
        Comment comment = (Comment)datum;
        param.put(comment.getName(), comment.getValue() );

    } else if (datum instanceof LineList) {
        LineList lineList = (LineList)datum;
        StringVector stringVector = lineList.createStringVector();
        param.put(lineList.getName(), stringVector);

    } else if (datum instanceof ElementList) {
        ElementList elementList = (ElementList)datum;
        Vector vector = elementList.createVector();
        vector = convertElementsToParamVector(vector);
        param.put(elementList.getName(), vector);

    } else {
        throw new IllegalArgumentException("Unknown datum "
            + datum.getClass().getName());
    }
}
//---------- Private Methods -----------------------------
private static Vector convertElementsToParamVector(Vector elements) {
    Vector newElements = new Vector();
    int size = elements.size();
    for (int i = 0; i < size; i++) {
        Datum datum = (Datum)elements.elementAt(i);
        if (datum instanceof PropList) {
            // RECURSE
            Param param = toParam((PropList)datum); 
            newElements.addElement(param);
            
        } else if (datum instanceof Comment) {
            Comment comment = (Comment)datum;
            newElements.addElement(comment.getValue());
            
        } else {
            throw new IllegalArgumentException("Unknown " +
            "type in ElementList: " + datum.getClass());
        }    
    }
    return newElements;
}
//--- Std
private static void print(String text) {
    System.out.println("PropListConverter" + text);
}

} // End class
