package org.jcon.ba.lib;

import org.jcon.param.Param;
import org.jcon.param.ParamDriven;
import org.jcon.param.ParamDrivenInfo;
import org.jcon.util.DataLib;
import org.jcon.util.msg.Message;
import org.jcon.util.msg.MessageDef;
import org.jcon.util.msg.MessageListener;
import org.jcon.util.msg.MessageSourceStd;
import java.util.Vector;

/**
* This class is a stub part for use in designing and
* incrementally building BA system trees. Basically stubs
* are used by the designer to conceptualize and specify a
* system's structure, just like class modeling. Then, just
* like modeling, in each implementation iteration one or
* more stubs are implemented for each new wave of 
* functionality.
* <p>
* We expect stubs to eventually embody everything a Part
* Shop needs to produce the part that will replace the
* stub. This allows system design, part creation and part
* assembly to become a smooth process.
* <p>
* A stub's DK defines it's Message interests and signals,
* plus its real DK. A DK example for a StubPart is: <p> <p> <pre>
* 
* MessageInterests hasLines:
*     NameA
*     NameB
*     NameC
*     End: MessageInterests
* MessageSignals has:
*     MessageName1 has:
*         PropertyName1 has:
*             Type is: String
*             ShortDesc is: This is a short description
*             End: PropertyName1
*         PropertyName2 has:
*             Type is: int
*             ShortDesc is: The person's age
*             End: PropertyName2
*         End: MessageName1
*     MessageName2 has:
*         PropertyName1 has:
*             Type is: org.jcon.util.PropMap
*             ShortDesc is: The person's properties
*             End: PropertyName1
*         End: MessageName2
*     End: MessageSignals
*
* // All else is normal DK which this part does not use.
* </pre> <p>
* "Type" in MessageSignals must be String, boolean, int,
*  long, short, double or a full class name.
* ShortDesc is optional but recommended.
*
* @author Jack Harich
*/
public class StubPart extends MessageSourceStd implements
        MessageListener, ParamDriven {

//---------- Private Fields ------------------------------
private Param param;
private String[] messageInterests = new String[] { };

//---------- MessageSourceStd Abstract Implementations ---
/**
* Returns the MessageDefs from the  MessageSignals in the
* part's DK. Throws an IllegalArgumentException if an 
* invalid Message property "Type" is encountered.
*/
public Vector getMessageDefs() {
    Vector defs = new Vector();
    
    Param signalsParam = param.getParam("MessageSignals");
    String[] keys = signalsParam.getDataKeys();
    for (int i = 0; i < keys.length; i++) {
        // Each messageName has properties
        String messageName = keys[i]; // ie MessageName1
        MessageDef def = new MessageDef(messageName, this);
        Param propertiesParam = signalsParam.getParam(messageName);
        String[] propertyNames = propertiesParam.getDataKeys();
        
        for (int j = 0; j < propertyNames.length; j++) {
            // Each propertyName has type and description
            String propertyName = propertyNames[j];
            String typeText = propertiesParam.getString(propertyName + ".Type");
            Class typeClass = createClassType(typeText);
            String shortDesc = propertiesParam.getStringDefault(propertyName + ".ShortDesc", "(no doc)");
            def.add(propertyName, typeClass, shortDesc);
        }
        defs.addElement(def);
    }
    return defs;
// *** An example, to be deleted ***
//    def = new MessageDef(START_DEVICE_TAB, this);
//    def.add("WidgetSet", WidgetSet.class, "Contains Devices tab widgets.");
//    defs.addElement(def);            
}
private Class createClassType(String typeText) {
    typeText = typeText.trim();
    
    if (typeText.equals("String")) {
        return String.class;
        
    } else if (typeText.equals("boolean")) {
        return Boolean.TYPE;
        
    } else if (typeText.equals("int")) {
        return Integer.TYPE;
    
    } else if (typeText.equals("long")) {
        return Long.TYPE;
    
    } else if (typeText.equals("float")) {
        return Float.TYPE;
    
    } else if (typeText.equals("double")) {
        return Double.TYPE;
    
    } else { 
        // Assume it's a class name
        try {
            return Class.forName(typeText);
        } catch(Exception ex) {
            throw new IllegalArgumentException(
                "Invalid class type '" + typeText + "'");
        }
    }
}
//---------- MessageListener Implementation --------------
/**
* This print the messageName received and whether we would
* normally process or ignore it, according to our DK.
*/
public void processMessage(Message message) {
    String messageName = message.getName();
    
    if (DataLib.arrayContainsValueIgnoreCase(
                    messageInterests, messageName)) {
        print(".processMessage() - Would normally process " + messageName);
    } else {
        print(".processMessage() - Ignoring -----> " + messageName);    
    }
}
/**
* Returns the MessageInterets names in the DK.
*/
public String[] loadMessageInterests() {
    return messageInterests;
}    
//---------- ParamDriven Implementation ------------------
public void setParam(Param param) {
    this.param = param;
    // Set messsageInterests
    Vector lines = param.getLinesVector("MessageInterests");
//print(".setParam() - lines are: " + lines);    
    messageInterests = DataLib.convertVectorToStringArray(lines);
//print(".setParam() - messageInterests length is: " + messageInterests.length);    
}
public Param getParam() {
    return param; 
}
public boolean applyNewParam(Param newParam) {
    setParam(newParam);
    return true; // Successful
}
public ParamDrivenInfo getParamDrivenInfo() {
    return null;
}
//---------- Public Methods ------------------------------

//---------- Private Methods -----------------------------
//--- Std
private static void print(String text) {
    System.out.println("StubPart" + text);
}

} // End class