package org.jcon.param.util.build;

import org.jcon.param.ParamConverter;
import org.jcon.util.DataLib;
import org.jcon.util.msg.Message;
import org.jcon.util.msg.MessageDef;
import org.jcon.util.msg.MessageService;
import java.util.Enumeration;
import java.util.Hashtable;

/**
* This class "builds" a Param by replacing marked sections
* with different text, aka inclusion. It is a stepping stone
* to XML. See C:\awork\doc\dy_param.mdl for details.
* <p>
* A StartMark indicates where a replacement
* begins in Param text. For example: <p><pre><code>
*    FiscalYear is: [[LookupATSDR FiscalYear]]
*    <p></pre></code>
* has a StartMark of "[[", a Key of "LookupATSDR",
* a Value of "FiscalYear" and an EndMark of "]]". 
* A space is required after the Key. No spaces are 
* allowed between the StartMark and BuildKey. 
* <p>
* Note this scheme allows replacing the line: <p><pre><code>
*    [[LookupATSDR StandardEntityColumnDefs]]
*    <p></pre></code>
* with several lines for the 3 standard ColumnDefs. This
* in effect causes Entities to inherit the 3 ColumnDefs,
* but with a clean, extensible XML like mechanism.
*
* @author Jack Harich
*/
public class ParamBuilder implements MessageService {

//---------- Private Fields ------------------------------
private static final String START_MARK = ParamConverter.START_INCLUDE_MARK;
private static final String END_MARK   = ParamConverter.END_INCLUDE_MARK;

// Key = String Key, Object = BuildParamListener
private Hashtable listeners = new Hashtable();

//---------- MessageService Implementation ---------------
public void serviceMessage(Message message) {
    String messageName = message.getName();

    if (messageName == "BuildParam") {
        buildParam(message);

    } else {
        print(".processMessage() - Unknown messageName '" + messageName + "'");
    }
}
public MessageDef[] getServiceMessageDefs() {
//print(".getServiceMessageDefs() - Entered");
    MessageDef[] defs = new MessageDef[1];
    defs[0] = new MessageDef("BuildParam", this);
    defs[0].add("OldParamText", String.class, "The text to possibly build.");
    defs[0].add("NewParamText", String.class, "Initially null. The new text.");
    return defs;
}
//---------- Events --------------------------------------
public synchronized void addBuildParamListener(
                        BuildParamListener listener) {  
    // Query listener for events of interest
    // A "Key" example is "LookupATSDR"
    String[] keys = listener.getBuildParamKeys();
    //print(".addBuildParamListener() - listener keys.length = " + keys.length);    
    for (int i = keys.length; --i >= 0; ) {
        String key = keys[i];
        if (getListener(key) != null) throw new
            IllegalStateException("The key '" + key +
                "' already has a listener.");
        //print(".addBuildParamListener() - Adding listener for key '" + key + "'"); 
        listeners.put(key, listener);
    }
}
/**
* Removes the listener for BuildParamEvent events. For
* simplicity we remove all occurrances of the listener for
* all Keys. Thus to change what Keys a listener
* is interested in, it must be removed and then added with
* the new Keys in effect.
*/
public synchronized void removeBuildParamListener(
                        BuildParamListener listener) {
    Enumeration keys = listeners.keys();
    while (keys.hasMoreElements()) {
        String key = (String)keys.nextElement();
        if (getListener(key) == listener) {
            listeners.remove(key);
        }
    }
}
//---------- Private Methods -----------------------------
/**
* The Message must contain: <pre>
*     "OldParamText" - The text to possibly build
*     "NewParamText" - Initially null. The listener will
*        set this to the new text if it is changed.
*/
// Sample: [[LookupATSDR FiscalYear]]
private void buildParam(Message message) {
    String text = message.getString("OldParamText");
    int startMarkIndex = text.indexOf(START_MARK);
    while(startMarkIndex >= 0) {
        // Get key
        int firstSpaceIndex = text.indexOf(" ", startMarkIndex);
        String key = text.substring(startMarkIndex +
            START_MARK.length(), firstSpaceIndex);
        // Get value
        int endMarkIndex = text.indexOf(END_MARK, startMarkIndex);
        String value = text.substring(startMarkIndex +
            START_MARK.length() + key.length() + 1, endMarkIndex);        // Get newParamText
        // Get listener
        BuildParamListener listener = getListener(key);
        if (listener == null) throw new IllegalStateException(
            "No listener for key '" + key + "'.");
        // Get replacement for entire section
        BuildParamEvent evt = new BuildParamEvent(BuildParamEvent.REPLACE);
        evt.setBuildKey(key);
        evt.setBuildValue(value);
        listener.processBuildParamEvent(evt); // <-----<<<
        String replacement = evt.getReplacement();
        //print(".buildParam() - replacement = '" + replacement + "'");
        // Replace text section with replacement
        String section = START_MARK + key + " " + value + END_MARK;
      //print(".buildParam() - section = '" + section + "'");        
        text = DataLib.replaceToken(text, section, replacement);     
        // Continue
        startMarkIndex = text.indexOf(START_MARK);
    }
    // Set NewParamText if we replaced anything
    if (! message.getString("OldParamText").equals(text)) {
        message.set("NewParamText", text);
    }
}
// Returns null if none
private BuildParamListener getListener(String key) {
    return (BuildParamListener)listeners.get(key);
}
//--- Std
private static void print(String text) {
    System.out.println("ParamBuilder" + text);
}

} // End class
