package jif.types;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import jif.types.label.ArgLabel;
import jif.types.label.Label;
import polyglot.ext.jl.types.ConstructorInstance_c;
import polyglot.main.Report;
import polyglot.types.*;
import polyglot.util.Position;
import polyglot.util.TypedList;

/** An implementation of the <code>JifConstructorInstance</code> interface. 
 */
public class JifConstructorInstance_c extends ConstructorInstance_c
				     implements JifConstructorInstance
{
    protected Label startLabel;
    protected Label returnLabel;
    protected List constraints;
    protected boolean isDefaultStartLabel;
    protected boolean isDefaultReturnLabel;

    public JifConstructorInstance_c(JifTypeSystem ts, Position pos,
	    ClassType container, Flags flags,
	    Label startLabel, boolean isDefaultStartLabel, Label returnLabel, 
            boolean isDefaultReturnLabel, List formalTypes, List formalArgLabels,
	    List excTypes, List constraints) {

	super(ts, pos, container, flags, formalTypes, excTypes);
	this.startLabel = startLabel;
	this.returnLabel = returnLabel;
	this.constraints = TypedList.copyAndCheck(constraints, Assertion.class, true);

	this.startLabel = startLabel;
        this.isDefaultStartLabel = isDefaultStartLabel;
	this.returnLabel = returnLabel;
        this.isDefaultReturnLabel = isDefaultReturnLabel;
	this.throwTypes = TypedList.copyAndCheck(throwTypes, 
					       Type.class, 
					       true);
	this.formalTypes = TypedList.copyAndCheck(formalTypes, 
					       Type.class, 
					       true);
    }


    public Label startLabel() {
	return startLabel;
    }

    public Label externalPC() {
	return startLabel;
    }

    public Label returnLabel() {
	return returnLabel;
    }
    
    public void setReturnLabel(Label returnLabel, boolean isDefault) {
	this.returnLabel = returnLabel;
        this.isDefaultReturnLabel = isDefault;
    }

    public boolean isDefaultReturnLabel() {
        return isDefaultReturnLabel;
    }

    public void  setStartLabel(Label startLabel, boolean isDefault) {
	this.startLabel = startLabel;
	this.isDefaultStartLabel = isDefault;
    }

    public boolean isDefaultStartLabel() {
        return isDefaultStartLabel;
    }

    public List constraints() {
	return constraints;
    }

    public void setConstraints(List constraints) {
	this.constraints = TypedList.copyAndCheck(constraints, Assertion.class, true);
    }

    public boolean isCanonical() {
        if (!(super.isCanonical()
                && startLabel.isCanonical()
                && returnLabel.isCanonical()
                && listIsCanonical(constraints)
                && formalTypes != null)) {
            return false;
        }
        
        JifTypeSystem jts = (JifTypeSystem)typeSystem();
        // also need to make sure that every formal type is labeled with an arg label
        for (Iterator i = formalTypes().iterator(); i.hasNext(); ) {
            Type t = (Type) i.next();
            if (!jts.isLabeled(t) || !(jts.labelOfType(t) instanceof ArgLabel)) return false;
        }    
        return true;
    }
    
    public void subst(VarMap bounds) {
	if (this.startLabel != null) 
	    this.startLabel = bounds.applyTo(startLabel);
	
	if (this.returnLabel != null) 
	    this.returnLabel = bounds.applyTo(returnLabel);
	
	List formalTypes = new LinkedList();
	for (Iterator i = formalTypes().iterator(); i.hasNext(); ) {
	    Type t = (Type) i.next();
	    formalTypes.add(bounds.applyTo(t));
	}
	this.formalTypes(formalTypes);
	
        List throwTypes = new LinkedList();
        for (Iterator i = throwTypes().iterator(); i.hasNext(); ) {
            Type t = (Type) i.next();
            throwTypes.add(bounds.applyTo(t));
        }
        
        this.throwTypes(throwTypes);
    }    
 
    public void subst(LabelSubstitution subst) throws SemanticException {
        TypeSubstitutor tsbs = new TypeSubstitutor(subst);
        setStartLabel(startLabel().subst(subst), isDefaultStartLabel());
        setReturnLabel(returnLabel().subst(subst), isDefaultReturnLabel());

        List formalTypes = new LinkedList();
        for (Iterator i = formalTypes().iterator(); i.hasNext(); ) {
            Type t = (Type) i.next();
            formalTypes.add(tsbs.rewriteType(t));
        }
        this.setFormalTypes(formalTypes);

        List throwTypes = new LinkedList();
        for (Iterator i = throwTypes().iterator(); i.hasNext(); ) {
            Type t = (Type) i.next();
            throwTypes.add(tsbs.rewriteType(t));
        }
        this.setThrowTypes(throwTypes);
        
    }
    public String debugString() {
        return debugString(true);
    }
    private String debugString(boolean showInstanceKind) {
        String s = "";
        if (showInstanceKind) {
            s = "constructor ";
        }
	s += flags.translate() + container + "(";

	for (Iterator i = formalTypes.iterator(); i.hasNext(); ) {
	    Type t = (Type) i.next();
	    s += ((JifTypeSystem)ts).unlabel(t).toString();

	    if (i.hasNext()) {
	        s += ", ";
	    }
	}

	s += ")";

	return s;
    }
    
    public String signature() {
        if (Report.should_report(Report.debug, 1)) { 
            return fullSignature();
        }
        return debugString(false);
    }
    public String fullSignature() {
	String s = container.toString();
        if (!isDefaultStartLabel() || Report.should_report(Report.debug, 1)) {
            s += startLabel;
        }
        s += "(";

        for (Iterator i = formalTypes.iterator(); i.hasNext(); ) {
            Type t = (Type) i.next();
            s += t.toString();

            if (i.hasNext()) {
                s += ",";
            }
        }
        s += ")";
        if (!isDefaultReturnLabel() || Report.should_report(Report.debug, 1)) {
            s += ":" + returnLabel;
        }

        return s;
    }

}
