package edu.gemini.spModel.template;

import edu.gemini.pot.sp.ISPTemplateFolder;
import edu.gemini.pot.sp.ISPTemplateGroup;
import edu.gemini.pot.sp.ISPTemplateParameters;
import edu.gemini.pot.sp.SPComponentType;
import edu.gemini.shared.util.TimeValue;
import edu.gemini.shared.util.immutable.ApplyOp;
import edu.gemini.spModel.data.AbstractDataObject;
import edu.gemini.spModel.gemini.obscomp.SPSiteQuality;
import edu.gemini.spModel.pio.ParamSet;
import edu.gemini.spModel.pio.Pio;
import edu.gemini.spModel.pio.PioFactory;
import edu.gemini.spModel.target.SPTarget;
import edu.gemini.spModel.target.system.ITarget;

/**
 * Data object representing template parameters.
 *
 * Note that this class is effectively immutable. Either use the non-empty
 * constructor or call setParamSet() immediately on construction. setParamSet()
 * can be invoked only once.  Sorry this is so awkward.  Everything should be
 * truly immutable.
 */
public final class TemplateParameters extends AbstractDataObject {
    public static final SPComponentType SP_TYPE = SPComponentType.TEMPLATE_PARAMETERS;
    public static final String VERSION = "2015A-1";
    public static final String PARAM_TIME = "time";

    public static TemplateParameters newEmpty() {
        return new TemplateParameters(new SPTarget(), new SPSiteQuality(), TimeValue.ZERO_HOURS);
    }

    public static TemplateParameters newInstance(SPTarget target, SPSiteQuality conditions, TimeValue timeValue) {
        return new TemplateParameters(
                (SPTarget) target.clone(),
                conditions.clone(),
                timeValue
        );
    }

    private SPTarget target;
    private SPSiteQuality conditions;
    private TimeValue time;

    public TemplateParameters() {
        setTitle("Template Parameters");
        setType(SP_TYPE);
        setVersion(VERSION);
    }

    public TemplateParameters(ParamSet paramSet) {
        this();
        setParamSet(paramSet);
    }

    // The private constructor is used to prevent making clones during calls
    // to copy() when not necessary.
    private TemplateParameters(SPTarget target, SPSiteQuality conditions, TimeValue timeValue) {
        this();
        this.target     = target;
        this.conditions = conditions;
        this.time       = timeValue;
    }

    private void checkRef(Object o) {
        if (o == null) throw new IllegalStateException("Not initialized.");
    }
    private void checkRefs() {
        checkRef(target);
        checkRef(conditions);
        checkRef(time);
    }

    public SPTarget getTarget() {
        checkRef(target);
        final SPTarget newTarget = (SPTarget) target.clone();

        // Ugh.  Otherwise the two targets share the same inner "itarget".
        // I don't want to fix that directly in SPTarget.clone() because I don't
        // know what behavior depends on it.  The new target model will fix all
        // of this mess.
        newTarget.setTarget((ITarget) newTarget.getTarget().clone());

        return newTarget;
    }

    public TemplateParameters copy(SPTarget target) {
        return new TemplateParameters((SPTarget) target.clone(), conditions, time);
    }

    public SPSiteQuality getSiteQuality() {
        checkRef(conditions);
        return conditions.clone();
    }

    public TemplateParameters copy(SPSiteQuality sq) {
        return new TemplateParameters(target, sq.clone(), time);
    }

    public TimeValue getTime() {
        checkRef(time);
        return time;  // actually immutable
    }

    public TemplateParameters copy(TimeValue time) {
        return new TemplateParameters(target, conditions, time);
    }

    public ParamSet getParamSet(PioFactory factory) {
        checkRefs();
        final ParamSet ps = super.getParamSet(factory);

        ps.addParamSet(target.getParamSet(factory));
        ps.addParamSet(conditions.getParamSet(factory));
        Pio.addLongParam(factory, ps, PARAM_TIME, time.getMilliseconds());

        return ps;
    }

    public void setParamSet(ParamSet paramSet) {
        if ((target != null) || (conditions != null) || (time != null)) {
            throw new IllegalStateException("Already initialized.");
        }

        super.setParamSet(paramSet);

        final ParamSet targetPs = paramSet.getParamSet(SPTarget.PARAM_SET_NAME);
        target = new SPTarget();
        target.setParamSet(targetPs);

        final ParamSet conditionsPs = paramSet.getParamSet(SPSiteQuality.SP_TYPE.readableStr);
        conditions = new SPSiteQuality();
        conditions.setParamSet(conditionsPs);

        time = TimeValue.millisecondsToTimeValue(Pio.getLongValue(paramSet, PARAM_TIME, 0l), TimeValue.Units.hours);
    }

    @Override
    public boolean equals(Object o) {
        checkRefs();
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        final TemplateParameters that = (TemplateParameters) o;
        if (!conditions.equals(that.conditions)) return false;
        if (!target.equals(that.target)) return false;
        return time.equals(that.time);
    }

    @Override
    public int hashCode() {
        checkRefs();
        int result = target.hashCode();
        result = 31 * result + conditions.hashCode();
        result = 31 * result + time.hashCode();
        return result;
    }

    public static void foreach(ISPTemplateFolder folder, ApplyOp<TemplateParameters> op) {
        if (folder != null) {
            for (ISPTemplateGroup g : folder.getTemplateGroups()) {
                for (ISPTemplateParameters p : g.getTemplateParameters()) {
                    op.apply((TemplateParameters) p.getDataObject());
                }
            }
        }
    }
}

@@ -2610,419 +2610,40 @@
     
-final SPTarget newTarget = (SPTarget) target.clone();%0A%0A        // Ugh.  Otherwise the two targets share the same inner %22itarget%22.%0A        // I don't want to fix that directly in SPTarget.clone() because I don't%0A        // know what behavior depends on it.  The new target model will fix all%0A        // of this mess.%0A        newTarget.setTarget((ITarget) newTarget.getTarget().clone());%0A%0A        return newTarget
+return (SPTarget) target.clone()
 ;%0A  

remove hack around target clone issue in TemplateParameters