package org.jcon.param.stream;

import org.jcon.param.Param;
import org.jcon.param.ParamAccessor;
import org.jcon.param.ParamStore;
import org.jcon.util.DataLib;
import org.jcon.util.GenLib;
import java.io.IOException;
import java.util.Vector;

/**
 * This class is a ParamOutputStream filter by receiving
 * Params, using them for substitution in other params,
 * and outputing the modified params. The input Params are
 * CongigDefs.
 *
 * @author Jack Harich
 */
public class ParamSubstituter implements ParamOutputStream {

//---------- Private Fields ------------------------------
private ParamOutputStream output;
private ParamStore        paramStore;

//---------- ParamOutputStream Implementation ------------
public void writeParam(Param configDefParam) throws IOException {
try {
    // Get param to modify
    Object markerClass = GenLib.createInstance(configDefParam.getString("MarkerClass"));
    String fileName = configDefParam.getString("FileName");
    ParamAccessor accessor = new ParamAccessor(markerClass, fileName);
    Param param = paramStore.readParam(accessor);
    // Modify it
    modifyParam(param, configDefParam);
    // Send Param down pipeline
    try {
        output.writeParam(param);
    } catch(IOException ex) {
        // *** Need to identify element better ***
        print(".writeParam() - Param:\n" + param);
        GenLib.exception("ParamSubstituter.writeParam()",
            "Cannot write param to pipeline.", ex);   
    }
} catch(Exception ex) {
    print(".writeParam() - Param is:\n" + configDefParam);
    GenLib.exception("ParamSutstituter.writeParam()",
        "Failure on Param", ex);
    throw new IOException(ex.getMessage());
}    
} // End method
    
public void setParamOutput(ParamOutputStream output) {
    this.output = output;
}
public boolean isParamSink() {
    return false;   
}  
//---------- Properties ----------------------------------
public void setParamStore(ParamStore paramStore) {
    this.paramStore = paramStore;
}  
//---------- Private Methods -----------------------------
private void modifyParam(Param param, Param configDefParam) {   
    String pathType = configDefParam.getString("PathType");
    
    if (pathType == "NameValue") {
        modifyNameValue(param, configDefParam);
                
    } else if (pathType == "StringVector") {
        modifyStringVector(param, configDefParam);

    } else if (pathType == "ParamVector") {
        modifyParamVector(param, configDefParam);
        
    } else {
         throw new IllegalArgumentException(
             "Unknown PathType '" + pathType + "'.");   
    }
}   
// Most common and easiest. Change the named value.
// Ignore possibility it doesn't exist, which allows us
// to add new named values.
private void modifyNameValue(Param param, Param configDefParam) {
    String name = configDefParam.getString("NamePath");
    String value = configDefParam.getString("NewValue");
    param.put(name, value);
} 
// Param has StringVector. Replace occurances.
private void modifyStringVector(Param param, Param configDefParam) {
    String name = configDefParam.getString("NamePath");
    if (! param.hasProperty(name)) return;
    Vector lines = param.getVector(name);
    //if (lines == null) return;
    
    Vector valueDefs = configDefParam.getVector("ValueDefs");
    for (int i = 0; i < valueDefs.size(); i++) {
         Param valueDefParam = (Param)valueDefs.elementAt(i);
         modifyStringVectorValue(lines, valueDefParam);
    }
    param.put(name, lines);
} 
private void modifyStringVectorValue(Vector lines,
        Param valueDefParam) {
    String targetValue = valueDefParam.getString("TargetValue");
    String newValue = valueDefParam.getString("NewValue");
    // Replace all occurances in lines
    for (int i = 0; i < lines.size(); i++) {
        String line = (String)lines.elementAt(i);
        String newLine = DataLib.replaceToken(line,
            targetValue, newValue);
        if (! newLine.equals(line)) lines.setElementAt(newLine, i);
    }        
}
// Param has a Vector of Param elements. Replace occurances
// using criteria to find proper element.
// Only works for "is:" values
private void modifyParamVector(Param param, Param configDefParam) {
    String name = configDefParam.getString("NamePath");
    Vector elements = param.getVector(name);
    if (elements == null) return;
    
    Vector valueDefs = configDefParam.getVector("ValueDefs");
    for (int i = 0; i < valueDefs.size(); i++) {
         Param valueDefParam = (Param)valueDefs.elementAt(i);
         modifyParamVectorElement(elements, valueDefParam);
    }
    param.put(name, elements);
} 
private void modifyParamVectorElement(Vector elements,
        Param valueDefParam) {
    String criteriaName = valueDefParam.getString("CriteriaName");            
    String criteriaValue = valueDefParam.getString("CriteriaValue");
    for (int i = 0; i < elements.size(); i++) {
         Param param = (Param)elements.elementAt(i);
         if (param.hasProperty(criteriaName)) {
            String oldValue = param.getString(criteriaName);
            if (oldValue.equals(criteriaValue)) {
               param.put(
                  valueDefParam.getString("ReplacementName"),
                  valueDefParam.getString("ReplacementValue"));
            }                 
         }   
    }          
}            
//--- Std
private static void print(String text) {
    System.out.println("ParamSubstituter" + text);
}

} // End class
