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

pre { 
    ("Transforming..").log(1);
    //TODO:export configuration
      
    //use data mapping
    var useDataMapping = false;
    //??
    var transformAlt = true;
    //generate functions to simulate the SUT 
    var simulateSUT = false;
    //generate testcases
    var createTestcase = true;
    //generate SimpleDataType subtype
    var generateSDT = true;
    //resolve Integer to integer, etc.
    var resolvePredefined = true;
    //generate qualified names
    var qualifiedNaming = false;
    //generate age timers
    var generateAgeTimers = true;

    var predefined = new Set();
    predefined.add("String");
    predefined.add("Integer");
    predefined.add("Boolean");
    predefined.add("Verdict");

    var mpElapsedTime;

    var f = new TTCN!TTCN3File();
    var stack = new Map();
}

post {
    //Clean orphaned module defnition lists
    for (m in TTCN!TTCN3Module.allInstances()) {
        if (m.defs.isDefined() and m.defs.definitions.isEmpty()) {
            delete m.defs;
        }
    }
    ("Done!").log(1);
    
}

rule Package2Module 
    transform p : TDL!Package
    to m : TTCN!TTCN3Module {
    //guard : true
    //TODO: to support annotations and test objectives 
    //      need to add contained comments as a feature
    //      in modules
    //TODO: to support annotations and comments for elements 
    //      need to add contained comments as a feature
    //      in module definitions
    ("  "+p.name).log(1);
    m.name = p.TTCNname();
    f.modules.add(m);
    if (p.packagedElement.notEmpty() or p.`import`.notEmpty()) {
	    m.defs = new TTCN!ModuleDefinitionsList();
	    
	    if (generateAgeTimers) {
		    //default MP for component lifetime
		    var md = new TTCN!ModuleDefinition();
	        md.def = new TTCN!ModuleParDef;
	        md.def.param = new TTCN!ModulePar;
	        md.def.param.type = new TTCN!Type();
	        md.def.param.type.`pre` = "float";
	        md.def.param.list = new TTCN!ModuleParList;
	        
	        var mp = new TTCN!ModuleParameter;
	        mp.name = "mp_componentElapsedTimerMaxDuration";
	        md.def.param.list.params.add(mp);
	        
	        mpElapsedTime = mp;
	        
	        m.defs.definitions.add(md);
	    }
    }
}

rule ElementImport2ImportDef 
    transform ei : TDL!ElementImport
    to i : TTCN!ImportDef {
    i.name = ei.importedPackage.equivalent().name;
    //i.name = "mappings";
    
    var md = new TTCN!ModuleDefinition();
    md.def = i;
    var m = ei.eContainer.equivalent();
    m.defs.definitions.add(md);
    
    if (ei.importedElement.notEmpty()) {
	    i.importSpec = new TTCN!ImportSpec();
	    for (ie in ei.importedElement.select(x|x.equivalent().isDefined())) {
            var e = new TTCN!ImportElement();		    
	        //TODO: make reusable, extend to other types
		    if (ie.isKindOf(TDL!DataType) 
		      or ie.isKindOf(TDL!GateType)
		      or ie.isKindOf(TDL!ComponentType)) {
		        e.type = new TTCN!ImportTypeDefSpec();
		        e.~kind = e.type;
		    } else if (ie.isKindOf(TDL!DataInstance)) {
                //TODO: check if it is a constant instead
                if (ie.isValue()) {
	                e.const = new TTCN!ImportConstSpec();
	                e.~kind = e.const;
                } else {
		            e.template = new TTCN!ImportTemplateSpec();
		            e.~kind = e.template;
                }
            } else if (ie.isKindOf(TDL!Function)) {
                e.`function` = new TTCN!ImportFunctionSpec();
                e.~kind = e.`function`;
	        } else {
    	        //TODO: handle others
    	        ("Imports for " + ie.type.name + " are not supported yet!").log(1);
    	        delete e;
    	        continue;
	        }

	        e.~kind.idOrAll = new TTCN!IdentifierListOrAllWithExcept();
	        e.~kind.idOrAll.idList = new TTCN!IdentifierList();
	        //TODO: resolve if imported from mappings?
	        //TODO: extract equivalent name resolution to an operation
	        if (e.const.isDefined()) {
	            e.~kind.idOrAll.idList.ids.add(ie.equivalent().defs.list.get(0).name);
            } else if (e.template.isDefined()) {
                e.~kind.idOrAll.idList.ids.add(ie.equivalent().base.name);
	        } else {
		        e.~kind.idOrAll.idList.ids.add(ie.equivalent().name);
	        }
		    i.importSpec.element.add(e);
	    }
    } else {
        i.all = new TTCN!AllWithExcepts();
    }
    
    
}


rule DRM2ImportDef 
    transform drm : TDL!DataResourceMapping
    to i : TTCN!ImportDef {
    guard : useDataMapping
    i.name = drm.resourceURI.replaceAll("\"","");
    //i.name = "mappings";
    
    var md = new TTCN!ModuleDefinition();
	md.def = i;
    var m = drm.eContainer.equivalent();
    m.defs.definitions.add(md);

	i.importSpec = new TTCN!ImportSpec();
}

rule DEM2ImportElement
    transform dem : TDL!DataElementMapping
    to e : TTCN!ImportElement {
    guard : useDataMapping
    
    var i = dem.dataResourceMapping.equivalent();
	i.importSpec.element.add(e);
	
	if (dem.mappableDataElement.isKindOf(TDL!DataType)) {
		e.type = new TTCN!ImportTypeDefSpec();
		e.type.idOrAll = new TTCN!IdentifierListOrAllWithExcept();
		e.type.idOrAll.idList = new TTCN!IdentifierList();
		e.type.idOrAll.idList.ids.add(dem.elementURI.replaceAll("\"",""));
		
	} else {
		if (dem.mappableDataElement.isKindOf(TDL!DataInstance)) {
			e.template = new TTCN!ImportTemplateSpec();
			e.template.idOrAll = new TTCN!IdentifierListOrAllWithExcept();
			e.template.idOrAll.idList = new TTCN!IdentifierList();
			e.template.idOrAll.idList.ids.add(dem.elementURI.replaceAll("\"",""));
		} else {
		}
		//TODO: handle others
	}
}

//Generative, use mappings instead
rule SimpleDT2R 
    transform dt : TDL!SimpleDataType
    to s : TTCN!SubTypeDefNamed {
    guard : not useDataMapping and not (resolvePredefined and predefined.includes(dt.name))
    
    s.name = dt.TTCNname();
    
    s.type = new TTCN!Type();

    //TODO: handle predefined data types
    var m = dt.eContainer.equivalent();
    if (generateSDT) {
	    //create default simple data type?
	    s.type.ref = new TTCN!TypeReference(); 
	    s.type.ref.head = m.getSDT(); 
    } else {
        s.type.`pre` = "charstring";
    }
    
	var t = m.createTypeDefBody();
    t.sub = s;
}

//Generative, use mappings instead
rule StructuredDT2R 
    transform dt : TDL!StructuredDataType
    to r : TTCN!RecordDefNamed {
    guard : not useDataMapping
        
    r.name = dt.TTCNname();
    
    var module = dt.eContainer.equivalent();
	var t = module.createTypeDefBody();    

    t.structured = new TTCN!StructuredTypeDef();
    t.structured.record = r;
	
	r.body = new TTCN!StructDefBody();	
}

//Generative, use mappings instead
rule CollectionDT2RoR 
    transform dt : TDL!CollectionDataType
    to r : TTCN!RecordOfDefNamed {
    guard : not useDataMapping
        
    r.name = dt.TTCNname();
    
    var module = dt.eContainer.equivalent();
    var t = module.createTypeDefBody();    

    t.structured = new TTCN!StructuredTypeDef();
    t.structured.recordOf = r;
    
    r.type = dt.itemType.getTTCNType();  
}


rule M2SFD 
    transform m : TDL!Member
    to f : TTCN!StructFieldDef {
    guard : not useDataMapping
    
    f.name = m.TTCNname();
    if (m.isOptional) {
        f.optional = "optional";
    }
    f.type = m.dataType.getTTCNType();
    
    var r = m.eContainer.equivalent();
    r.body.defs.add(f);
}

//Generative, use mappings instead
rule Action2FD 
    transform a : TDL!Action
    to fd : TTCN!FunctionDef {
    guard : not useDataMapping
        
    fd.name = a.TTCNname();
    
    var md = new TTCN!ModuleDefinition();
    md.def = fd;
    var m = a.eContainer.equivalent();
    m.defs.definitions.add(md);
    
    fd.statement = new TTCN!StatementBlock();
    
    //TODO: fix formatting
    if (a.formalParameter.notEmpty()) {
        fd.parameterList = new TTCN!FunctionFormalParList(); 
        for (p in a.formalParameter) {
            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 = p.TTCNname();
            
            //TODO: direction support?
            
            fp.~kind.type = p.dataType.getTTCNType();
            
            fd.parameterList.params.add(fp);
        }
    }
    //TODO: add support for body?
}

//Generative, use mappings instead
rule Function2FD 
    transform f : TDL!Function
    to fd : TTCN!FunctionDef {
    guard : not useDataMapping
        
    fd.name = f.TTCNname();
    
    var md = new TTCN!ModuleDefinition();
    md.def = fd;
    var m = f.eContainer.equivalent();
    m.defs.definitions.add(md);
    
    fd.statement = new TTCN!StatementBlock();
    
    //TODO: fix formatting
    if (f.formalParameter.notEmpty()) {
        fd.parameterList = new TTCN!FunctionFormalParList(); 
	    for (p in f.formalParameter) {
	        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 = p.TTCNname();
	        
	        //TODO: direction support?
	        
	        fp.~kind.type = p.dataType.getTTCNType();
	        
	        fd.parameterList.params.add(fp);
	    }
    }
    fd.returnType = new TTCN!ReturnType();
    fd.returnType.type = f.returnType.getTTCNType();
    //TODO: add support for body?
    //TODO: add distinction between value and template?
}


rule TC2FD
    transform tc : TDL!TestConfiguration
    to fd : TTCN!FunctionDef {
    
    fd.name = tc.TTCNname();
    
    var md = new TTCN!ModuleDefinition();
    md.def = fd;

    var m = tc.eContainer.equivalent();
	m.defs.definitions.add(md);

	//CREATE MTC TYPE
	var mtc = m.createTypeDefBody();    
    mtc.structured = new TTCN!StructuredTypeDef();
    mtc.structured.component = new TTCN!ComponentDef();
    mtc.structured.component.name = "MTC_"+tc.TTCNname();

	//CREATE SYSTEM TYPE 
	//TODO: Is this necessary?
	var sut = m.createTypeDefBody();    
    sut.structured = new TTCN!StructuredTypeDef();
    sut.structured.component = new TTCN!ComponentDef();
    sut.structured.component.name = "SYSTEM_"+tc.TTCNname();

	fd.runsOn = new TTCN!RunsOnSpec();
	fd.runsOn.component = mtc.structured.component;
	
	fd.statement = new TTCN!StatementBlock();
	
	var sl = new TTCN!FunctionStatementList();
	fd.statement.stat.add(sl);

	//create components
	for (ci in tc.componentInstance.select(c|simulateSUT or c.role.name = "Tester")) {
		var s = new TTCN!FunctionStatement();
		s.basic = new TTCN!BasicStatements();
		s.basic.assign = new TTCN!Assignment();
		s.basic.assign.ref = new TTCN!VariableRef();
		s.basic.assign.ref.ref = new TTCN!ReferencedValue();
		s.basic.assign.ref.ref.head = new TTCN!Head();
		s.basic.assign.ref.ref.head.target = ci.equivalent();
		
		s.basic.assign.expression = new TTCN!OpCall();
		s.basic.assign.expression.configuration = new TTCN!ConfigurationOps();
		s.basic.assign.expression.configuration.create = new TTCN!CreateOp();
		s.basic.assign.expression.configuration.create.type = ci.type.equivalent();
		 
		sl.statements.add(s);
		sl.sc.add(";");
	}
	
    //merge ports of SUT components into sut 
    for (ci in tc.componentInstance.select(c|not simulateSUT and c.role.name = "SUT")) {
        for (gi in ci.type.gateInstance) {
	        var pe = new TTCN!PortElement();
	
		    pe.name = gi.TTCNname()+"_SUT";
		
		    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 = gi.type.equivalent();
		    d.element.port.instances.add(pe);
        }
    }	
	
	//create connections
	for (c in tc.connection) {
	    if (simulateSUT or 
	          (c.endPoint.get(0).component.role.name = "Tester" and 
	           c.endPoint.get(1).component.role.name = "Tester" )) {
			
			var s = new TTCN!FunctionStatement();
			s.configuration = new TTCN!ConfigurationStatements();
			s.configuration.connect = new TTCN!ConnectStatement();
			s.configuration.connect.spec = new TTCN!SingleConnectionSpec();
			s.configuration.connect.spec.port1 = c.endPoint.get(0).equivalent();
			s.configuration.connect.spec.port2 = c.endPoint.get(1).equivalent();
			sl.statements.add(s);
			sl.sc.add(";");
	    } else {
	    
            var s = new TTCN!FunctionStatement();
            s.configuration = new TTCN!ConfigurationStatements();
            s.configuration.map = new TTCN!MapStatement();
            s.configuration.map.spec = new TTCN!SingleConnectionSpec();
            s.configuration.map.spec.port1 = c.endPoint.get(0).equivalent();
            s.configuration.map.spec.port2 = c.endPoint.get(1).equivalent();
            sl.statements.add(s);
            sl.sc.add(";");
	    }
	}

}

rule TD2FD 
    transform td : TDL!TestDescription
    to fd : TTCN!FunctionDef {
    
    fd.name = td.TTCNname();
    
    var md = new TTCN!ModuleDefinition();
    md.def = fd;

    var m = td.eContainer.equivalent();
	m.defs.definitions.add(md);
	
	fd.runsOn = new TTCN!RunsOnSpec();
	fd.runsOn.component = ("MTC_"+td.testConfiguration.TTCNname()).getComponent();

	fd.statement = new TTCN!StatementBlock();

	stack.put(fd.name,new OrderedSet());
	stack.get(fd.name).add(fd.statement);

	var sl = new TTCN!FunctionStatementList();
	fd.statement.stat.add(sl);
	
	if (not createTestcase) {
		var s = new TTCN!FunctionStatement();
		s.behavior = new TTCN!BehaviourStatements();
		s.behavior.`function` = new TTCN!FunctionInstance();
		s.behavior.`function`.ref = td.testConfiguration.equivalent();

		sl.statements.add(s);
		sl.sc.add(";");
	}
	

	for (ci in td.testConfiguration.componentInstance) {
		var cf = new TTCN!FunctionDef();
		cf.name = td.TTCNname()+"_"+ci.TTCNname()+"_main";
    
    	var md = new TTCN!ModuleDefinition();
    	md.def = cf;

    	var m = td.eContainer.equivalent();
		m.defs.definitions.add(md);
	
		cf.runsOn = new TTCN!RunsOnSpec();
		cf.runsOn.component = ci.type.equivalent();
		cf.statement = new TTCN!StatementBlock();
	
		stack.put(cf.name,new OrderedSet());
		stack.get(cf.name).add(cf.statement);
	
		var fs = new TTCN!FunctionStatement();
		fs.configuration = new TTCN!ConfigurationStatements();
		fs.configuration.startTc = new TTCN!StartTCStatement();
		fs.configuration.startTc.ref = new TTCN!ComponentOrDefaultReference();
		fs.configuration.startTc.ref.variable = new TTCN!VariableRef();
		fs.configuration.startTc.ref.variable.ref = new TTCN!ReferencedValue();
		fs.configuration.startTc.ref.variable.ref.head = new TTCN!Head();
		fs.configuration.startTc.ref.variable.ref.head.target = ci.equivalent();

		fs.configuration.startTc.`function` = new TTCN!FunctionInstance();
		fs.configuration.startTc.`function`.ref = cf; 
		
		sl.statements.add(fs);
		sl.sc.add(";");
		
	}	
	
	if (td.behaviourDescription.isDefined()) {
		td.behaviourDescription.behaviour.execute();
	}
	
	stack.clear();
	
	if (createTestcase) {
		var tc = new TTCN!TestcaseDef();
		tc.name = "tc_"+td.TTCNname();
	    
	    var tmd = new TTCN!ModuleDefinition();
	    tmd.def = tc;
	
		m.defs.definitions.add(tmd);
		
		tc.spec = new TTCN!ConfigSpec();
		tc.spec.runsOn = new TTCN!RunsOnSpec();
		tc.spec.runsOn.component = ("MTC_"+td.testConfiguration.TTCNname()).getComponent();
		tc.spec.systemSpec = new TTCN!SystemSpec();
		tc.spec.systemSpec.component = ("SYSTEM_"+td.testConfiguration.TTCNname()).getComponent();
	
		tc.statement = new TTCN!StatementBlock();
	
		stack.put(tc.name,new OrderedSet());
		stack.get(tc.name).add(tc.statement);
	
		var tsl = new TTCN!FunctionStatementList();
		tc.statement.stat.add(tsl);
		
		//setup configuration
		var tsc = new TTCN!FunctionStatement();
		tsc.behavior = new TTCN!BehaviourStatements();
		tsc.behavior.`function` = new TTCN!FunctionInstance();
		tsc.behavior.`function`.ref = td.testConfiguration.equivalent();
		
		tsl.statements.add(tsc);
		tsl.sc.add(";");

		//start behaviour
		var tsb = new TTCN!FunctionStatement();
		tsb.behavior = new TTCN!BehaviourStatements();
		tsb.behavior.`function` = new TTCN!FunctionInstance();
		tsb.behavior.`function`.ref = fd;
		
		tsl.statements.add(tsb);
		tsl.sc.add(";");

		//all done
		var tsd = new TTCN!FunctionStatement();
		tsd.configuration = new TTCN!ConfigurationStatements();
		tsd.configuration.done = new TTCN!DoneStatement();
		tsd.configuration.done.component = new TTCN!ComponentOrAny();
		tsd.configuration.done.component.all = "all";

		tsl.statements.add(tsd);
		tsl.sc.add(";");

	}
	
}

rule GR2PR 
    transform gr : TDL!GateReference
    to pr : TTCN!PortRef {
	//guard : false
	
	var cr = new TTCN!ComponentRef();
	
	if (simulateSUT or gr.component.role.name = "Tester" ) {
		cr.ref = new TTCN!ComponentOrDefaultReference();
		cr.ref.variable = new TTCN!VariableRef();
		cr.ref.variable.ref = new TTCN!ReferencedValue();
		cr.ref.variable.ref.head = new TTCN!Head();
		cr.ref.variable.ref.head.target = gr.component.equivalent();
	} else {
	    cr.system = "system";
	    //TODO: is there a case for cr.mtc? 
	}
	pr.component = cr;
		
	pr.port = gr.gate.equivalent();
}


rule CT2C 
    transform ct : TDL!ComponentType
    to c : TTCN!ComponentDef {

    c.name = ct.TTCNname();

    var m = ct.eContainer.equivalent();
    var t = m.createTypeDefBody();    

    t.structured = new TTCN!StructuredTypeDef();
    t.structured.component = c;

    if (generateAgeTimers) {
	    var ti = new TTCN!SingleVarInstance();
	    ti.name = "T_elapsedTimeOfComponent";  
	    ti.ac = ":="; //TODO: Why is this needed?
	    ti.expr = new TTCN!Value();
	    ti.expr.ref = new TTCN!ReferencedValue();
	    ti.expr.ref.head = new TTCN!Head;
	    ti.expr.ref.head.target = mpElapsedTime;
	      
	    var d = new TTCN!ComponentDefList();
	    c.defs.add(d);
	
	    d.sc = ";";
	    d.element = new TTCN!ComponentElementDef();
	    
	    d.element.timer = new TTCN!TimerInstance();
	    d.element.timer.list = new TTCN!VarList();
	    d.element.timer.list.variables.add(ti);
    }
    
}


rule CI2V 
    transform ci : TDL!ComponentInstance
    to v : TTCN!SingleVarInstance {
    guard : simulateSUT or ci.role.name = "Tester"

    v.name = ci.TTCNname(); 
    
	var d = new TTCN!ComponentDefList();
    d.sc = ";";
    d.element = new TTCN!ComponentElementDef();
    d.element.variable = new TTCN!VarInstance();
    d.element.variable.listType = new TTCN!Type(); 
    d.element.variable.listType.ref = new TTCN!TypeReference();
    d.element.variable.listType.ref.head = ci.type.equivalent();
	d.element.variable.list = new TTCN!VarList();
	
	d.element.variable.list.variables.add(v);

	var mtc = ("MTC_"+ci.eContainer().TTCNname()).getComponent();
	mtc.defs.add(d);
}


rule GT2P 
    transform gt : TDL!GateType
    to p : TTCN!PortDef {
    //guard : true
    p.name = gt.TTCNname();

    var m = gt.eContainer.equivalent();
    var t = m.createTypeDefBody();    

    t.structured = new TTCN!StructuredTypeDef();
    t.structured.port = p;

    p.body = new TTCN!PortDefBody();
    p.body.attribs = new TTCN!PortDefAttribs();
    
    if (gt.kind.name = "Message") {
	    p.body.attribs.message = new TTCN!MessageAttribs();
	    //FIX
	    var ml = new TTCN!MessageList();
	    ml.direction = "inout";
	    ml.allOrTypeList = new TTCN!AllOrTypeList();
	    ml.allOrTypeList.typeList = new TTCN!TypeList();
	    p.body.attribs.message.messages.add(ml);
	    
	    //ADAPT
	    for (dt in gt.dataType) {
	        var t = dt.getTTCNType();
	        ml.allOrTypeList.typeList.types.add(t);
	    }
    } else {
        //TODO: handle procedure based gates..
    }
    
}

rule GI2PE
    transform gi : TDL!GateInstance
    to pe : TTCN!PortElement {

    pe.name = gi.TTCNname();

    var c = gi.eContainer.equivalent();
    var d = new TTCN!ComponentDefList();
    c.defs.add(d);

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

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

rule T2TI 
    transform t : TDL!Timer
    to ti : TTCN!SingleVarInstance {
    
    ti.name = t.TTCNname();

    var c = t.eContainer.equivalent();
    var d = new TTCN!ComponentDefList();
    c.defs.add(d);

    d.sc = ";";
    d.element = new TTCN!ComponentElementDef();
    
    d.element.timer = new TTCN!TimerInstance();
    d.element.timer.list = new TTCN!VarList();
    d.element.timer.list.variables.add(ti);
}

rule V2VI 
    transform v : TDL!Variable
    to vi : TTCN!SingleVarInstance {

    vi.name = v.TTCNname();

    var c = v.eContainer.equivalent();
    var d = new TTCN!ComponentDefList();
    c.defs.add(d);

    d.sc = ";";
    d.element = new TTCN!ComponentElementDef();
    
    d.element.variable = new TTCN!VarInstance();
    d.element.variable.listType = v.dataType.getTTCNType();
    d.element.variable.list = new TTCN!VarList();
    d.element.variable.list.variables.add(vi);
}


rule SimpleDI2TD
	transform di : TDL!SimpleDataInstance
	to td : TTCN!TemplateDef {
	//guard : false
    guard : not useDataMapping and not di.isValue() and di.dataType.name <> "Verdict"

    var md = new TTCN!ModuleDefinition();
	md.def = td;
    var m = di.eContainer.equivalent();
    m.defs.definitions.add(md);
    
    td.base = new TTCN!BaseTemplate();
    td.base.name = di.TTCNname();
    td.base.type = di.dataType.getTTCNType();

	var tb = new TTCN!TemplateBody();
    td.body = tb;
	tb.simple = new TTCN!SimpleSpec();
	tb.simple.expr = new TTCN!Value();
	tb.simple.expr.predef = new TTCN!PredefinedValue();
	tb.simple.expr.predef.charString = "\""+di.name+"\"";

}

rule SimpleDI2C
    transform di : TDL!SimpleDataInstance
    to cd : TTCN!ConstDef {
    //guard : false
    guard : not useDataMapping and di.isValue()

    var md = new TTCN!ModuleDefinition();
    md.def = cd;
    var m = di.eContainer.equivalent();
    m.defs.definitions.add(md);

    cd.type = di.dataType.getTTCNType();
    
    cd.defs = new TTCN!ConstList();

    var scd = new TTCN!SingleConstDef;
    scd.name = di.TTCNname();
    scd.expr = new TTCN!Value();
    scd.expr.predef = new TTCN!PredefinedValue();
    scd.expr.predef.charString = "\""+di.name+"\"";

    scd.assign = ":="; //TODO: this shall not be necessary

    cd.defs.list.add(scd);
}

rule StructuredDI2TD
    transform di : TDL!StructuredDataInstance
    to td : TTCN!TemplateDef {
    //guard : false
    guard : not useDataMapping and not di.isValue()

    var md = new TTCN!ModuleDefinition();
    md.def = td;
    var m = di.eContainer.equivalent();
    m.defs.definitions.add(md);
    
    td.base = new TTCN!BaseTemplate();
    td.base.name = di.TTCNname();
    td.base.type = di.dataType.getTTCNType();
    //TODO:unassigned fields
    //check if there is unassigned member handling
    //handle accordingly -> separate branch under .field
    var tb = new TTCN!TemplateBody();
    td.body = tb;
    
    if ((di.unassignedMember.isDefined()
           and not di.dataType.member.forAll(m|
                di.memberAssignment.exists(ma|ma.member = m)))
         //does it need to be recursive?
         or di.memberAssignment.exists(ma|
                ma.memberSpec.isKindOf(TDL!AnyValue) 
             or ma.memberSpec.isKindOf(TDL!AnyValueOrOmit))
      ) {
        //NOTE: implementation treats unset unassigned member (and other enums) as if 
        //  they have been set to the first value by default.
        //TODO: add none / omit as default option? 
//        ("Wildcard: "+di.name).log(1);
//        ("  "+di.unassignedMember+
//         "  "+di.unassignedMember.isDefined()+
//         "  "+(not di.dataType.member.forAll(m|di.memberAssignment.exists(ma|ma.member = m)))+
//         "  "+di.memberAssignment.exists(ma|ma.memberSpec.isKindOf(TDL!AnyValue) 
//              or ma.memberSpec.isKindOf(TDL!AnyValueOrOmit))).log(1);
        tb.field = di.transformWildcardMemberAssignment();
    } else {
        ("Simple: "+di.name).log(1);
	    tb.simple = new TTCN!SimpleSpec();
	    tb.simple.expr = di.transformMemberAssignment();
    }

}

rule StructuredDI2C
    transform di : TDL!StructuredDataInstance
    to cd : TTCN!ConstDef {
    //guard : false
    guard : not useDataMapping and di.isValue()

    var md = new TTCN!ModuleDefinition();
    md.def = cd;
    var m = di.eContainer.equivalent();
    m.defs.definitions.add(md);

    cd.type = di.dataType.getTTCNType();
    
    cd.defs = new TTCN!ConstList();

    var scd = new TTCN!SingleConstDef;
    scd.name = di.TTCNname();
    
    scd.expr = di.transformMemberAssignment();

    scd.assign = ":="; //TODO: this shall not be necessary

    cd.defs.list.add(scd);
}

rule CollectionDI2TD
    //TODO: the specification in the standard is insufficient
    transform di : TDL!CollectionDataInstance
    to td : TTCN!TemplateDef {
    //guard : false
    guard : not useDataMapping and not di.isValue()

    var md = new TTCN!ModuleDefinition();
    md.def = td;
    var m = di.eContainer.equivalent();
    m.defs.definitions.add(md);
    
    td.base = new TTCN!BaseTemplate();
    td.base.name = di.TTCNname();
    td.base.type = di.dataType.getTTCNType();
    //TODO:unassigned fields
    //check if there is unassigned member handling
    //handle accordingly -> separate branch under .field
    var tb = new TTCN!TemplateBody();
    td.body = tb;
    
    tb.simple = new TTCN!SimpleSpec();
    tb.simple.expr = new TTCN!ArrayExpression();
    tb.simple.expr.list = new TTCN!ArrayElementExpressionList();
    
    for (i in di.item) {
        if (i.isKindOf(TDL!DataInstanceUse) and i.dataInstance.isDefined()) {
            var v = new TTCN!Value();
            v.ref = new TTCN!ReferencedValue();
            v.ref.head = new TTCN!Head();
            
	        var e = i.dataInstance.equivalent();
	        if (e.isKindOf(TTCN!TemplateDef)) {
	            v.ref.head.target = e.base;
	        } else if (e.isKindOf(TTCN!ConstDef)) {
	            v.ref.head.target = e.defs.list.get(0);             
	        } else {
	            //what else can it be?
	        }
	        tb.simple.expr.list.expr.add(v);
        }
    }
}

rule CollectionDI2C
    //TODO: the specification in the standard is insufficient
    transform di : TDL!CollectionDataInstance
    to cd : TTCN!ConstDef {
    //guard : false
    guard : not useDataMapping and di.isValue()

    var md = new TTCN!ModuleDefinition();
    md.def = cd;
    var m = di.eContainer.equivalent();
    m.defs.definitions.add(md);
    
    
    cd.type = di.dataType.getTTCNType();
    
    cd.defs = new TTCN!ConstList();

    var scd = new TTCN!SingleConstDef;
    scd.name = di.TTCNname();
    
    scd.expr = new TTCN!ArrayExpression();
    scd.expr.list = new TTCN!ArrayElementExpressionList();

    for (i in di.item) {
        if (i.isKindOf(TDL!DataInstanceUse) and i.dataInstance.isDefined()) {
            var v = new TTCN!Value();
            v.ref = new TTCN!ReferencedValue();
            v.ref.head = new TTCN!Head();
            
            var e = i.dataInstance.equivalent();
            if (e.isKindOf(TTCN!TemplateDef)) {
                v.ref.head.target = e.base; //TODO: is that even possible here?
            } else if (e.isKindOf(TTCN!ConstDef)) {
                v.ref.head.target = e.defs.list.get(0);             
            } else {
                //what else can it be?
            }
            scd.expr.list.expr.add(v);
        }
    }

    scd.assign = ":="; //TODO: this shall not be necessary

    cd.defs.list.add(scd);
    
}

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 Any transformSingleMemberAssignment(m : TDL!Member, mad : TDL!DataUse) : TTCN!TemplateBody {
    var body = new TTCN!TemplateBody();
    if (mad.isDefined() and mad.isKindOf(TDL!DataInstanceUse)) {
        if (mad.dataInstance.isDefined() and mad.argument.isEmpty()) {
            body.simple = new TTCN!SimpleSpec();
            body.simple.expr = new TTCN!Value();
            body.simple.expr.ref = new TTCN!ReferencedValue();
            body.simple.expr.ref.head = new TTCN!Head();
            
            //Template ->target = BaseTemplate reference
            //Constant ->target = SingleConstDef reference
            var e = mad.dataInstance.equivalent();
            if (e.isKindOf(TTCN!TemplateDef)) {
                body.simple.expr.ref.head.target = e.base;
            } else if (e.isKindOf(TTCN!ConstDef)) {
                body.simple.expr.ref.head.target = e.defs.list.get(0);             
            } else {
                //what else can it be?
            }
            
            //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.dataInstance.isDefined()) {
	                    var efr = new TTCN!ExtendedFieldReference();
	                    efr.array = new TTCN!Value();
	                    efr.array.ref = new TTCN!ReferencedValue();
	                    efr.array.ref.head = new TTCN!Head();
	                    
	                    var ci = mr.collectionIndex.dataInstance.equivalent();
	                    if (ci.isKindOf(TTCN!TemplateDef)) {
	                        efr.array.ref.head.target = ci.base;
	                    } else if (ci.isKindOf(TTCN!ConstDef)) {
	                        efr.array.ref.head.target = ci.defs.list.get(0);             
	                    } else {
	                        //what else can it be?
	                    }

	                    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
            body.field = mad.transformWildcardMemberAssignment();
        }
    } else {
        body.simple = new TTCN!SimpleSpec();
        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()) {
            ("TODO: Implement?").log(1);
        } 
    }
    return body;
}

operation TDL!StaticDataUse transformWildcardMemberAssignment() : TTCN!FieldSpecList {
    //dataInstance? or inferred type
    //unassignedMember?
    //argument -> ParameterBinding (parameter:Member, dataUse:DataUse)
    //reduction? -> MemberReference
    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: Context Not Implemented Yet").log(1);
        }
    }
    for (m in t.member) {
        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
        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 {
    var field = new TTCN!FieldSpecList();
    for (m in self.dataType.member) {
        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: deprecated?
operation TDL!StructuredDataInstance transformMemberAssignment() : TTCN!CompoundExpression {
    var expr;
    if (not self.memberAssignment.isEmpty()) {
        expr = new TTCN!FieldExpressionList();
        for (ma in self.memberAssignment) {
            var fes = new TTCN!FieldExpressionSpec(); 
            fes.fieldRef = ma.member.equivalent();
            
            //ma.memberSpec
            fes.expr = new TTCN!Value();
            if (ma.memberSpec.isKindOf(TDL!DataInstanceUse) 
              and ma.memberSpec.dataInstance.isDefined()) {
                fes.expr.ref = new TTCN!ReferencedValue();
                fes.expr.ref.head = new TTCN!Head();
                
                //Template ->target = BaseTemplate reference
                //Constant ->target = SingleConstDef reference
                var e = ma.memberSpec.dataInstance.equivalent();
                if (e.isKindOf(TTCN!TemplateDef)) {
                    fes.expr.ref.head.target = e.base;
                } else if (e.isKindOf(TTCN!ConstDef)) {
                    fes.expr.ref.head.target = e.defs.list.get(0);             
                } else {
                    //what else can it be?
                }
                //TODO: handle overriding parameters?
            } else {
                fes.expr.predef = new TTCN!PredefinedValue();
                fes.expr.predef.charString = "\"TODO: Not Implemented Yet\"";
            }
        
            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.member.excludingAll(assigned)) {
            (self.name + ": Skipping unassigned member "+m.name).log(1);
        }
        
    } else {
        expr = new TTCN!ArrayExpression();
    }
    return expr;

}

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 = "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 getTemplateBody() : 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 {
		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;
	}
	return tb;
}

operation Any getTestDescription() : Any {
	if (self.eContainer.isDefined()) {
		if (not self.eContainer.isKindOf(TestDescription)) {
			return self.eContainer.getTestDescription();
		} else {
			return self.eContainer;
		}
	} 
}


operation TDL!Interaction executeSource() : TTCN!FunctionStatementList {
	var ssl = new TTCN!FunctionStatementList();
	
	var sfs = new TTCN!FunctionStatement();
	sfs.communication = new TTCN!CommunicationStatements();
	sfs.communication.send = new TTCN!SendStatement();
	sfs.communication.send.port = self.sourceGate.gate.equivalent();
	sfs.communication.send.send = new TTCN!PortSendOp();
	sfs.communication.send.send.template = new TTCN!InLineTemplate();
	sfs.communication.send.send.template.template = self.argument.getTemplateBody();
	
	ssl.statements.add(sfs);
	ssl.sc.add(";");
	return ssl;
}

operation TDL!Interaction executeTarget(t : TDL!Target) : TTCN!FunctionStatementList {
	var tsl = new TTCN!FunctionStatementList();
	
	var tfs = new TTCN!FunctionStatement();
	tfs.communication = new TTCN!CommunicationStatements();
	tfs.communication.receive = new TTCN!ReceiveStatement();
	tfs.communication.receive.any = new TTCN!PortOrAny();
	tfs.communication.receive.any.ref = t.targetGate.gate.equivalent();
	tfs.communication.receive.receive = new TTCN!PortReceiveOp();
	tfs.communication.receive.receive.template = new TTCN!InLineTemplate();
	tfs.communication.receive.receive.template.template = self.argument.getTemplateBody();
	
	tsl.statements.add(tfs);
	tsl.sc.add(";");
	return tsl;
}

operation TDL!Interaction execute() {
	//TODO: needs adapting to context
	//      add a statement block stack for each component during transformation?
	var td = self.getTestDescription();
	
	//var sfd = (td.name+"_"+self.sourceGate.component.name+"_main").getFunction();
	//var sb = sfd.statement;
	var sc = self.getMainContext(self.sourceGate.component);
	var sb = stack.get(sc).last();

	//var tfd = (td.name+"_"+self.target.first().targetGate.component.name+"_main").getFunction();
	//var tb = tfd.statement;
	var tc = self.getMainContext(self.target.first().targetGate.component);
	var tb = stack.get(tc).last();

	self.execute(sb, tb);
}

operation TDL!Interaction execute(sourceContext : TTCN!StatementBlock, targetContext : TTCN!StatementBlock) {
	//source behaviour
	var ssl = self.executeSource();
	sourceContext.stat.add(ssl);
	//TODO: handle multicast?
	//target behaviour
	var t = self.target.first();
	var tsl = self.executeTarget(t);
	targetContext.stat.add(tsl);

}

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

operation TDL!AlternativeBehaviour execute() {
	if (not transformAlt) return;
	var td = self.getTestDescription();
	//TODO: naive simplified assumption
	var fb = self.block.first().behaviour.first();
	if (fb.isKindOf(TDL!Interaction)) {
		var sc = self.getMainContext(fb.sourceGate.component);
		var sb = stack.get(sc).last();
		self.executeSource(sb);

		var tc = self.getMainContext(fb.target.first().targetGate.component);
		var tb = stack.get(tc).last();
		self.executeTarget(tb);
	}
	
}

operation TDL!AlternativeBehaviour executeTarget(targetContext : TTCN!StatementBlock) {
	var tsl = self.executeTarget();
	targetContext.stat.add(tsl);
}

operation TDL!AlternativeBehaviour executeSource(sourceContext : TTCN!StatementBlock) {
	//TODO: simple assumption - trigger first
	//TODO: needs to follow path, right now triggers first interactions in other branches as well
	var fb = self.block.first().behaviour.first();
	if (fb.isKindOf(TDL!Interaction)) {
		var ssl = fb.executeSource();
		sourceContext.stat.add(ssl);
	}
}

operation TDL!AlternativeBehaviour checkPath() : Boolean {
	var first = true;
	if (self.eContainer.isDefined()) {
		if (self.eContainer.isKindOf(TDL!Block)) {
			if (self.eContainer.eContainer.isKindOf(TDL!AlternativeBehaviour)) {
				if (self.eContainer.eContainer.block.indexOf(self.eContainer) <> 0) {
					first = false;
				} else {
					first = self.eContainer.eContainer.checkPath();
				}
			}
		}
	} 
	
	return first;
}


operation TDL!AlternativeBehaviour executeTarget() : TTCN!FunctionStatementList {
	if (not transformAlt) return;
	//TODO
	
	//target only so far..
	//where should this be placed? in the receiving side?
	//how about source
	//TODO: add explicit scoping
	
	var tsl = new TTCN!FunctionStatementList();
	
	var fs = new TTCN!FunctionStatement();
	fs.behavior = new TTCN!BehaviourStatements();
	fs.behavior.alt = new TTCN!AltConstruct();
	fs.behavior.alt.agList = new TTCN!AltGuardList();
	
	for (block in self.block) {
		var gs = block.executeAltBlock();
		fs.behavior.alt.agList.guardList.add(gs);
	}
	
	tsl.statements.add(fs);
	//tsl.sc.add(";");
	return tsl;
}


operation TDL!Block executeAltBlock() : TTCN!GuardStatement {
	var gs = new TTCN!GuardStatement();
	gs.`guard` = new TTCN!AltGuardChar();
	//TODO: expression

	gs.block = new TTCN!StatementBlock();
	
	//naive assumption
	var cf = self.behaviour.first().getMainContext(self.behaviour.first().target.first().targetGate.component);
	stack.get(cf).add(gs.block);
	
	var i = 0;
	for (b in self.behaviour) {
		if (i==0) {
			if (b.isKindOf(TDL!Interaction)) {
				gs.op = b.getGuardOp();
			} else {
				//TODO: handle other behaviours?	
			}
		} else {
			b.execute();
		}
		i = i+1;
	}

	stack.get(cf).remove(gs.block);
	//TODO: statement block
	return gs;
}

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.receive = new TTCN!PortReceiveOp();
	op.receive.receive.template = new TTCN!InLineTemplate();
	op.receive.receive.template.template = self.argument.getTemplateBody();
	return op;
}

operation TDL!BoundedLoopBehaviour execute() {
	//TODO
	for (b in self.block.behaviour) {
		b.execute();
	}
}

operation TDL!UnboundedLoopBehaviour execute() {
	//TODO
	for (b in self.block.behaviour) {
		b.execute();
	}
}

operation TDL!ActionReference execute() {
	//TODO
}

operation TDL!VerdictAssignment execute() {
	//TODO
}

operation TDL!CompoundBehaviour execute() {
	//TODO
	for (b in self.block.behaviour) {
		b.execute();
	}
}


operation TDL!Behaviour execute() {
	("  Skipping " +self.type.name+" (not yet supported)").println();
	if (self.type.name = "ParallelBehaviour" and false) {
		(indent+"group Parallel").printlno();
		var i = 0;
		for (block in self.block) {
			for (b in block.behaviour) {
				b.execute(indent+"  ");
			}
			if (i <= block.size()) {
				(indent+"else").printlno();
			}
			i = i+1;
		}
		(indent+"end").printlno();
	}
}
