import "logging.eol";
import "common.eol";
import "helper.eol";
import "debug.eol";

operation Any transformTo(fd : TTCN!FunctionDef) {
    fd.name = self.TTCNname();

    var md = new TTCN!ModuleDefinition();
    md.def = fd;
    var m = self.eContainer.equivalent();
    m.defs.definitions.add(md);
    
    fd.statement = new TTCN!StatementBlock();
    
    //TODO: fix formatting
    //TODO: extract into transformation?
    if (self.formalParameter.notEmpty()) {
        fd.parameterList = new TTCN!FunctionFormalParList(); 
        for (p in self.formalParameter) { 
            fd.parameterList.params.add(p.transformToFFP());
        }
    }
    //TODO: add support for body?
}

operation TDL!FormalParameter transformToFFP() : TTCN!FunctionFormalPar {
    var fp = new TTCN!FunctionFormalPar();
    //TODO: support for value parameters?
    // -> process annotation
    // -> extract into a rule to make use of equivalent?
    fp.template = new TTCN!FormalTemplatePar();
    fp.~kind = fp.template;
    
    fp.~kind.name = self.TTCNname();
    
    //TODO: direction support?
    
    fp.~kind.type = self.dataType.getTTCNType();
    ("FP: "+self.name+" : "+self.dataType.name).log(1);
	return fp;           
}

operation Any getModule() : TTCN!TTCN3Module {
	if (self.eContainer.isDefined()) {
		if (not self.eContainer.isKindOf(TTCN!TTCN3Module)) {
			return self.eContainer.getModule();
		} else {
			return self.eContainer;
		}
	} 
}


operation Any getElapsedTime() : Any {
	var mpElapsedTime = self.getModule().getElapsedTime();
	return mpElapsedTime;
}

operation TTCN!TTCN3Module getElapsedTime() : Any {
	var mpElapsedTime = self.~mpElapsedTime;
	return mpElapsedTime;
}

operation TTCN!TTCN3Module createTypeDefBody() : TTCN!TypeDefBody{
    var md = new TTCN!ModuleDefinition();
    md.def = new TTCN!TypeDef();
    md.def.body = new TTCN!TypeDefBody();
    self.defs.definitions.add(md);
    return md.def.body; 
}

operation String getStringBody() : TTCN!TemplateBody {
    var tb = new TTCN!TemplateBody();
    tb.simple = new TTCN!SimpleSpec();
    tb.simple.expr = self.getStringValue();
	return tb;
}

operation String getStringValue() : TTCN!Value {
    var v = new TTCN!Value();
    v.predef = new TTCN!PredefinedValue();
    v.predef.charString = "\""+self+"\"";
    return v;
}

operation TDL!DataUse getDataInstance() : TDL!DataInstance {
	if (self.isKindOf(TDL!DataInstanceUse)) {
		return self.dataInstance;
	} else if (self.isKindOf(TDL!DataElementUse) and self.dataElement.isKindOf(TDL!DataInstance)) {
		return self.dataElement;
	} else {
		("  TODO: "+self.~rule+" : Not Implemented Yet : "+self.type).log(1);
		return null;
	}
}

operation Any transformSingleMemberAssignment(m : TDL!Member, mad : TDL!DataUse) : TTCN!TemplateBody {
    ("transformSingleMemberAssignment: "+self.name+"::"+m.name).log(1);
    var body = new TTCN!TemplateBody();
    if (mad.isDefined()) {
	    var di = mad.getDataInstance();
	    if (di.isDefined()) { 
	    	//TODO: add support for DataElementUse
	        if (mad.argument.isEmpty()) {
	            var e = di.equivalent();
	            body.simple = new TTCN!SimpleSpec();
	            body.simple.expr = new TTCN!Value();
	            body.simple.expr.ref = e.getReferencedValue(); 
	            	            
	            //reduction
	            //use extended field references
	            var useEFR = mad.reduction.exists(mr|mr.collectionIndex.isDefined());
	            
	            if (useEFR) {
	                for (mr in mad.reduction) {
	                    if (mr.member.isDefined()) {
	                        var efr = new TTCN!ExtendedFieldReference();
	                        efr.field = mr.member.equivalent();
	                        body.simple.expr.ref.fields.add(efr);
	                    }
	                    if (mr.collectionIndex.isDefined() 
	                      and mr.collectionIndex.getDataInstance().isDefined()) {
	                        var ci = mr.collectionIndex.getDataInstance().equivalent();
	                        var efr = new TTCN!ExtendedFieldReference();
	                        efr.array = new TTCN!Value();
	                        efr.array.ref = ci.getReferencedValue();
	                        body.simple.expr.ref.fields.add(efr);
	                    }
	                }            
	            } else {
	                var t = body.simple.expr.ref.head;
	                for (mr in mad.reduction) {
	                    if (mr.member.isDefined()) {
	                        t.tail = new TTCN!RefValueTail();
	                        t.tail.value = mr.member.equivalent();
	                        
	                    }
	                    t = t.tail;
	                }
	            }
	            
	        } else {
	            //nested anonymous definitions
	            //or overriding
	            ("  : TODO: "+mad.item).log(1);
	            if (mad.item.isDefined()) {
		            body = self.transformDefaultMatchingSymbol(m, mad);
	            } else {
		            body.field = mad.transformWildcardMemberAssignment();
	            }
	        }
        } else {
        	body = self.transformDefaultMatchingSymbol(m, mad);
        }
    } else {
    	body = self.transformDefaultMatchingSymbol(m, mad);
    }
    self.debugElement("Self: ");
    body.debugElement("Body: ");
    return body;
}

operation Any transformDefaultMatchingSymbol(m : TDL!Member, mad : TDL!DataUse) : TTCN!TemplateBody {
    var body = new TTCN!TemplateBody();
    body.simple = new TTCN!SimpleSpec();
    if (mad.isUndefined() 
      or not mad.isKindOf(TDL!LiteralValueUse)) {
	    body.simple.spec = new TTCN!SimpleTemplateSpec();
	    body.simple.spec.expr = new TTCN!SingleTemplateExpression();
	    body.simple.spec.expr.symbol = new TTCN!MatchingSymbol();
    }
    if (mad.isUndefined() 
      and self.unassignedMember.isDefined() 
      and self.unassignedMember.name = "AnyValueOrOmit") {
        if (m.isOptional) {
            body.simple.spec.expr.symbol.anyornone = "*";
        } else {
            body.simple.spec.expr.symbol.any = "?";
        }
    } else if (mad.isUndefined() 
      and self.unassignedMember.isDefined() 
      and self.unassignedMember.name = "AnyValue") {
        body.simple.spec.expr.symbol.any = "?";
    } else if (mad.isDefined() 
      and mad.isKindOf(TDL!AnyValue)) {
        body.simple.spec.expr.symbol.any = "?";
    } else if (mad.isDefined() 
      and mad.isKindOf(TDL!AnyValueOrOmit)) {
        body.simple.spec.expr.symbol.anyornone = "*";
    } else if (mad.isDefined()) {
      	body.simple.spec = null;
      	body.simple.expr = new TTCN!Value();
      	body.simple.expr.predef = new TTCN!PredefinedValue();
        if (mad.isKindOf(TDL!LiteralValueUse)) {
	      	//("TODO: Implement full support for literal values : "+mad).log(1);
	        body.simple.expr.predef.charString = mad.value;
        } else {
	        ("  TODO: "+self.~rule+" : Not Implemented Yet : "+mad).log(1);
	        body.simple.expr.predef.charString = "\"TODO_NOT_SUPPORTED_YET\"";
        }
    } 
	return body;
}

operation TDL!Member isInherited(type:TDL!StructuredDataType) : Boolean {
    return self.eContainer() != type;
}

operation TDL!Member getFieldDef(type:TDL!StructuredDataType) : TTCN!StructFieldDef {
    if (self.isInherited(type)) { 
        var r = type.equivalent();
        return r.body.defs.select(d | d.name = self.TTCNname()).first();
    } else {
        return self.equivalent();
    }
}

operation Any getReferencedValue() : TTCN!ReferencedValue {
	var v = new TTCN!ReferencedValue();
    v.head = new TTCN!Head();
    
    //Template ->target = BaseTemplate reference
    //Constant ->target = SingleConstDef reference
    if (self.isKindOf(TTCN!TemplateDef)) {
        v.head.target = self.base;
    } else if (self.isKindOf(TTCN!ConstDef)) {
        v.head.target = self.defs.list.get(0);             
    } else if (self.isKindOf(TTCN!Enumeration)) {
        v.head.target = self;             
    } else {
    	("  TODO: Not implemented yet: getReferencedValue for"+self).log(1);
        //what else can it be?
    }
    return v;
}

//TODO: deprecated? -> not at present
operation TDL!StructuredDataInstance transformMemberAssignment() : TTCN!CompoundExpression {
    var expr;
    ("transformMemberAssignment: "+self.name).log(1);
    if (not self.memberAssignment.isEmpty()) {
        expr = new TTCN!FieldExpressionList();
        for (ma in self.memberAssignment) {
            var fes = new TTCN!FieldExpressionSpec(); 
            //fes.fieldRef = ma.member.equivalent();
            fes.fieldRef = ma.member.getFieldDef(self.dataType);
            ("  "+ma.member.name+"->"+fes.fieldRef.eContainer().eContainer().name).log(1);
            
            //ma.memberSpec
            fes.expr = new TTCN!Value();
            //TODO: other data uses?
            var di = ma.memberSpec.getDataInstance();
            if (di.isDefined()) {
	            (di).println();
                var e = di.equivalent();
                e.println();

                if (e.isUndefined()) {
                	if (di.dataType.name = "Boolean") {
                		fes.expr.predef = new TTCN!PredefinedValue();
	            		fes.expr.predef.boolean = di.name.toString().toLower();
                	}
                } else {
	                fes.expr.ref = e.getReferencedValue();
                }
                //TODO: handle overriding parameters?
            } else if (ma.memberSpec.isKindOf(TDL!LiteralValueUse)) {
            	//TODO: extract
            	fes.expr.predef = new TTCN!PredefinedValue();
            	if (ma.memberSpec.intValue.isDefined()) {
            		fes.expr.predef.integer = ma.memberSpec.intValue.toString();
            	} else if (ma.memberSpec.boolValue.isDefined()) {
            		fes.expr.predef.boolean = ma.memberSpec.boolValue.toString();
            	} else {
	                fes.expr.predef.charString = "\""+ma.memberSpec.value+"\"";
            	}
            } else if (ma.memberSpec.isKindOf(TDL!SpecialValueUse)) {
            	fes.expr.predef = new TTCN!PredefinedValue();
            	if (ma.memberSpec.isKindOf(TDL!OmitValue)) {
	                fes.expr.predef.omit = "omit";
            	} else if (ma.memberSpec.isKindOf(TDL!AnyValue)) {
            		fes.expr.predef.charString = "TODO: ? (AnyValue)"; 
            	} else if (ma.memberSpec.isKindOf(TDL!AnyValueOrOmit)) {
            		fes.expr.predef.charString = "TODO: * (AnyValueOrOmit)"; 
            	}
            	
            } else {
                //TODO: handle DataElementUse, others
                fes.expr.predef = new TTCN!PredefinedValue();
                fes.expr.predef.charString = "\"TODO: Not Implemented Yet\"";
	            ("    NOT IMPLEMENTED YET: "+ma.memberSpec).log(1);
                
            }
            expr.specs.add(fes);
        }
        //TODO: handle unassigned members? -> add guards to corresponding rule
        //TODO: handle special characters
        var assigned = self.memberAssignment.collect(ma|ma.member);
        for (m in self.dataType.allMembers().excludingAll(assigned)) {
            (self.name + ": Skipping unassigned member "+m.name).log(1);
        }
        
    } else {
        ("  TODO: "+self.~rule+" : What is going on here: "+self).log(1);
        expr = new TTCN!ArrayExpression();
    }
    expr.debugElement("Expr:");
    return expr;

}

operation TDL!StaticDataUse transformWildcardMemberAssignment() : TTCN!FieldSpecList {
    //dataInstance? or inferred type
    //unassignedMember?
    //argument -> ParameterBinding (parameter:Member, dataUse:DataUse)
    //reduction? -> MemberReference
    ("transformWildcardMemberAssignment: "+self.name).log(1);
    var field = new TTCN!FieldSpecList();
    var t;
    if (self.dataInstance.isDefined()) {
        t = self.dataInstance.dataType;
        ("DI:"+self.dataInstance.name).log(1);
    } else {
        if (self.eContainer.isKindOf(TDL!ParameterBinding)) {
            t = self.eContainer.parameter.dataType;
        } else if (self.eContainer.isKindOf(TDL!MemberAssignment)) {
            t = self.eContainer.member.dataType;
        } else {
            ("  TODO: "+self.~rule+" : Context Not Implemented Yet").log(1);
        }
    }
    if (self.item.isDefined() or t.isKindOf(TDL!CollectionDataType)) {
    	("  TODO: "+self.~rule+" : Collections not supported yet: "+self).log(1);
    	("  "+t.name).log(1);
    	var it = t.itemType;
    	
        var fs = new TTCN!FieldSpec(); 
        field.spec.add(fs);
	} else {
	    for (m in t.allMembers()) {
	        var fs = new TTCN!FieldSpec(); 
	        fs.ref = m.equivalent();
	        
	        var mad;
	        var ma = self.argument.selectOne(a|a.parameter = m);
	        //default: no wildcard, defined
	        //in case not defined, check for inherited
	        ma.println();
	        if (ma.isDefined()) {
	            mad = ma.dataUse;
	        } else if (ma.isUndefined() 
	          and self.isKindOf(TDL!DataInstanceUse) 
	          and self.dataInstance.isDefined()) {
	            var mai = self.dataInstance.memberAssignment.selectOne(a|a.member = m);
	            if (mai.isDefined()) {
	                mad = mai.memberSpec;
	            } 
	        } else {
	            
	        }  
	
	        fs.body = self.transformSingleMemberAssignment(m, mad);
	        field.spec.add(fs);
	    }
	}
    return field;
}

//operation TDL!StructuredDataInstance transformWildcardMemberAssignment() : TTCN!FieldSpecList {
//    ("transformWildcardMemberAssignment: "+self.name).log(1);
//    var field = new TTCN!FieldSpecList();
//    for (m in self.dataType.allMembers()) { //TODO: why all? why not only assigned?
////    for (ma in self.memberAssignment) {
//        var fs = new TTCN!FieldSpec(); 
//        fs.ref = m.equivalent();
//        
//        var ma = self.memberAssignment.selectOne(ma|ma.member = m);
//        var mad;
//        if (ma.isDefined()) {
//            mad = ma.memberSpec;
//        } 
//
//        fs.body = new TTCN!TemplateBody();
//        fs.body = self.transformSingleMemberAssignment(m, mad);
//        field.spec.add(fs);
//    }
//    return field;
//}

//TODO: reactivate or differentiate above
operation TDL!StructuredDataInstance transformWildcardMemberAssignment() : TTCN!FieldSpecList {
    ("transformWildcardMemberAssignmentX: "+self.name).log(1);
    var field = new TTCN!FieldSpecList();
    for (ma in self.memberAssignment) {
        var fs = new TTCN!FieldSpec(); 
        fs.ref = ma.member.equivalent();

        fs.body = new TTCN!TemplateBody();
        fs.body = self.transformSingleMemberAssignment(ma.member, ma.memberSpec);
        field.spec.add(fs);
    }
    return field;
}


operation TDL!DataType getTTCNType() : TTCN!Type {
    var type = new TTCN!Type();
    if (resolvePredefined and self.name == "String") {
        //TODO: add support for universal
        type.`pre` = "charstring";
    } else if (resolvePredefined and self.name == "Integer") {
        type.`pre` = "integer";
    } else if (resolvePredefined and self.name == "Boolean") {
        type.`pre` = "boolean";
    } else if (resolvePredefined and self.name == "Verdict") {
        type.`pre` = "verdicttype";
    } else {
        //TODO: use data mapping to overide predefined?
        if (useDataMapping) {
            //TODO: handle predefined types?
            //t.`pre` = "TODO_"+dt.name;
            //use mappings, simple approach
            var mapping = TDL!DataElementMapping.allInstances().select(m|m.mappableDataElement = self).first();
            if (mapping.isDefined()) {
                //t.ref = new TTCN!TypeReference();
                //TODO: check if defined
                //t.ref.head = mapping.elementURI;
                type.`pre` = mapping.elementURI.replaceAll("\"","");
            } else {
                type.`pre` = "MAP_"+self.TTCNname();
            }
        } else {
            type.ref = new TTCN!TypeReference();
            //TODO: check if defined
            type.ref.head = self.equivalent();
        }
    }
    
    return type;
} 

@cached
operation TTCN!TTCN3Module getSDT() : TTCN!SubTypeDefNamed {
    if (self.~sdt.isUndefined()) {
        var sdt = new TTCN!SubTypeDefNamed();
        sdt.name = self.name+"_SimpleDataType";
        sdt.type = new TTCN!Type();
        //TODO: make configurable? 
        sdt.type.`pre` = "charstring";
        var body = self.createTypeDefBody();
        body.sub = sdt;
        self.~sdt = sdt;
    } 
    return self.~sdt;
}

@cached
operation String getComponent() : TTCN!ComponentDef {
    return TTCN!ComponentDef.allInstances().select(c|c.name = self).first();
} 

@cached
operation String getFunction() : TTCN!FunctionDef {
    return TTCN!FunctionDef.allInstances().select(c|c.name = self).first();
} 

operation TDL!DataInstanceUse getDataUseValue() : TTCN!Value {
    var value = new TTCN!Value();
    value.ref = new TTCN!ReferencedValue();
    value.ref.head = new TTCN!Head();
    if (self.dataInstance.isValue()) {
        value.ref.head.target = self.dataInstance.equivalent().defs.list.first();
    } else {
        value.ref.head.target = self.dataInstance.equivalent().base;
    }
    return value;
}

operation TDL!LiteralValueUse getDataUseValue() : TTCN!Value {
    var v = new TTCN!Value();
    v.predef = new TTCN!PredefinedValue();
    //TODO: also infer from context?
    if (self.intValue.isDefined()) {
        v.predef.integer = self.intValue.asString();
    } else if (self.boolValue.isDefined()) {
        v.predef.boolean = self.boolValue.asString();
    } else {
        v.predef.charString = "\""+self.value+"\"";
    }

    return v;      
}


operation TDL!GateReference checkGateReference() : Boolean {
    var accepted = false;
    if (self.component.role = TDL!ComponentInstanceRole#Tester) {
        accepted = true;
    }
    if (self.component.role = TDL!ComponentInstanceRole#SUT) {
        if (self.eContainer.isKindOf(TDL!Connection)) {
            var opposite = self.eContainer.endPoint.select(e|e <> self).first();
            if (opposite.component.role = TDL!ComponentInstanceRole#Tester) {
                accepted = true;
            }
        }
    }
    return accepted;
}

operation TDL!GateReference mergeIntoSUT(sut : TTCN!TypeDefBody) {
    var pe = new TTCN!PortElement();

    pe.name = self.component.TTCNname()+"_"+self.gate.TTCNname();

    var d = new TTCN!ComponentDefList();
    sut.structured.component.defs.add(d);

    d.sc = ";";
    d.element = new TTCN!ComponentElementDef();
    d.element.port = new TTCN!PortInstance();

    d.element.port.ref = self.gate.type.equivalent();
    d.element.port.instances.add(pe);
    
    self.equivalent().port = pe;
}

//TODO: remove parameter?
operation TDL!GateReference createConnectionPort() {
    if (self.eContainer.isKindOf(TDL!Connection)) {
        var opposite = self.eContainer.endPoint.select(e|e <> self).first();
        
        //only if it does not exist
        var targetName = self.gate.TTCNname()+"_to_"+opposite.component.TTCNname()+"_"+opposite.gate.TTCNname();
        var cdl = self.component.type.equivalent().defs.select(e|
            e.element.port.isDefined() and 
            e.element.port.instances.first().name = targetName).first();

        var pe;        
        if (cdl.isDefined()) {
            pe = cdl.element.port.instances.first();
        } else {
	        pe = new TTCN!PortElement();
	    
	        pe.name = targetName;
	    
	        var d = new TTCN!ComponentDefList();
	        self.component.type.equivalent().defs.add(d);
	    
	        d.sc = ";";
	        d.element = new TTCN!ComponentElementDef();
	        d.element.port = new TTCN!PortInstance();
	    
	        d.element.port.ref = self.gate.type.equivalent();
	        d.element.port.instances.add(pe);
        }
    
        //TODO: check this has no side effects
        self.equivalent().port = pe;
    } else {
        //TODO: handle?
    }
}

//TODO: move to helper?
operation TTCN!ComponentRef getComponentName() : String {
    if (self.ref.isDefined()) {
        return self.ref.variable.ref.head.target.name;
    } else if (self.system.isDefined()) {
        return self.system;
    } else if (self.mtc.isDefined()) {
        return self.mtc;
    } else if (self.`self`.isDefined()) {
        return self.`self`;
    }
}

operation TDL!LiteralValueUse getTemplateBody() : TTCN!TemplateBody {
	//TODO: add support for integer?
	if (self.intValue.isDefined() or self.boolValue.isDefined()) {
	    var tb = new TTCN!TemplateBody();
	    tb.simple = new TTCN!SimpleSpec();
	    tb.simple.expr = self.getDataUseValue();
	    return tb;
	} else {
		return self.value.replaceAll("\"", "").getStringBody();
	}
}

operation TDL!LiteralValueUse getTemplateBody(flat : Boolean) : TTCN!TemplateBody {
	//TODO: add support for integer?
	return self.getTemplateBody();
}

operation TDL!FormalParameterUse getTemplateBody(flat : Boolean) : TTCN!TemplateBody {
	return ("TODO:"+self.type.name+":"+self.parameter.name).getStringBody();
}

operation TDL!VariableUse getTemplateBody() : TTCN!TemplateBody {
    var tb = new TTCN!TemplateBody();
    tb.simple = new TTCN!SimpleSpec();
    tb.simple.expr = new TTCN!Value();
    tb.simple.expr.ref = new TTCN!ReferencedValue();
    tb.simple.expr.ref.head = new TTCN!Head();
    tb.simple.expr.ref.head.target = self.variable.equivalent();
    return tb;
//    return ("TODO:"
//        +self.componentInstance.name
//        +"::"
//        +self.variable.name
//        +" {"+self.resolveDataType().name+"}").getStringBody();
}


operation TDL!DataInstanceUse getTemplateBody() : TTCN!TemplateBody {
	return self.getTemplateBody(false);
}

operation TDL!DataElementUse getTemplateBody() : TTCN!TemplateBody {
	return self.getTemplateBody(false);
}

//TODO: refactor, copied from below
operation TDL!DataElementUse getTemplateBody(flat : Boolean) : TTCN!TemplateBody {
    var tb = new TTCN!TemplateBody();
    tb.simple = new TTCN!SimpleSpec();
    tb.simple.expr = new TTCN!Value();
    if (useDataMapping) {
        var mapping = TDL!DataElementMapping.allInstances().select(m|m.mappableDataElement = self.dataElement).first(); //TODO: extract parameter
        tb.simple.expr.predef = new TTCN!PredefinedValue();
        if (mapping.isDefined()) {
            //t.ref = new TTCN!TypeReference();
            //TODO: check if defined
            //t.ref.head = mapping.elementURI;
            tb.simple.expr.predef.charString = mapping.elementURI.replaceAll("\"","");
        } else {
            tb.simple.expr.predef.charString = "MAP_"+self.dataInstance.name;
        }
    } else {
    	if (self.dataElement.isDefined() and self.dataElement.isKindOf(TDL!DataInstance)) {
	        tb.simple.expr.ref = new TTCN!ReferencedValue();
	        tb.simple.expr.ref.head = new TTCN!Head();
	        tb.simple.expr.ref.head.target = self.dataElement.equivalent().base;
    	} else if (self.dataElement.isDefined() and self.dataElement.isKindOf(TDL!DataType)) { //TODO:differentiate structured vs simple vs collection
			if (flat) {
		        ("  TODO: "+self.~rule+" : Not supported yet: "+self).log(1);
		        tb.simple.expr.predef = new TTCN!PredefinedValue();
		        tb.simple.expr.predef.charString = "\"TODO_INLINE_DATA_ELEMENT_FOR_"+self.dataElement.name+"\"";
			} else if (self.argument.isEmpty()) {
				//TODO: make sure type is present
				tb.simple.expr = new TTCN!ArrayExpression();				
			} else {
		        tb.simple=null;
		        tb.~type = self.dataElement.getTTCNType();
				tb.field = new TTCN!FieldSpecList();
		    
		    	for (a in self.argument) {
			        var fs = new TTCN!FieldSpec(); 
			        fs.ref = a.parameter.equivalent();
			        fs.body = a.dataUse.getTemplateBody(true); //TODO: expand?
		    	    tb.field.spec.add(fs);
		    	}
			}
	        
    	} else if (self.item.isDefined()) {
	        ("  TODO: "+self.~rule+" : Not supported yet: "+self).log(1);
	        tb.simple.expr.predef = new TTCN!PredefinedValue();
	        tb.simple.expr.predef.charString = "\"TODO_INLINE_COLLECTION\"";
    	} else {
	        ("  TODO: "+self.~rule+" : Not supported yet: "+self).log(1);
    	}
    }
    return tb;
	
}

operation TDL!DataInstanceUse getTemplateBody(flat : Boolean) : TTCN!TemplateBody {
    var tb = new TTCN!TemplateBody();
    tb.simple = new TTCN!SimpleSpec();
    tb.simple.expr = new TTCN!Value();
    if (useDataMapping) {
        var mapping = TDL!DataElementMapping.allInstances().select(m|m.mappableDataElement = self.dataInstance).first();
        tb.simple.expr.predef = new TTCN!PredefinedValue();
        if (mapping.isDefined()) {
            //t.ref = new TTCN!TypeReference();
            //TODO: check if defined
            //t.ref.head = mapping.elementURI;
            tb.simple.expr.predef.charString = mapping.elementURI.replaceAll("\"","");
        } else {
            tb.simple.expr.predef.charString = "MAP_"+self.dataInstance.name;
        }
    } else {
    	if (self.dataInstance.isDefined()) {
	        tb.simple.expr.ref = new TTCN!ReferencedValue();
	        tb.simple.expr.ref.head = new TTCN!Head();
	        tb.simple.expr.ref.head.target = self.dataInstance.equivalent().base;
    	} else if (self.dataType.isDefined()) {
			if (flat) {
		        ("  TODO: "+self.~rule+" : Not supported yet: "+self).log(1);
		        tb.simple.expr.predef = new TTCN!PredefinedValue();
		        tb.simple.expr.predef.charString = "\"TODO_INLINE_DATA_INSTANCE_FOR_"+self.dataType.name+"\"";
			} else {
		        tb.simple=null;
		        tb.~type = self.dataType.getTTCNType();
				tb.field = new TTCN!FieldSpecList();
		    
		    	for (a in self.argument) {
			        var fs = new TTCN!FieldSpec(); 
			        fs.ref = a.parameter.equivalent();
			        fs.body = a.dataUse.getTemplateBody(true);
		    	    tb.field.spec.add(fs);
		    	}
			}
	        
    	} else if (self.item.isDefined()) {
	        ("  TODO: "+self.~rule+" : Not supported yet: "+self).log(1);
	        tb.simple.expr.predef = new TTCN!PredefinedValue();
	        tb.simple.expr.predef.charString = "\"TODO_INLINE_COLLECTION\"";
    	} else {
	        ("  TODO: "+self.~rule+" : Not supported yet: "+self).log(1);
    	}
    }
    return tb;
}

operation TDL!Behaviour getMainContext(component : TDL!ComponentInstance) : String {
    return self.getTestDescription().getMainContext(component);
}

operation TDL!TestDescription getMainContext(component : TDL!ComponentInstance) : String {
    return self.TTCNname()+"_"+component.TTCNname()+"_main";
}


operation TDL!TimeOut getGuardOp() : TTCN!GuardOp {
    var op = new TTCN!GuardOp();
    op.timeout = new TTCN!TimeoutStatement();
    op.timeout.ref = new TTCN!TimerRefOrAny();
    op.timeout.ref.ref = self.timer.equivalent();
    return op;
}

operation TDL!Interaction getGuardOp() : TTCN!GuardOp {
    var op = new TTCN!GuardOp();
    op.receive = new TTCN!ReceiveStatement();
    op.receive.any = new TTCN!PortOrAny();
    //TODO: handle broadcast?
    //op.receive.any.ref = self.target.first().targetGate.gate.equivalent();
    op.receive.any.ref = self.target.first().targetGate.equivalent().port;
    op.receive.receive = new TTCN!PortReceiveOp();
    op.receive.receive.template = new TTCN!InLineTemplate();
    op.receive.receive.template.template = self.argument.getTemplateBody();

    //TODO: a bit of an escape hack
    op.receive.receive.template.type = op.receive.receive.template.template.~type;

    var t = self.target.first();    
    if (not t.valueAssignment.isEmpty()) {
        var pr = new TTCN!PortRedirect();
        op.receive.receive.redirect = pr;
        pr.value = new TTCN!ValueSpec();
        pr.value.variable = new TTCN!VariableRef();
        pr.value.variable.ref = new TTCN!ReferencedValue();
        pr.value.variable.ref.head = new TTCN!Head();
        pr.value.variable.ref.head.target = t.valueAssignment.get(0).variable.equivalent();
    }
    
    return op;
}

operation String getVerdictStatement() : TTCN!FunctionStatement {
    var sfs = new TTCN!FunctionStatement();
    sfs.verdict = new TTCN!SetLocalVerdict();
    sfs.verdict.expression = new TTCN!Value();
    //TODO: handle corresponding verdict properly
    sfs.verdict.expression.predef = new TTCN!PredefinedValue();
    if (self = "pass") {
        sfs.verdict.expression.predef.verdictType = TTCN!VerdictTypeValue#pass;
    } else if (self = "fail") {
        sfs.verdict.expression.predef.verdictType = TTCN!VerdictTypeValue#fail;
    } else if (self = "inconclusive") {
        sfs.verdict.expression.predef.verdictType = TTCN!VerdictTypeValue#inconc;
    } else {
        //TODO
    }
    return sfs;
}

operation TDL!DataUse getVerdictStatement() : TTCN!FunctionStatement {
	var verdictName = "pass"; //TODO: more reasonable default?
    //TODO: handle corresponding verdict properly
    if (self.isKindOf(TDL!DataInstanceUse)) {
    	verdictName = self.dataInstance.name;
 	} else if (self.isKindOf(TDL!DataElementUse)) { //TODO: check that it is a dataInstance of type Verdict?
 		verdictName = self.dataElement.name;
    } else {
       //TODO
 	}
 	return verdictName.getVerdictStatement(); 
}

