// This file defines templates for transforming Modelica code to C# code.

package CodegenCSharp

import interface SimCodeTV;

// SECTION: SIMULATION TARGET, ROOT TEMPLATE

template translateModel(SimCode simCode) ::=
  match simCode
  case SIMCODE(modelInfo = MODELINFO(__)) then
    let()= textFile(simulationFile(simCode), '<%dotPath(modelInfo.name)%>.cs')
    //let()= textFile(simulationFunctionsFile(simCode), '<%modelInfo.name%>_functions.cs')
    "" // empty result of the top-level template .., only side effects
end translateModel;

template wrapIntoExtraFileIfModelTooBig(Text txtCode, String fileNamePostfix, SimCode simCode) ::=
  match simCode
  case SIMCODE(modelInfo = MODELINFO(__)) then
    if isModelTooBigForCSharpInOneFile(simCode) then
        let wrappedCode =
           <<
           // Simulation code for <%dotPath(modelInfo.name)%> generated by the OpenModelica Compiler.

           using System;
           using System.Collections.Generic;
           using Bodylight.Solvers;
           namespace Bodylight.Models<%modelNameSpace(modelInfo.name)%>
           {
             public partial class <%lastIdentOfPath(modelInfo.name)%> : DAESystem
             {
               <% txtCode %>
             }
           }
           >>
        let fileName = '<%dotPath(modelInfo.name)%>_<%fileNamePostfix%>.cs'
        let()= textFile(wrappedCode, fileName)
        <<

        //-----------------------------------
        //#include  <%fileName%>
        //-----------------------------------

        >>
    else
        txtCode
end wrapIntoExtraFileIfModelTooBig;

// SECTION: SIMULATION TARGET, C# FILE SPECIFIC TEMPLATES


template simulationFile(SimCode simCode) ::=
match simCode
case SIMCODE(modelInfo = MODELINFO(__)) then
<<
// Simulation code for <%dotPath(modelInfo.name)%> generated by the OpenModelica Compiler.

using System;
using System.Collections.Generic;
using Bodylight.Solvers;

namespace Bodylight.Models<%modelNameSpace(modelInfo.name)%>
{

  public partial class <%lastIdentOfPath(modelInfo.name)%> : DAESystem
  {
    <%modelDescription(modelInfo, simCode)%>

    <%wrapIntoExtraFileIfModelTooBig(
        <<
        <%modelMetadata(modelInfo, simCode)%>

        <%sharedLiteralDefinitions(literals, simCode)%>
        >>
        , "MetaData", simCode)
    %>

    <%let fbody = simulationFunctionsBody(simCode)
      if fbody then
        <<
        #region Functions
        <%fbody%>
        #endregion
        >>
      else "//** No functions **"
    %>

    <%functionCallExternalObjectConstructors(extObjInfo, simCode)%>

    <%functionCallExternalObjectDestructors(extObjInfo, simCode)%>

    <% wrapIntoExtraFileIfModelTooBig(
         <<
         <%functionNonlinearResiduals(initialEquations, contextAlgloopInitialisation, simCode)%>

         <%functionNonlinearResiduals(parameterEquations, contextAlgloop, simCode)%>

         <%functionNonlinearResiduals(allEquations, contextAlgloop, simCode)%>

         <%functionInitialNonLinearSystems(initialEquations, parameterEquations, allEquations, simCode)%>
         >>
       , "Residuals", simCode)
    %>

    <%functionInput(modelInfo, simCode)%>

    <%functionOutput(modelInfo, simCode)%>

    <%functionInitSample(timeEvents, simCode)%>

    <%functionStoreDelayed(simCode)%>

    <%functionUpdateBoundStartValues(startValueEquations, simCode)%>

    <% wrapIntoExtraFileIfModelTooBig(
       functionInitialEquations(initialEquations, simCode),
       "InitialEquations", simCode) %>

    <%functionUpdateBoundParameters(parameterEquations, simCode)%>

    <% wrapIntoExtraFileIfModelTooBig(
       functionODE(odeEquations, simCode),
       "FunctionODE", simCode) %>

    <%functionAlgebraic(algebraicEquations, simCode)%>

    <% wrapIntoExtraFileIfModelTooBig(
       functionDAE(allEquations, simCode),
       "FunctionDAE", simCode) %>

    <%functionZeroCrossing(zeroCrossings, simCode)%>

    <%functionRelations(relations, simCode)%>

    <%functionCheckForDiscreteChanges(simCode)%>

    <%/*functionAssertsforCheck(algorithmAndEquationAsserts)*/%>

  }
}
>>
end simulationFile;

template modelNameSpace(Path modelName) ::=
  match modelName
  case QUALIFIED(__) then '.<%name%><%modelNameSpace(path)%>'
  case FULLYQUALIFIED(__) then modelNameSpace(path)
end modelNameSpace;

template lastIdentOfPath(Path modelName) ::=
  match modelName
  case QUALIFIED(__) then lastIdentOfPath(path)
  case IDENT(__)     then name
  case FULLYQUALIFIED(__) then lastIdentOfPath(path)
end lastIdentOfPath;

template sharedLiteralDefinitions(list<Exp> literals, SimCode simCode)
::=
  <<
  <%literals |> literal hasindex i0 fromindex 0 => literalExpConst(literal, i0, simCode) ; separator="\n";empty%>
  >>
end sharedLiteralDefinitions;

template literalExpConst(Exp lit, Integer index, SimCode simCode)
::=
  let name = '_OMC_LIT<%index%>'

  match lit
  case SCONST(__) then
    let escstr = Util.escapeModelicaStringToCString(string)
    <<
    const string <%name%> = "<%Util.escapeModelicaStringToCString(string)%>";
    >>
  case lit as MATRIX(ty=ty as T_ARRAY(__))
  case lit as ARRAY(ty=ty as T_ARRAY(__)) then
    let arrType = expTypeArray(ty, listLength(getDimensionSizes(ty)))
    let sty = expTypeShort(ty)
    let dims = (getDimensionSizes(ty) |> dim => dim ;separator=", ")
    let data = flattenArrayExpToList(lit) |> exp => literalExpConstArrayVal(exp) ; separator=", "
    <<
    static <%arrType%> <%name%> = new <%arrType%>(<%dims%>,-1, new <%sty%>[] { <%data%> });
    >>
  else error(sourceInfo(), 'literalExpConst failed: <%printExpStr(lit)%>')
end literalExpConst;

//should be identical to call daeExp()
//but it is more specific, maybe can catch an illegal exp which is expected to be a constant
template literalExpConstArrayVal(Exp lit)
::=
  match lit
    case ICONST(__) then integer
    case BCONST(__) then if bool then "true" else "false"
    case RCONST(__) then real
    case ENUM_LITERAL(__) then '<%index%>/*ENUM:<%dotPath(name)%>*/'
    case SHARED_LITERAL(__) then '_OMC_LIT<%index%>'
    else error(sourceInfo(), 'literalExpConstArrayVal failed: <%printExpStr(lit)%>')
end literalExpConstArrayVal;

template simulationFunctionsBody(SimCode simCode) ::=
match simCode
case SIMCODE(modelInfo = modelInfo as MODELINFO(__)) then
    (modelInfo.functions |> fn => match fn
          case FUNCTION(__)           then functionBodyRegularFunction(fn, simCode)
          case EXTERNAL_FUNCTION(__)  then functionBodyExternalFunction(fn, simCode) //'EXTERNAL_FUN_NOT_IMPLEMETED(name=<%dotPath(name)%>)'
          case RECORD_CONSTRUCTOR(__) then 'RECORD_CONSTRUCTOR_NOT_IMPLEMENTED(name=<%dotPath(name)%>)'
          else "UNKNOWN_FUNCTION"
    ;separator="\n")
end simulationFunctionsBody;

/*
//not used now
template simulationFunctionsFile(SimCode simCode) ::=
match simCode
case SIMCODE(modelInfo = MODELINFO(__)) then
<<
// Simulation functions code for <%modelInfo.name%> generated by the OpenModelica Compiler.

using System;
using Bodylight.Solvers;
namespace Bodylight.Models
{
  public partial class <%dotPath(modelInfo.name)%> : DAESystem
  {

    <%simulationFunctionsBody(simCode)%>

  }
}
>>
end simulationFunctionsFile;
*/

template recordDeclaration(RecordDeclaration recDecl, SimCode simCode)
 "Generates structs for a record declaration."
::=
  match recDecl
  case RECORD_DECL_FULL(__) then
    <<
    struct <%name%> {
      <%variables |> var as VARIABLE(__) => '<%varType(var)%> <%crefStr(var.name,simCode)%>;' ;separator="\n"%>
    };
    >>
  case RECORD_DECL_DEF(__) then "RECORD_DECL_DEF_NOT_SUPPORTED"
  else "UNKNOWN_RECORD_DECL_"
end recordDeclaration;



template functionBodyRegularFunction(Function fn, SimCode simCode)
 "Generates the body for a Modelica/MetaModelica function."
::=
match fn
case FUNCTION(__) then
  let()= System.tmpTickReset(1)
  let fname = underscorePath(name)
  let retType = match outVars //a hack, only one output var for now
                case fv :: _ then varType(fv)
                else "void"
  let retVar = match outVars
               case (fv as VARIABLE(__)) :: _ then crefStr(fv.name,simCode)
  let varInits = variableDeclarations |> var => varInit(var, simCode)
                 ;separator="\n" //TODO:special end of line,see varInit
  let bodyPart = funStatement(body, simCode)
  <<
  <%retType%> _<%fname%>(<%functionArguments |> var => funArgDefinition(var,simCode) ;separator=", "%>)
  {
    <%varInits%>

    <%bodyPart%>

    _return:
    <%if outVars then 'return <%retVar%>;'%>
  }

  >>
end functionBodyRegularFunction;


template functionBodyExternalFunction(Function fn, SimCode simCode)
 "Generates the body for an external function (just a wrapper)."
::=
match fn
case efn as EXTERNAL_FUNCTION(__) then
  let()= System.tmpTickReset(1)
  let fname = underscorePath(name)
  let retType = match outVars
                case fv :: _ then varType(fv)
                else "void"
  let &preExp = buffer "" /*BUFD*/
  let callPart = //extFunCall(fn, &preExp /*BUFC*/, &varDecls /*BUFD*/)
      let args = (extArgs |> arg => extArg(arg, &preExp, simCode) ;separator=", ")
      let lib = hackGetFirstExternalFunctionLib(libs) // match libs case fnLib::_ then fnLib else "NO_EXT_LIB"
      'Bodylight.Solvers.ExternalLibraries.<%lib%>.<%extName%>(<%args%>);'
  <<
  static <%retType%> _<%fname%>(<%funArgs |> var => funArgDefinition(var,simCode) ;separator=", "%>)
  {
    <%preExp%>
    <%if outVars then "return "
    %><%callPart%>
  }

  >>
end functionBodyExternalFunction;


template extArg(SimExtArg extArg, Text &preExp, SimCode simCode)
 "Helper to extFunCall."
::=
  match extArg
  case SIMEXTARG(cref=c, outputIndex=oi, isArray=true, type_=t) then
    let name = if oi then 'NOT_SUPPORTED_out.targ<%oi%>' else crefStr(c,simCode)
    name
    //let shortTypeStr = expTypeShort(t)
    //'(<%extType(t,isInput,true)%>) data_of_<%shortTypeStr%>_array(&(<%name%>))'
  case SIMEXTARG(cref=c, isInput=ii, outputIndex=0, type_=t) then
    let cr = crefStr(c,simCode)
    cr
  case SIMEXTARG(cref=c, isInput=ii, outputIndex=oi, type_=t) then
    'NOT_SUPPORTED_ext<%oi%>'
  case SIMEXTARGEXP(__) then
    daeExp(exp, contextFunction, &preExp, simCode)
  case SIMEXTARGSIZE(cref=c) then
    let name = if outputIndex then 'NOT_SUPPORTED_SIZE_out.targ<%outputIndex%>' else crefStr(c,simCode)
    match exp
    case ICONST(__) then
      '<%name%>.size<%integer%>'
    else
      '<%name%>.size(<%daeExp(exp, contextFunction, &preExp, simCode)%>)'
end extArg;


template varInit(Variable var, SimCode simCode)
 "Generates code to initialize variables."
::=
match var
case var as VARIABLE(__) then
  if instDims then
    let varName = crefStr(var.name,simCode)
    let &preDimsExp = buffer ""
    let instDimsInit = (var.instDims |> exp =>
          daeExp(exp, contextFunction, &preDimsExp, simCode)
        ;separator=", ")
    <<
    <%preDimsExp%>
    var <%varName%> = new <%expTypeArray(var.ty,listLength(instDims))%>(<%instDimsInit%>);<%
      //special \n solution ... the new line is dependent on this line, but the varInit as a whole does not put \n
      match var.value  case SOME(CREF(componentRef = cr)) then
      '<%\n%><%varName%>.CopyFrom(<%crefStr(cr,simCode)%>);'
    %>
    >>
  else
    <<
    <%varType(var)%> <%crefStr(var.name,simCode)%>;
    >>
else "UNSUPPORTED_VARIABLE_varInit"

end varInit;

template funArgDefinition(Variable var, SimCode simCode)
::=
  match var
  case VARIABLE(__) then '<%varType(var)%> <%crefStr(name,simCode)%>'
  case FUNCTION_PTR(__) then 'funArgDefinition_UNSUPPORTED_fnptr <%name%>'
  else "UNSUPPORTED_VARIABLE_funArgDefinition"
end funArgDefinition;

template funStatement(list<DAE.Statement> statementLst, SimCode simCode)
 "Generates function statements."
::=
  statementLst |> stmt => algStatement(stmt, contextFunction, simCode)
end funStatement;

constant String localRepresentationArrayDefines =
"var X = States; var Xd = StatesDerivatives; var Y = Algebraics; var YI = AlgebraicsInt; var YB = AlgebraicsBool;
var P = Parameters; var PI = ParametersInt; var PB = ParametersBool; var PS = ParametersString; var EO = ExternalObjects;
var preX = PreStates; var preXd = PreStatesDerivatives; var preY = PreAlgebraics; var preYI = PreAlgebraicsInt; var preYB = PreAlgebraicsBool;
var mathEVPre = MathEventsValuePre;
/*TODO: HACK FOR non-time varying params*/ var preP = P; var prePI = PI; var prePB = PB;";

template modelDescription(ModelInfo modelInfo, SimCode simCode) ::=
match modelInfo
case MODELINFO(varInfo = VARINFO(__), vars = SIMVARS(__)) then
<<
#region Model description

const int
  NG = <%varInfo.numZeroCrossings%>,
  NREL = <%varInfo.numRelations%>,
  NM   = <%varInfo.numMathEventFunctions%>,
  NG_SAM = <%varInfo.numTimeEvents%>,
  NX = <%varInfo.numStateVars%>,
  NY = <%varInfo.numAlgVars%> + <%varInfo.numDiscreteReal%>,
  NA = <%varInfo.numAlgAliasVars%>, // number of alias variables
  NP = <%varInfo.numParams%>,
  NO = <%varInfo.numOutVars%>,
  NI = <%varInfo.numInVars%>,
  NEXT = <%varInfo.numExternalObjects%>,
  //NFUNC = <%listLength(functions)%>, // number of functions used by the simulation
  NYSTR = <%varInfo.numStringAlgVars%>,
  NASTR = <%varInfo.numStringAliasVars%>, // number of alias string variables
  NYI = <%varInfo.numIntAlgVars%>,
  NAI = <%varInfo.numIntAliasVars%>, // number of alias int variables
  NYB = <%varInfo.numBoolAlgVars%>,
  NAB = <%varInfo.numBoolAliasVars%>, // number of alias bool variables
  NPI = <%varInfo.numIntParams%>,
  NPB = <%varInfo.numBoolParams%>,
  NPSTR = <%varInfo.numStringParamVars%>,
  NAAll = NA+NAI+NAB+NASTR, // number of all aliases
  NLS   = <%varInfo.numLinearSystems%>,
  NNLS  = <%varInfo.numNonLinearSystems%>,
  NMIXS = <%varInfo.numMixedSystems%>;

  private class <%lastIdentOfPath(name)%>Metadata : DAESystemMetadata
  {
    public override int ZeroCrossingsCount  { get { return NG; } }
    public override int RelationsCount      { get { return NREL; } }
    public override int MathEventsCount      { get { return NM; } }
    public override int SampleTypesCount    { get { return NG_SAM; } }

    public override int OutputsCount   { get { return NO; } }
    public override int InputsCount    { get { return NI; } }
    public override int ExternalObjectsCount { get { return NEXT; } }

    public override int LinearSystemsCount { get { return NLS; } }
    public override int NonLinearSystemsCount { get { return NNLS; } }
    public override int MixedSystemsCount { get { return NMIXS; } }

    public <%lastIdentOfPath(name)%>Metadata(VarData[][] varData, AliasVarData[] aliasVarData, Dictionary<string, int> fullVarDictionary,
                         Dictionary<int, double> startValues, Dictionary<int, int> startValuesInt, Dictionary<int, bool> startValuesBool,
                         string[] startParameterString)
           : base("<%dotPath(name)%>", varData, aliasVarData, fullVarDictionary, startValues, startValuesInt, startValuesBool, startParameterString)
    {}

    public override DAESystem NewSystem()
    {
      return new <%lastIdentOfPath(name)%>();
    }
  }

  public static readonly DAESystemMetadata SystemMetadata;

  public <%lastIdentOfPath(name)%>() : base(<%lastIdentOfPath(name)%>.SystemMetadata)
  {}
#endregion
>>
end modelDescription;

template modelMetadata(ModelInfo modelInfo, SimCode simCode) ::=
match modelInfo
case MODELINFO(varInfo = VARINFO(__), vars = SIMVARS(__)) then
<<
static <%lastIdentOfPath(name)%>() {
  var varData = new VarData[(int)SimVarType.NonAliasTypesCnt][];
  VarData[] vd;

  <%{
        varInfos("State",     vars.stateVars,    varInfo.numStateVars, simCode),
        varInfos("StateDer",  vars.derivativeVars, varInfo.numStateVars, simCode),
        varInfosAlgebraic("Algebraic",    vars.algVars, vars.discreteAlgVars, varInfo.numAlgVars, varInfo.numDiscreteReal, simCode),
    //may be later:
    //varInfos("Algebraic",    vars.algVars, varInfo.numAlgVars, simCode),
    //varInfos("AlgebraicDiscrete",    vars.discreteAlgVars, varInfo.numDiscreteReal, simCode),
        varInfos("AlgebraicInt",    vars.intAlgVars,  varInfo.numIntAlgVars, simCode),
        varInfos("AlgebraicBool",   vars.boolAlgVars,  varInfo.numBoolAlgVars, simCode),
        varInfos("AlgebraicString", vars.stringAlgVars, varInfo.numStringAlgVars, simCode),
        varInfos("Parameter",       vars.paramVars,      varInfo.numParams, simCode),
        varInfos("ParameterInt",    vars.intParamVars,   varInfo.numIntParams, simCode),
        varInfos("ParameterBool",   vars.boolParamVars,  varInfo.numBoolParams, simCode),
        varInfos("ParameterString", vars.stringParamVars, varInfo.numStringParamVars, simCode)
    } ;separator="\n\n"%>

  #region *** all aliases ***
  var fvd = Bodylight.Solvers.SystemMetadata.SetupVariableNamesLookup(varData, NAAll);
  var ad = new AliasVarData[NAAll];
  <%{
        aliasVarInfos("alias Algebraic", vars.aliasVars, /*varInfo.numAlgAliasVars,*/ "", simCode),
        aliasVarInfos("alias AlgebraicInt", vars.intAliasVars, /*varInfo.numIntAliasVars,*/ "NA+", simCode),
        aliasVarInfos("alias AlgebraicBool", vars.boolAliasVars, /*varInfo.numBoolAliasVars,*/ "NA+NAI+", simCode),
        aliasVarInfos("alias AlgebraicString", vars.stringAliasVars, /*varInfo.numStringAliasVars,*/ "NA+NAI+NAB+", simCode)
    } ;separator="\n\n"%>
  #endregion

  #region *** start values ***
  var svs = new Dictionary<int, double>();
  var svsI = new Dictionary<int, int>();
  var svsB = new Dictionary<int, bool>();
  var svsS = new string[NPSTR];

  <%initVals("svs", "State",     vars.stateVars, simCode)%>
  <%initVals("svs", "StateDer",  vars.derivativeVars, simCode)%>
  <%initVals("svs", "Algebraic", vars.algVars, simCode)%>
  <%initVals("svs", "Algebraic", vars.discreteAlgVars, simCode)%>
  <%initVals("svs", "Parameter", vars.paramVars, simCode)%>
  <%initVals("svsI", "AlgebraicInt", vars.intAlgVars, simCode)%>
  <%initVals("svsI", "ParameterInt", vars.intParamVars, simCode)%>
  <%initVals("svsB", "AlgebraicBool", vars.boolAlgVars, simCode)%>
  <%initVals("svsB", "ParameterBool", vars.boolParamVars, simCode)%>

  // *** AlgebraicStrings not fully supported ***
  <%/*initVals("AlgebraicString", vars.stringAlgVars, simCode)*/%>
  <%initValsString("svsS", "ParameterString", vars.stringParamVars, simCode)%>

  #endregion

  SystemMetadata = new <%lastIdentOfPath(name)%>Metadata(varData, ad, fvd, svs, svsI, svsB, svsS);
}
>>
end modelMetadata;


template simVarTypeName(VarKind varKind, Type type_) ::=
  match varKind
  case VARIABLE(__)    then "Algebraic" + simVarTypeNamePostfix(type_)
  case STATE(__)       then "State"
  case STATE_DER(__)   then "StateDer"
  case DUMMY_DER(__)   //then "Algebraic"
  case DUMMY_STATE(__) then "Algebraic"
  case DISCRETE(__)    then 'Algebraic<%simVarTypeNamePostfix(type_)%>/*d*/'
  case PARAM(__)       then "Parameter" + simVarTypeNamePostfix(type_)
  else error(sourceInfo(), "Unexpected simVarTypeName varKind")
  //case EXTOBJ(__)      then "EO"
  //case CONST(__)       then "CONST_VAR_KIND"
end simVarTypeName;

template simVarTypeNamePostfix(Type type_) ::=
  match type_
  case T_INTEGER(__)  then "Int"
  case T_REAL(__) then ""
  case T_BOOL(__) then "Bool"
  case T_STRING(__) then "String"
  case T_ENUMERATION(__) then "/*Enum*/"
  //case T_COMPLEX(__) then "Complex"
  //case T_ARRAY(__) then "Array"
  //case T_OTHER(__) then "Other"
  else error(sourceInfo(), "Unknown simVarTypeNamePostfix")
end simVarTypeNamePostfix;

template varInfos(String regionName, list<SimVar> varsLst, Integer varsCnt, SimCode simCode) ::=
  //if varsLst then
  //we need all of them to initialize also empty arrays
   <<
   #region *** (<%varsCnt%>) <%regionName%> variable data ***
   vd = new VarData[<%varsCnt%>];
   <% varsLst |> sv as SIMVAR(__) =>
      <<
      vd[<%index%>] = new VarData("<%crefStrWithDerOnLast(name, simCode)%>", "<%Util.escapeModelicaStringToCString(comment)%>"<%noaliasSimVarInfoRest(sv)%>);
      >>
      ;separator="\n"
   %>
   varData[(int)SimVarType.<%regionName%>] = vd;
   #endregion<%\n%>
   >>
end varInfos;

template varInfosAlgebraic(String regionName, list<SimVar> varsLstAlg, list<SimVar> varsLstDisc, Integer varsCntAlg, Integer varsCntDisc, SimCode simCode) ::=
//A hack to temorarily handle discrete algebraics
//TODO: fix it
  //if varsLst then
  //we need all of them to initialize also empty arrays
   <<
   #region *** (<%varsCntAlg%> + <%varsCntDisc%>) algebraic and discrete algebraic variable data ***
   vd = new VarData[<%varsCntAlg%> + <%varsCntDisc%>];
   <% varsLstAlg |> sv as SIMVAR(__) =>
      <<
      vd[<%index%>] = new VarData("<%crefStrWithDerOnLast(name, simCode)%>", "<%Util.escapeModelicaStringToCString(comment)%>"<%noaliasSimVarInfoRest(sv)%>);
      >>
      ;separator="\n"
   %>
   <% varsLstDisc |> sv as SIMVAR(__) =>
      <<
      vd[<%varsCntAlg%> + <%index%>] = new VarData("<%crefStrWithDerOnLast(name, simCode)%>", "<%Util.escapeModelicaStringToCString(comment)%>"<%noaliasSimVarInfoRest(sv)%>);
      >>
      ;separator="\n"
   %>

   varData[(int)SimVarType.<%regionName%>] = vd;
   #endregion<%\n%>
   >>
end varInfosAlgebraic;


//rather hacky ... when more attributes, use named members initalization
template noaliasSimVarInfoRest(SimVar simVar) ::=
  match simVar
  case sv as SIMVAR(__) then
    if initialValue then
        match nominalValue
        case SOME(exp as RCONST(__)) then ', new SimVarAttributes(<%exp.real%>, true, <%sv.isFixed%>)'
        case SOME(__) then error(sourceInfo(), "Unexpected expression for nominal value.")
        else ', SimVarAttributes.DefaultUseStart<%if sv.isFixed then "Fixed"%>'
    else
      match nominalValue
      case SOME(exp as RCONST(__)) then ', new SimVarAttributes(<%exp.real%>, false, <%sv.isFixed%>)'
      case SOME(__) then "Error-other-nominal-value-than-real-constant-not-supported"
      else if sv.isFixed then ", SimVarAttributes.DefaultFixed"
           //else nothing ...
end noaliasSimVarInfoRest;

template aliasVarInfos(String regionName, list<SimVar> varsLst, /*Integer varsCnt,*/ String offset, SimCode simCode) ::=
  if varsLst then
   <<
   #region *** (<%listLength(varsLst)%>) <%regionName%> variable infos ***
   <% varsLst |> sv as SIMVAR(__) hasindex i0 =>
      let strNameCmt = '"<%crefStrWithDerOnLast(name, simCode)%>", "<%Util.escapeModelicaStringToCString(comment)%>"'
      let aliasLookup =
        match aliasvar
        case NOALIAS(__)  then
          error(sourceInfo(), "Unexpected non-alias variable amongst aliases.")
        case ALIAS(__) then
          'fvd["<%crefStrWithDerOnLast(varName, simCode)%>"]'
        case NEGATEDALIAS(__)   then
          '-fvd["<%crefStrWithDerOnLast(varName, simCode)%>"]'
      <<
      ad[<%offset%><%i0%>] = new AliasVarData("<%crefStrWithDerOnLast(name, simCode)%>", "<%Util.escapeModelicaStringToCString(comment)%>", <%aliasLookup%>);
      >>
      ;separator="\n"
   %>
   #endregion<%\n%>
   >>
end aliasVarInfos;




//template nominalVal(DAE.Exp nomExp) ::=
//    match nomExp
//    case RCONST(__) then real
//    else 'other nominal value than real const not supported'
//end nominalVal;

template initVals(String arrName, String typName, list<SimVar> varsLst, SimCode simCode) ::=
if varsLst then
<<

#region **** <%typName%> start values *****
<%
  varsLst |> sv as SIMVAR(__) =>
    match initialValue
    case SOME(v) then
      match v
      case ICONST(__)
      case RCONST(__)
      case SCONST(__)
      case BCONST(__)
      case ENUM_LITERAL(__) then
        let &preExp = buffer "" //dummy ... the value should be always a constant
        match daeExp(v, contextOther, &preExp, simCode)
        case vStr as "0"
        case vStr as "0.0"
        case vStr as "false"
        case vStr as "(0)" then
          '//<%arrName%>[<%sv.index%>] = <%vStr%>; //<%crefStr(sv.name, simCode)%> --> zero val'
        //case "true" then //a hack for now
        // '<%arrName%>[<%sv.index%>] = 1.0; //true //<%crefStr(sv.name, simCode)%>'
        case vStr then
          '<%arrName%>[(int)SimVarType.<%typName%>+(<%sv.index%><<4)] = <%vStr%>; //<%crefStr(sv.name, simCode)%>'
        end match
    else '//<%arrName%>[<%sv.index%>] = 0.0; //<%crefStr(sv.name, simCode)%> --> default val'
  ;separator="\n"
%>
#endregion
>>
end initVals;

template initValsString(String arrName, String typName, list<SimVar> varsLst, SimCode simCode) ::=
if varsLst then
<<

#region **** (<%listLength(varsLst)%>) <%typName%> start values *****
<%
  varsLst |> sv as SIMVAR(__) =>
    match initialValue
      case SOME(v) then
        let &preExp = buffer "" //dummy ... the value should be always a constant
        let val = daeExp(v, contextOther, &preExp, simCode)
        '<%arrName%>[<%sv.index%>] = <%val%>; //<%crefStr(sv.name, simCode)%>'
    else '<%arrName%>[<%sv.index%>] = ""; //<%crefStr(sv.name, simCode)%> --> default val'
  ;separator="\n"
%>
#endregion
>>
end initValsString;

/*
template initFixed(list<SimVar> simVarLst, String varType, Boolean defaultIsTrue, SimCode simCode) ::=
  simVarLst |> SIMVAR(__) =>
    <<
    <%if defaultIsTrue then (if isFixed then "//")
      else (if not isFixed then "//")
    %><%varType%>InitialFixed[<%index%>] = <%isFixed%>; /* <%crefStr(name, simCode)%> */
    >>
  ;separator="\n"
end initFixed;
*/

template functionCallExternalObjectConstructors(ExtObjInfo extObjInfo, SimCode simCode)
 "Generates external objects construction calls."
::=
match extObjInfo
case EXTOBJINFO(__) then
  <<
  public override void FunCallExternalObjectConstructors() {
    <% localRepresentationArrayDefines %>
    <% vars |> var as SIMVAR(initialValue=SOME(exp)) =>
      let &preExp = buffer "" /*BUFD*/
      let arg = daeExp(exp, contextOther, &preExp, simCode)
      <<
      <%preExp%>
      <%cref(var.name, simCode)%> = <%arg%>;
      >>
    %>
    <%aliases |> (var1, var2) => '<%cref(var1,simCode)%> = <%cref(var2,simCode)%>;' ;separator="\n"%>
  }

  >>
end functionCallExternalObjectConstructors;

template functionCallExternalObjectDestructors(ExtObjInfo extObjInfo, SimCode simCode)
 "Generates external objects construction calls."
::=
match extObjInfo
case EXTOBJINFO(__) then
  <<
  public override void FunCallExternalObjectDestructors() {
    <% localRepresentationArrayDefines %>
    <% vars |> var as SIMVAR(varKind=ext as EXTOBJ(__)) =>
      <<
      _<%underscorePath(ext.fullClassName)%>_destructor(<%cref(var.name, simCode)%>);
      >>
      ;separator="\n"
    %>
  }

  >>
end functionCallExternalObjectDestructors;

template functionAlgebraic(list<list<SimEqSystem>> algebraicEquations, SimCode simCode) ::=
let()= System.tmpTickReset(1)
<<
public override void FunAlgebraics()
{
  <% localRepresentationArrayDefines %>
  var preRels = PreRelations;
  <% List.flatten(algebraicEquations) |> it => equation_(it,contextSimulationNonDiscrete, simCode) ;separator="\n"%>
}
>>
end functionAlgebraic;


template functionInput(ModelInfo modelInfo, SimCode simCode) ::=
match modelInfo
case MODELINFO(varInfo = VARINFO(__), vars = SIMVARS(__)) then
<<
public override void InputFun()
{
  <% localRepresentationArrayDefines %>
  <%vars.inputVars |> SIMVAR(__) hasindex i0 =>
  <<
  <%cref(name, simCode)%> = inputVars[<%i0%>];
  >> ;separator="\n"%>
}
>>
end functionInput;

template functionOutput(ModelInfo modelInfo, SimCode simCode) ::=
match modelInfo
case MODELINFO(varInfo = VARINFO(__), vars = SIMVARS(__)) then
<<
public override void OutputFun()
{
  <% localRepresentationArrayDefines %>
  // * not yet
  <%vars.outputVars |> SIMVAR(__) hasindex i0 =>
  <<
  //outputVars[<%i0%>] = <%cref(name, simCode)%>;
  >> ;separator="\n"%>
}
>>
end functionOutput;


template functionInitSample(list<BackendDAE.TimeEvent> timeEvents, SimCode simCode)
  "Generates function initSample() in simulation file."
::=
  /* Initializes the raw time events of the simulation using the now
     calcualted parameters. */
  <<
  public override void FunSampleInit()
  {
    <% localRepresentationArrayDefines %>
    var SA = SampleHeap.Samples;

    <%(timeEvents |> timeEvent  =>
      match timeEvent
        case SAMPLE_TIME_EVENT(__) then
          let &preExp = buffer "" /*BUFD*/
          let startE = daeExp(startExp, contextOther, &preExp, simCode)
          let intervalE = daeExp(intervalExp, contextOther, &preExp, simCode)
          <<
          <%preExp%>
          /* $P$sample<%index%> */
          SA[i++] = new OneSample(<%intervalE%>, <%startE%>, <%intSub(index, 1)%>, false);
          >>
        else ''
        )%>
  }
  >>
end functionInitSample;


template functionDAE(list<SimEqSystem> allEquationsPlusWhen, SimCode simCode)
::=
let()= System.tmpTickReset(1)
<<
public override bool FunDAE()
{
  <% localRepresentationArrayDefines %>
  var rels = Relations; var preRels = PreRelations; var relsHE = RelationsHysteresisEnabled; var relsZC = RelationsZeroCrossing;
  var needToIterate = false;

  <%allEquationsPlusWhen |> eq => equation_(eq, contextSimulationDiscrete, simCode) ;separator="\n"%>

  return needToIterate;
}
>>
end functionDAE;

template whenConditions(list<ComponentRef> conditions, SimCode simCode)
" Generates reinit statemeant"
::=
  //if conditions then
  (conditions |> cr => '<%cref(cr, simCode)%> && !<%preCref(cr, simCode)%> /* edge */';separator=" || ")
  //else "false"
end whenConditions;

template infoArgs(builtin.SourceInfo info)
::=
  match info
  case SOURCEINFO(__) then '"<%fileName%>",<%lineNumberStart%>,<%columnNumberStart%>,<%lineNumberEnd%>,<%columnNumberEnd%>,<%if isReadOnly then 1 else 0%>'
end infoArgs;

template assertCommon(Exp condition, Exp message, builtin.SourceInfo info, Context context, SimCode simCode)
 "Generates an assert statement."
::=
  let &preExpCond = buffer ""
  let &preExpMsg = buffer ""
  let condVar = daeExp(condition, context, &preExpCond, simCode)
  let msgVar = daeExp(message, context, &preExpMsg, simCode)
  <<
  <%preExpCond%>
  if (!<%condVar%>) {
    <%preExpMsg%>
    //<%infoArgs(info)%>
    throw new Exception(<%msgVar%>);
  }
  >>
end assertCommon;



template functionZeroCrossing(list<ZeroCrossing> zeroCrossingLst, SimCode simCode) ::=
let()= System.tmpTickReset(1)
<<
public override void FunZeroCrossings(double time, double[] gout) //TODO:??time in original is *t only ... how is it called?
{
  <% localRepresentationArrayDefines %>
  var rels = Relations; var relsHE = RelationsHysteresisEnabled; var relsZC = RelationsZeroCrossing;
  <%zeroCrossingLst |> ZERO_CROSSING(__) hasindex i0 =>
     let &preExp = buffer ""
     let zc = zeroCrossing(relation_, &preExp, simCode)
     <<
     //ZC for: <%ExpressionDump.printExpStr(relation_)%>
     <%preExp%>
     gout[<%i0%>] = <%zc%>;
     >>
  ;separator="\n"%>
}
>>
end functionZeroCrossing;

template zeroCrossing(Exp zcExp, Text &preExp, SimCode simCode) ::=
  match zcExp
  case RELATION(index=-1) then
    //probably a discrete only relation (e.g., with parameter against a constant)
    //should be used only in a logical expression (not a proper zero crossing)
    //going like logical unary only
    //no need for hysteresis there as it should be constant during continuous intervals
    let e1 = daeExp(zcExp, contextZeroCross, &preExp, simCode)
    '(<%e1%>)?1:-1'
  case RELATION(__) then
    let e1 = daeExp(exp1, contextZeroCross, &preExp, simCode)
    let e2 = daeExp(exp2, contextZeroCross, &preExp, simCode)
    zeroCrossingValue(zcExp, e1, e2)
  case (LBINARY(exp1=CALL(path=IDENT(name="initial"), expLst={}), operator=OR(__) ))
  case (LBINARY(exp1=LUNARY( operator=NOT(__),exp=CALL(path=IDENT(name="initial"), expLst={}) ), operator=AND(__) ))
    then
    '/*opt out initial()*/<%zeroCrossing(exp2, &preExp, simCode)%>'
  case (LBINARY(__))
  case (LUNARY(__))
    then
    let e1 = daeExp(zcExp, contextZeroCross, &preExp, simCode)
    '(<%e1%>) ? 1:-1'

  case CALL(path=IDENT(name="integer"), expLst={exp1, idx})
  case CALL(path=IDENT(name="floor"), expLst={exp1, idx})
   then
    let e1 = daeExp(exp1, contextZeroCross, &preExp, simCode)
    let indx = daeExp(idx, contextZeroCross, &preExp, simCode)
    '(Math.Floor(<%e1%>) != mathEVPre[<%indx%>]) ? 1:-1'
  case CALL(path=IDENT(name="ceil"), expLst={exp1, idx}) then
    let e1 = daeExp(exp1, contextZeroCross, &preExp, simCode)
    let indx = daeExp(idx, contextZeroCross, &preExp, simCode)
    '(Math.Ceil(<%e1%>) != mathEVPre[<%indx%>]) ? 1:-1'
  case CALL(path=IDENT(name="div"), expLst={exp1, exp2, idx}) then
    let e1 = daeExp(exp1, contextZeroCross, &preExp, simCode)
    let e2 = daeExp(exp2, contextZeroCross, &preExp, simCode)
    let indx = daeExp(idx, contextZeroCross, &preExp, simCode)
    '( (int)(<%e1%>/<%e2%>) != (int)mathEVPre[<%indx%>] ) ? 1:-1'

/** these were attemt to use Min/Max as "logical" zero crossing operators,
   ** but failed to work due to non-equivalence of  g > 0 and -g <= 0
   ** when used in Min and Max ... it would work with both-sided epsilon on hysteresis (as C runtime uses),
   ** but not with epsilon only on the > side (to allow equality to be found precisely)
   ** so, sadly, this won't work ...
   ** instead, we should find every relation change and then decide if it is an event (a condition changed)
  case (LBINARY(operator=AND(__))) then
    <<Math.Min(
      (<%zeroCrossing(exp1, &preExp, simCode)%>),
      (<%zeroCrossing(exp2, &preExp, simCode)%>)
    )>>
  case (LBINARY(operator=OR(__))) then
    <<Math.Max(
      (<%zeroCrossing(exp1, &preExp, simCode)%>),
      (<%zeroCrossing(exp2, &preExp, simCode)%>)
    )>>
  case (LUNARY(__)) then
    let e1 = daeExp(zcExp, contextOther, &preExp, simCode)
    //the very high number is to assure a good chance for root finding to take advantage of interpolation
    //in cases this is combined via Min or Max with something else
    '(<%e1%>)?1e12:-1e12'
**/

  else
    error(sourceInfo(), 'Unexpected zero crossing expression: <%ExpressionDump.printExpStr(zcExp)%>')

end zeroCrossing;

template zeroCrossingOpFunc(Operator it) ::=
  match it
  case LESS(__)      then "Less"
  case GREATER(__)   then "Greater"
  case LESSEQ(__)    then "LessEq"
  case GREATEREQ(__) then "GreaterEq"
  case EQUAL(__)     then "!!!Equal"
  case NEQUAL(__)    then "!!!NEqual"
  else "!!!unsupported ZC operator!!!"

end zeroCrossingOpFunc;

template functionRelations(list<ZeroCrossing> relations, SimCode simCode) "template functionRelations
  Generates function in simulation file.
  This is a helper of template simulationFile."
::=
  let &varDecls = buffer "" /*BUFD*/
  //using only contextOther, here, only relation operands are genereted
  let relationsCode = relationsTpl(relations, contextOther, simCode)
  //let relationsCodeElse = relationsTpl(relations, contextOther, simCode)

  <<
  public override void FunUpdateRelations()
  {
    <% localRepresentationArrayDefines %>
    var rels = Relations; var relsHE = RelationsHysteresisEnabled; var relsZC = RelationsZeroCrossing;

    <%relationsCode%>
  }
  >>
end functionRelations;

template relationsTpl(list<ZeroCrossing> relations, Context context, SimCode simCode)
 "Generates code for zero crossings."
::=
  (relations |> ZERO_CROSSING(__) hasindex i0 =>
    relationTpl(i0, relation_, context, simCode)
  ;separator="\n";empty)
end relationsTpl;


template relationTpl(Integer index1, Exp relationExp, Context context, SimCode simCode)
 "Generates code for a zero crossing."
::=
  match relationExp
  case RELATION(__) then
    let &preExp = buffer ""
    let e1 = daeExp(exp1, context, &preExp, simCode)
    let e2 = daeExp(exp2, context, &preExp, simCode)
    <<
    //relation[<%index%>]: <%ExpressionDump.printExpStr(relationExp)%>
    <%preExp%>
    <%relationWithZeroCrossing(relationExp, e1, e2)%>
    >>
  else
    <<
    // UNKNOWN Relation[<%index1%>]:  <%ExpressionDump.printExpStr(relationExp)%>
    >>
end relationTpl;

// New runtime function. What should it do?
template functionStoreDelayed(SimCode simCode) ::=
let()= System.tmpTickReset(1)
<<
public override void FunStoreDelayed()
{
  <% localRepresentationArrayDefines %>
}
>>
end functionStoreDelayed;

template functionODE(list<list<SimEqSystem>> stateContEquations, SimCode simCode) ::=
let()= System.tmpTickReset(1)
<<
public override void FunODE()
{
  <% localRepresentationArrayDefines %>
  var preRels = PreRelations;
  <%stateContEquations |> eqs =>
     (eqs |> it => equation_(it, contextSimulationNonDiscrete, simCode) ;separator="\n")
     ;separator="\n"%>
}
>>
end functionODE;

//TODO:??there is st more in the trunk
template functionUpdateBoundStartValues(list<SimEqSystem> startValueEquations, SimCode simCode) ::=
let()= System.tmpTickReset(1)
<<
public override void UpdateBoundStartValues()
{
  <% localRepresentationArrayDefines %>
  <%startValueEquations |> saeq as SES_SIMPLE_ASSIGN(__) => equation_(saeq, contextOther, simCode) ;separator="\n"%>

  //if (sim_verbose) {
    <%startValueEquations |> SES_SIMPLE_ASSIGN(__) =>
    <<
    //Debug.WriteLine("Setting variable start value: {0}(start={1})", "<%cref(cref, simCode)%>", <%cref(cref, simCode)%>);
    >> ;separator="\n"%>
  //}
}
>>
end functionUpdateBoundStartValues;

template functionNonlinearResiduals(list<SimEqSystem> allEquations, Context context, SimCode simCode) ::=
  let()= System.tmpTickReset(1)
  (allEquations |> eq => match eq
    case SES_MIXED(__)     then functionNonlinearResidual(cont, context, simCode)
    case SES_NONLINEAR(__) then functionNonlinearResidual(eq, context, simCode)
  ;separator="\n")
end functionNonlinearResiduals;

template functionNonlinearResidual(SimEqSystem equ, Context context, SimCode simCode) ::=
  match equ
  case SES_NONLINEAR(nlSystem=nls as NONLINEARSYSTEM(__)) then
    <<
    int ResidualFun<%nls.index%>(int n, double[] xloc, double[] res, int iflag)
    {
       <% localRepresentationArrayDefines %>
       var rels = Relations; var preRels = PreRelations; var relsHE = RelationsHysteresisEnabled; var relsZC = RelationsZeroCrossing;
       <%nls.crefs |> cr hasindex i0 => '<%cref(cr,simCode)%> = xloc[<%i0%>];' ;separator="\n"%>
       <%nls.eqs |> saeq => //as SES_SIMPLE_ASSIGN(__) =>
         (match saeq
          case SES_RESIDUAL(__) then ""
          else
           equation_(saeq, context, simCode)
         ) ;separator="\n"
       %>
       <%nls.eqs |> SES_RESIDUAL(__) hasindex i0 =>
         let &preExp = buffer ""
         let expPart = daeExp(exp, context, &preExp, simCode)
         <<
         <%preExp%>
         res[<%i0%>] = <%expPart%>;
         >> ;separator="\n"
       %>
       return 0;
    }
    >>
end functionNonlinearResidual;

template functionInitialNonLinearSystems(list<SimEqSystem> initialEquations,
   list<SimEqSystem> parameterEquations, list<SimEqSystem> allEquations, SimCode simCode)
::=
  <<
  public override void FunInitialNonLinearSystem()
  {
    var nlss = NonLinearSystems;
    NLSystem nls;
    <%bodyNonLinearSystems(initialEquations, simCode)%>
    <%bodyNonLinearSystems(parameterEquations, simCode)%>
    <%bodyNonLinearSystems(allEquations, simCode)%>
  }
  >>
end functionInitialNonLinearSystems;

template bodyNonLinearSystems(list<SimEqSystem> equations, SimCode simCode)
::=
  equations |> eqn => bodyInitialNonLinearSystem(eqn, simCode)
  ;separator="\n"
end bodyNonLinearSystems;

template bodyInitialNonLinearSystem(SimEqSystem eqn, SimCode simCode)
::=
  match eqn
  case SES_MIXED(__)     then bodyInitialNonLinearSystem(cont, simCode)
  case SES_NONLINEAR(nlSystem=nls as NONLINEARSYSTEM(__)) then
    let size = listLength(nls.crefs)
    //let newtonStep = if linearTearing then '1' else '0'
    //let generatedJac = match jacobianMatrix case SOME(__) then 'functionJacNLSJac<%eq.index%>_column' case NONE() then 'NULL'
    //let initialJac = match jacobianMatrix case SOME(__) then 'initialAnalyticJacobianNLSJac<%eq.index%>' case NONE() then 'NULL'
    // et jacIndex = match jacobianMatrix case SOME(__) then 'INDEX_JAC_NLSJac<%eq.index%>' case NONE() then '-1'
    let attribs =
        nls.crefs |> cr hasindex i0 =>
            (cref2simvar(cr, simCode) |> SIMVAR(__) =>
               let nominalAssign =
                  match nominalValue
                  case SOME(exp as RCONST(__)) then  'nls.Nominals[<%i0%>] = <%exp.real%>; // '
                  case SOME(__) then error(sourceInfo(), "Unexpected expression for nominal value.")
                  else '//[<%i0%>] default nominal = 1.0 for '
               '<%nominalAssign%><%crefStr(cr, simCode)%>'
            ); separator="\n"
    <<
    nls = nlss[<%nls.indexNonLinearSystem%>] = new NLSystem(this, ResidualFun<%nls.index%>, <%size%>, <%nls.index%>);
    <%attribs%>
    >>
end bodyInitialNonLinearSystem;


template functionInitialEquations(list<SimEqSystem> initalEquations, SimCode simCode) ::=
let()= System.tmpTickReset(1)
<<
public override bool UseSymbolicInitialization { get { return true; } }
public override void FunInitialEquations()
{
  <% localRepresentationArrayDefines %>
  var rels = Relations; var relsHE = RelationsHysteresisEnabled; var relsZC = RelationsZeroCrossing;
  <%initalEquations |> eq => equation_(eq, contextSimulationDiscrete, simCode) ;separator="\n"%>
}
>>
end functionInitialEquations;

template functionUpdateBoundParameters(list<SimEqSystem> parameterEquations, SimCode simCode) ::=
let()= System.tmpTickReset(1)
<<
public override void UpdateBoundParameters()
{
  <% localRepresentationArrayDefines %>
  <% parameterEquations
     |> saeq as SES_SIMPLE_ASSIGN(__)  => equation_(saeq, contextOther, simCode) ;separator="\n"%>
  <% parameterEquations
     |> eq as SES_ALGORITHM(__) => equation_(eq, contextOther, simCode) ;separator="\n"%>
}
>>
end functionUpdateBoundParameters;

// TODO: Is the -1 thing really correct? It seems to work.
template functionCheckForDiscreteChanges(SimCode simCode) ::=
match simCode case SIMCODE(__) then
<<
public override bool CheckForDiscreteChanges()
{
  <% localRepresentationArrayDefines %>
  //var needToIterate = false;
<%/* old stuff
  //edge(H[i])
  <% helpVarInfo |> (id1, exp, id2) => match id2 case -1 then ""
     else
       'if (<%edgeHelpVar(id1)%>) EventQueue.Add(<%id2%> + NG);'
    ;separator="\n" %>
*/%>
  //if change()
  <%discreteModelVars |> var =>
     match var
     case CREF_QUAL(__)
     case CREF_IDENT(__) then
     <<
     if (<%preCref(var, simCode)%> != <%cref(var, simCode)%>) return true;
     >> ;separator="\n"
  %>
<%/* old stuff
  for (int i = 0; i < H.Length; i++) {
    //change(H[i]) ?? TODO: not sure if it can be only 1.0 or 0.0
    if (H[i] != preH[i])
      return true; //needToIterate=true;
  }
*/%>
  return false; //needToIterate;
}
>>
end functionCheckForDiscreteChanges;

template edgeHelpVar(String idx) ::=
   '(/*edge(h[<%idx%>])*/H[<%idx%>]!=0.0 && preH[<%idx%>]==0.0)'
end edgeHelpVar;

//TODO: eliminate this entirely  ??
template daeExpToReal(Exp exp, Context context, Text &preExp, SimCode simCode) ::=
    daeExp(exp, context, &preExp, simCode)
    + (match expTypeFromExp(exp) case "bool" then " ?1.0:0.0") //TODO: a HACK ?

end daeExpToReal;

// Residual equations are not handled here
template equation_(SimEqSystem eq, Context context, SimCode simCode) ::=
match eq
case SES_SIMPLE_ASSIGN(__) then
  let &preExp = buffer ""
  let expPart = daeExp(exp, context, &preExp, simCode) //was daeExpToReal
  //a hack - start values should be never on the right side of an equation,
  //specially in FunInitialEquations()
  let codeTxt =
    <<
    <%preExp%>
    <%cref(cref, simCode)%> = <%expPart%>;
    >>
  match exp
  case CALL(path=IDENT(name="$_start"), expLst={arg as CREF(__)}) then
    <<
    //### useless start value assignment ??
    //<%codeTxt%>
    //###
    >>
  else
    codeTxt
case SES_ARRAY_CALL_ASSIGN(__) then "SES_ARRAY_CALL_ASSIGN"
case SES_ALGORITHM(__) then
  (statements |> stmt =>
    algStatement(stmt, context, simCode)
  ;separator="\n")

case SES_LINEAR(lSystem=ls as LINEARSYSTEM(__)) then
  let uid = System.tmpTick()
  let size = listLength(ls.vars)
  let aname = 'A<%uid%>'
  let bname = 'b<%uid%>'
  <<
  var <%aname%> = new double[<%size%>*<%size%>]; //declare_matrix(<%aname%>, <%size%>, <%size%>);
  var <%bname%> = new double[<%size%>]; //declare_vector(<%bname%>, <%size%>);
  <%ls.simJac |> (row, col, eq as SES_RESIDUAL(__)) =>
     let &preExp = buffer ""
     let expPart = daeExpToReal(eq.exp, context, &preExp, simCode)
     '<%preExp%><%aname%>[<%row%>+<%col%>*<%size%>] = <%expPart%>; //set_matrix_elt(<%aname%>, <%row%>, <%col%>, <%size%>, <%expPart%>);'
  ;separator="\n"%>
  <%ls.beqs|> it hasindex i0 =>
     let &preExp = buffer ""
     let expPart = daeExpToReal(it, context, &preExp, simCode)
     '<%preExp%><%bname%>[<%i0%>] = <%expPart%>; //set_vector_elt(<%bname%>, <%i0%>, <%expPart%>);'
  ;separator="\n"%>
  <%if ls.partOfMixed then
      'if(SolveLinearSystemMixed(<%aname%>, <%bname%>, <%size%>, <%uid%>) != 0) found_solution = -1;'
    else
      'SolveLinearSystem(<%aname%>, <%bname%>, <%size%>, <%uid%>);'
  %>
  <%ls.vars |> SIMVAR(__) hasindex i0 => '<%cref(name, simCode)%> = <%bname%>[<%i0%>]; //get_vector_elt(<%bname%>, <%i0%>);' ;separator="\n"%>
  >>
case SES_MIXED(__) then
  let uid = System.tmpTick()
  let contEqs = equation_(cont, context, simCode)
  let numDiscVarsStr = listLength(discVars)
//  let valuesLenStr = listLength(values)
  let &preDisc = buffer "" /*BUFD*/
  let discLoc2 =
    discEqs |> discEq as SES_SIMPLE_ASSIGN(__) hasindex i0 =>
        <<
        <%cref(discEq.cref, simCode)%> = <%daeExp(discEq.exp, context, &preDisc, simCode)%>;
        double discrete_loc2_<%i0%> = <%crefToReal(discEq.cref, simCode)%>;
        >>
        //double discrete_loc2_<%i0%> = <%daeExpToReal(discEq.exp, context, &preDisc, simCode)%>;
        //<%cref(discEq.cref, simCode)%> = discrete_loc2_<%i0%>;

        ;separator="\n"
      /*
      match discEqs
    case { discEq as SES_SIMPLE_ASSIGN(__) } then
      <<
      <%cref(discEq.cref, simCode)%> = <%daeExpToReal(discEq.exp, context, &preDisc, simCode)%>;
      double discrete_loc2_0 = <%crefToReal(discEq.cref, simCode)%>;
      >>
      case discEqs then
      <<
      var discrete_loc2 = new double[<%numDiscVarsStr%>];
      <%discEqs |> discEq as SES_SIMPLE_ASSIGN(__) hasindex i0 =>
        <<
        <%cref(discEq.cref, simCode)%> = <%daeExpToReal(exp, context, &preDisc, simCode)%>;
        discrete_loc2[<%i0%>] = <%crefToReal(discEq.cref, simCode)%>;
        >>
        ;separator="\n"%>
      >>
      */
  <<
  // *** mixed_equation_system(<%numDiscVarsStr%>) ***
  { int found_solution = 0;
    <%/*
      int cur_value_indx = -1;
      var values = new double[]{<%values ;separator=", "%>};
    */%>
      var boolVar = new bool[<%numDiscVarsStr%>];
    do {
      <%
        discVars |> SIMVAR(__) hasindex i0 =>
          'double discrete_loc_<%i0%> = <%crefToReal(name, simCode)%>;'
        ;separator="\n"

       /*match discVars
        case { var as SIMVAR(__) } then
          <<
          double discrete_loc_0 = <%crefToReal(var.name, simCode)%>;
          >>
        case discVars then
          <<
          var discrete_loc = new double[<%numDiscVarsStr%>] {
            <%discVars |> SIMVAR(__) => cref(name, simCode) ;separator=",\n"%>
          };
          >> */
      %>
      {
        <%contEqs%>
      }
      <%preDisc%>
      <%discLoc2%>
      {
        // check_discrete_values(...);
        if (found_solution == -1) { /*system of equations failed*/
            found_solution = 0;
        } else {
            found_solution = 1;
            <%
              discVars |> SIMVAR(__) hasindex i0 =>
                <<
                if ( Math.Abs(discrete_loc_<%i0%> - discrete_loc2_<%i0%>) > 1e-12) found_solution = 0;
                >> ;separator="\nelse "
              /*match discVars
              case { SIMVAR(__) } then
                <<
                if ( Math.Abs(discrete_loc_0 - discrete_loc2_0) > 1e-12) {
                    found_solution = 0;
                }
                >>
              case discVars then
                <<
                for (int i=0; i < <%numDiscVarsStr%>; i++) {
                    if ( Math.Abs(discrete_loc[i] - discrete_loc2[i]) > 1e-12) {
                        found_solution = 0;
                    }
                }
                >>*/
            %>
        }
        if (found_solution == 0) { //!found_solution
             if (NextMixedBoolCombination(boolVar)){
               <% discVars |> SIMVAR(__) hasindex i0 =>
                  '<%cref(name, simCode)%> = <%preCref(name, simCode)%> != boolVar[<%i0%>];'
                 ;separator="\n"
               %>
             }
             else
               throw new Exception("Mixed system could not be solved.");
        }
      }
    } while (found_solution == 0);
  } // *** mixed_equation_system_end(<%numDiscVarsStr%>) ***
  >>

case SES_NONLINEAR(nlSystem=nls as NONLINEARSYSTEM(__)) then
  let size = listLength(nls.crefs)
  <<
  //start NLS ResidualFun<%nls.index%>, size=<%size%>);
  { var nls = NonLinearSystems[<%nls.indexNonLinearSystem%>]; var nls_X = nls.X;
    <%nls.crefs |> name hasindex i0 =>
      <<
      nls_X[<%i0%>] = <%cref(name, simCode)%>;
      >> ;separator="\n"
    %>
    nls.Solve();
    <% //*** maybe this is useless as the last call to the ResidualFun has already set the crefs ****
       nls.crefs |> name hasindex i0 =>
    '<%cref(name, simCode)%> = <%convertRealExpForCref('nls_X[<%i0%>]', name, simCode)%>;'
      ;separator="\n"
    %>
  } //end_nonlinear_system();
  >>

  /******* old impl
  <<
  //start NLS ResidualFun<%index%>, size=<%size%>);
  { var oldX = StatesOld; var oldXd = StatesDerivativesOld;  var old2X = StatesOld2; var old2Xd = StatesDerivativesOld2;
    var oldY = AlgebraicsOld; var old2Y = AlgebraicsOld2; var extrapolateTimeFactor = (TimeOld - TimeOld2) != 0.0 ? (Time - TimeOld2)/(TimeOld - TimeOld2) : 0.0;
    var nls = NonLinearSystems[<%indexNonLinearSystem%>]; var nls_x = nls.X; var nls_xold = nls.XOld;
    <%crefs |> name hasindex i0 =>
      <<
      nls_x[<%i0%>] = /*extraPolate(<%crefStr(name, simCode)%>)*/ extrapolateTimeFactor == 0.0 ? <%cref(name, simCode)%> : (<%old2Cref(name, simCode)%> + extrapolateTimeFactor * (<%oldCref(name, simCode)%> - <%old2Cref(name, simCode)%>));
      nls_xold[<%i0%>] = <%oldCref(name, simCode)%>;
      >> ;separator="\n"
    %>
    nls.Solve();
    <%nls.crefs |> name hasindex i0 =>
    '<%cref(name, simCode)%> = <%convertRealExpForCref('nls_x[<%i0%>]', name, simCode)%>;'
      ;separator="\n"
    %>
  } //end_nonlinear_system();
  >>
  old impl *********/

case SES_WHEN(__) then
  let body = whenOperators(whenStmtLst, context, simCode)
  let elseWhenConds = match elseWhen case SOME(ew) then equationElseWhen(ew, context, simCode)
  if initialCall then
    <<
    if(Initial<%if conditions then " || " + whenConditions(conditions, simCode)%>) {
      <%body%>
    }
    <%elseWhenConds%>
    >>
  else
    <<
    if( ! Initial) {
      if(<%whenConditions(conditions, simCode)%>) {
        <%body%>
      }
      <%elseWhenConds%>
    }
    >>
else
  error(sourceInfo(), "UNKNOWN_equation")

end equation_;

template equationElseWhen(SimEqSystem eq, Context context, SimCode simCode) ::=
match eq
case SES_WHEN(__) then
  let body = whenOperators(whenStmtLst, context, simCode)
  let elseWhenConds = match elseWhen case SOME(ew) then equationElseWhen(ew, context, simCode)
  <<
  else if(<%whenConditions(conditions, simCode)%>) {
    <%body%>
  }
  <%elseWhenConds%>
  >>
end equationElseWhen;

template whenOperators(list<WhenOperator> whenOps, Context context, SimCode simCode)
 "Generates re-init statement for when equation."
::=
  whenOps |> whenOp =>
    match whenOp
    case ASSIGN(__) then
      let preExp = ""
      let rightExp = daeExp(right, context, &preExp, simCode)
      <<
      <%preExp%>
      <%cref(left, simCode)%> = <%rightExp%>;
      >>
    case REINIT(__) then
      let &preExp = buffer ""
      let val = daeExp(value, contextSimulationDiscrete, &preExp, simCode)
      <<
      <%preExp%>
      <%cref(stateVar, simCode)%> = <%val%>;
      needToIterate = true;
      >>
    case TERMINATE(__) then
      let &preExp = buffer ""
      let msgVar = daeExp(message, contextSimulationDiscrete, &preExp, simCode)
      <<
      <%preExp%>
      MODELICA_TERMINATE(<%msgVar%>);
      >>
    case ASSERT(source=SOURCE(info=info)) then
      assertCommon(condition, message, info, contextSimulationDiscrete, simCode)
   ;separator="\n"
end whenOperators;

// SECTION: SIMULATION TARGET, FUNCTIONS FILE SPECIFIC TEMPLATES
// not yet implemented

// SECTION: GENERAL TEMPLATES, COMPONENT REFERENCES

template crefRepresentationArrayAndIndex(ComponentRef cr, Text &indexTxt, SimCode simCode) ::=
  match cr
    //deprecated: case CREF_IDENT(ident = "xloc") then crefStr(cr, simCode) //TODO: ??xloc
    case CREF_IDENT(ident = "time") then "Time" //no index
    case CREF_IDENT(ident = "$_lambda") then "_lambda" //no index
    //??is this a HACK (on the SimCode level) ??
    case CREF_QUAL(ident = "$PRE") then
      'pre<%crefRepresentationArrayAndIndex(componentRef, &indexTxt, simCode)%>'
    else
//    (cref2simvar(cr, simCode) |> SIMVAR(__) =>
//      let &indexTxt += index
//      representationArrayName(varKind, type_))

      match cref2simvar(cr, simCode)
//      case SIMVAR(varKind = DISCRETE()) then
//        let offset = match simCode
//          case SIMCODE(__) then
//            match modelInfo
//            case MODELINFO(__) then
//              match varInfo
//              case VARINFO(__) then numAlgVars
//        let &indexTxt += index + ' + ' + offset
//        representationArrayName(varKind, type_)
      case SIMVAR(__) then
        let &indexTxt += index
        representationArrayName(varKind, type_)

//      match cref2simvar(cr, simCode)
//      case SIMVAR(varKind = DISCRETE()) then
//        let offset =
//          match simCode
//            case (simCode = SIMCODE(modelInfo = MODELINFO(varInfo = VARINFO(__)))) then numAlgVars
//        let &indexTxt += index + ' + ' + offset
//        representationArrayName(varKind, type_)
//      case SIMVAR(__) then
//        let &indexTxt += index
//        representationArrayName(varKind, type_)
end crefRepresentationArrayAndIndex;

template representationArrayName(VarKind varKind, Type type_) ::=
  match varKind
  case VARIABLE(__)    then "Y" + representationArrayNameTypePostfix(type_)
  case STATE(__)       then "X"
  case STATE_DER(__)   then "Xd"
  case DUMMY_DER(__)   then "Y" // => algebraics
  case DUMMY_STATE(__) then "Y" // => algebraics
  case DISCRETE(__)    then 'Y<%representationArrayNameTypePostfix(type_)%>/*d*/'
  case PARAM(__)       then "P" + representationArrayNameTypePostfix(type_)
  case EXTOBJ(__)      then "EO"
  case CONST(__)       then "CONST_VAR_KIND"
  else "BAD_VARKIND"
end representationArrayName;

template representationArrayNameTypePostfix(Type type_) ::=
  match type_
  case T_INTEGER(__)
  case T_ENUMERATION(__)  then "I"
  case T_BOOL(__) then "B"
  case T_REAL(__) then ""
  case T_STRING(__) then "S"
  else "BAD_ARRAY_NAME_POSTFIX"
end representationArrayNameTypePostfix;


template representationCref(ComponentRef inCref, SimCode simCode) ::=
  let &indexTxt = buffer ""
  let arrAndIdx = crefRepresentationArrayAndIndex(inCref, &indexTxt, simCode)
  if indexTxt then
    '<%arrAndIdx%>[<% indexTxt %>]'
  else
    arrAndIdx
  //cref2simvar(inCref, simCode) |> SIMVAR(__) =>
  //  '<%representationArrayName(inCref, varKind, type_)%>[<% index %>]'
end representationCref;

template cref(ComponentRef cr, SimCode simCode) ::=
  '/*<% crefStr(cr, simCode) %>*/<% representationCref(cr, simCode) %>'

/*
  match cr
  //deprecated: case CREF_IDENT(ident = "xloc") then crefStr(cr, simCode) //TODO: ??xloc
  case CREF_IDENT(ident = "time") then "Time"
  case CREF_IDENT(ident = "$_lambda") then "_lambda"
  //$PRE is handled by representationCref ...
  // case CREF_QUAL(ident = "$PRE") then preCref(componentRef, simCode)
  else '/*<% crefStr(cr, simCode) %>*/<% representationCref(cr, simCode) %>'
  // not needed ... //the space at the start is important to avoid ambiguity with "/" operator to become "//" line comment
*/
end cref;

template preCref(ComponentRef cr, SimCode simCode) ::=
'/*pre(<%crefStr(cr, simCode)%>)*/pre<%representationCref(cr, simCode)%>'
end preCref;

template derCref(ComponentRef cr, SimCode simCode) ::=
'/*derCall!!(<% crefStr(cr, simCode) %>)*/<%representationCref(derComponentRef(cr), simCode)%>'
end derCref;

/*** not used
template oldCref(ComponentRef cr, SimCode simCode) ::=
'/*old(<%crefStr(cr, simCode)%>)*/old<%representationCref(cr, simCode)%>'
end oldCref;

template old2Cref(ComponentRef cr, SimCode simCode) ::=
'/*old2(<%crefStr(cr, simCode)%>)*/old2<%representationCref(cr, simCode)%>'
end old2Cref;
***/

//this one should be used only in InitialResidual( ..., double[][] startValues)
template startCref(ComponentRef cr, SimCode simCode) ::=
//'/*start(<%crefStr(cr, simCode)%>)*/start<%representationCref(cr, simCode)%>'

    match cref2simvar(cr, simCode)
    case sv as SIMVAR(__) then
      let fviIndex =
        match varKind
        case STATE(__)     then "State"
        case STATE_DER(__) then "StateDer"
        case VARIABLE(__)  then "Algebraic"
        case PARAM(__)     then "Parameter"
        else /*error(sourceInfo(),*/ "UNEXPECTED_variable_varKind_in_startCref_template" //)
      'startValues[(int)SimVarType.<%fviIndex%>][<%sv.index%>]'
end startCref;


//TODO: a HACK ? ... used in mixed system only
template crefToReal(ComponentRef cr, SimCode simCode) ::=
<</*(double)<% crefStr(cr, simCode)
            %>*/<% cref2simvar(cr, simCode) |> SIMVAR(__) => //representationCref(cr, simCode)
                   '<%representationArrayName(varKind, type_)%>[<% index %>]'
                   + (match type_ case T_BOOL(__)
                      then "?1.0:0.0")
                %>
>>
end crefToReal;

//TODO: a HACK ?
template daeExpRealConversionPostfix(Exp exp, SimCode simCode) ::=
  match typeof(exp)
  case T_BOOL(__) then
     "?1.0:0.0"
  //TODO: make conversion also for other expressions
end daeExpRealConversionPostfix;

//TODO: a HACK ?
template convertRealExpForCref(Text realExp, ComponentRef cr, SimCode simCode) ::=
  cref2simvar(cr, simCode) |> SIMVAR(__) =>
     match type_
     case T_BOOL(__) then '(<%realExp%>) != 0.0'
     case T_INTEGER(__) then '(int)(<%realExp%>)'
     else realExp

end convertRealExpForCref;


template contextCref(ComponentRef cr, Context context, SimCode simCode)
  "Generates code for a component reference depending on which context we're in."
::=
  match context
  case FUNCTION_CONTEXT(__) then crefStr(cr, simCode)
  else cref(cr, simCode)
end contextCref;


template crefStr(ComponentRef cr, SimCode simCode)
 "Generates the name of a variable for variable name array."
::=
  match cr
  case CREF_IDENT(__) then csharpIdent(ident) + subscriptsStr(subscriptLst, simCode)
  case CREF_QUAL(ident = "$DER") then '$der(<%crefStr(componentRef, simCode)%>)'
  case CREF_QUAL(ident = "$PRE") then '$pre(<%crefStr(componentRef, simCode)%>)'
  case CREF_QUAL(__) then '<%ident%><%subscriptsStr(subscriptLst, simCode)%>.<%crefStr(componentRef, simCode)%>'
  else "CREF_NOT_IDENT_OR_QUAL"
end crefStr;

template crefStrWithDerOnLast(ComponentRef cr, SimCode simCode)
 "Generates the name of a variable for variable name array.
  It used Dymola's name convention where the der() is on the last component."
::=
  match cr
  case CREF_IDENT(__) then csharpIdent(ident) + subscriptsStr(subscriptLst, simCode)
  case CREF_QUAL(ident = "$DER") then crefStrWithDerOnLastIsDer(componentRef, simCode)
  case CREF_QUAL(__) then '<%ident%><%subscriptsStr(subscriptLst, simCode)%>.<%crefStrWithDerOnLast(componentRef, simCode)%>'
  else "CREF_NOT_IDENT_OR_QUAL"
end crefStrWithDerOnLast;

template crefStrWithDerOnLastIsDer(ComponentRef cr, SimCode simCode)
 "Helper to crefStrWithDerOnLast."
::=
  match cr
  case CREF_IDENT(__) then 'der(<%csharpIdent(ident)%><%subscriptsStr(subscriptLst, simCode)%>)'
  case CREF_QUAL(ident = "$DER") then
    //this one should not happen, but for sure ...
    crefStrWithDerOnLastIsDer(componentRef, simCode)
  case CREF_QUAL(__) then '<%ident%><%subscriptsStr(subscriptLst, simCode)%>.<%crefStrWithDerOnLastIsDer(componentRef, simCode)%>'
  else "CREF_NOT_IDENT_OR_QUAL"
end crefStrWithDerOnLastIsDer;

template csharpIdent(String ident)
 "Generates the name of a variable for variable name array."
::=
  match ident
  case "string" then "@string"
  case "int"    then "@int"
  else ident
end csharpIdent;

template subscriptsStr(list<Subscript> subscripts, SimCode simCode)
 "Generares subscript part of the name."
::=
  if subscripts then
    '[<% subscripts |> s => match s
           case INDEX(__)
           case SLICE(__) then
              let &preExp = buffer "" //dummy ??
              daeExp(exp, contextFunction, &preExp, simCode)
           case WHOLEDIM(__) then "WHOLEDIM"
           else "UNKNOWN_SUBSCRIPT"
      ;separator="," %>]'
end subscriptsStr;



// SECTION: GENERAL TEMPLATES, PATHS

template dotPath(Path it) ::=
  match it
  case QUALIFIED(__)      then '<%name%>.<%dotPath(path)%>'
  case IDENT(__)          then name
  case FULLYQUALIFIED(__) then dotPath(path)
end dotPath;

//TODO: not 100% back-convertible e.g. Awkward_.Model vs. Awkward._Model, both -> Awkward___Model
template underscorePath(Path it) ::=
  match it
  case QUALIFIED(__)      then '<%System.stringReplace(name, "_", "__")%>_<%underscorePath(path)%>'
  case IDENT(__)          then System.stringReplace(name, "_", "__")
  case FULLYQUALIFIED(__) then underscorePath(path)
end underscorePath;


// SECTION: GENERAL TEMPLATES, FUNCTION GENERATION
// not yet implemented



// Codegen.generateStatement
template algStatement(DAE.Statement stmt, Context context, SimCode simCode) ::=
  match stmt
  case STMT_ASSIGN(exp1 = CREF(componentRef = WILD(__)), exp = e) then
    let &preExp = buffer ""
    let expPart = daeExp(e, context, &preExp, simCode)
    <<
    <%preExp%>
    <%expPart%>
    >>
  case STMT_ASSIGN(exp1 = CREF(__)) then
    let &preExp = buffer ""
    let expPart = daeExp(exp, context, &preExp, simCode)
    <<
    <%preExp%>
    <%scalarLhsCref(exp1, context, &preExp, simCode)%> = <%expPart%>;
    >>
  case STMT_ASSIGN(__) then
    let &preExp = buffer ""
    let expPart1 = daeExp(exp1, context, &preExp, simCode)
    let expPart2 = daeExp(exp, context, &preExp, simCode)
    <<
    <%preExp%>
    <%expPart1%> = <%expPart2%>;
    >>
  //works only for array name as IDENT with subscripts
  case STMT_ASSIGN_ARR(lhs=CREF(componentRef= cr as CREF_IDENT(subscriptLst=subs as (_ :: _)))) then
     let &preExp = buffer ""
     let expPart = daeExp(exp, context, &preExp, simCode)
     let spec = daeExpCrefRhsIndexSpec(subs, context, &preExp, simCode)
     <<
     <%preExp%>
     <%cr.ident%>.AssignSpec(<%spec%>, <%expPart%>.A);
     >>
  case STMT_ASSIGN_ARR(lhs=CREF(componentRef=cr)) then
     let &preExp = buffer ""
     let expPart = daeExp(exp, context, &preExp, simCode)
     <<
     <%preExp%>
     ArrayCopy(<%expPart%>.A, <%contextCref(cr, context, simCode)%>.A);
     >>
  case STMT_IF(__) then
    let &preExp = buffer ""
    let condExp = daeExp(exp, context, &preExp, simCode)
    <<
    <%preExp%>
    if (<%condExp%>) {
      <%statementLst |> it => algStatement(it, context, simCode) ;separator="\n"%>
    }
    <%elseExpr(else_, context, simCode)%>
    >>
  case STMT_FOR(range=rng as RANGE(__)) then
    let identType = expType(type_, iterIsArray) //TODO: ?? what is this for ... no array typed iterator is possible ???
    let identTypeShort = expTypeShort(type_)
    let stmtStr = (statementLst |> stmt => algStatement(stmt, context, simCode)
                   ;separator="\n")
    algStmtForRange_impl(rng, iter, identType, identTypeShort, stmtStr, context, simCode)

  case s as STMT_FOR(__) then "algStmtForGeneric_NOT_IMPLEMENTED"
    //  algStmtForGeneric(s, context, &varDecls /*BUFC*/)

  case STMT_WHILE(__)  then
    let &preExp = buffer ""
    let var = daeExp(exp, context, &preExp, simCode)
    <<
    for(;;) {
      <%preExp%>
      if (!<%var%>) break;
      <%statementLst |> stmt => algStatement(stmt, context, simCode) ;separator="\n"%>
    }
    >>

  case STMT_TUPLE_ASSIGN(__)   then "STMT_TUPLE_ASSIGN_NI"
  case STMT_ASSERT(source=SOURCE(info=info)) then
    assertCommon(cond, msg, info, context, simCode)
  case STMT_TERMINATE(__)      then "STMT_TERMINATE_NI"
  case STMT_WHEN(__)           then algStmtWhen(stmt, context, simCode)
  case STMT_BREAK(__)          then 'break; //break stmt<%\n%>'
  case STMT_FAILURE(__)        then "STMT_FAILURE_NI"
  case STMT_RETURN(__)         then "STMT_RETURN_NI"
  case STMT_NORETCALL(__)      then "STMT_NORETCALL_NI"
  case STMT_REINIT(__)         then "STMT_REINIT_NI"

  case _ then "NOT_IMPLEMENTED_ALG_STATEMENT"
 /*
  case STMT_FOR(range = rng as RANGE(__)) then
    <<
    {
      <%expTypeA(type_, boolean)%>
        _r1 = <%daeExp(rng.exp,isSimulationCode)%>,
        _r2 = <%match rng.expOption case SOME(eo) then daeExp(eo,isSimulationCode) else "(1)"%>,
        _r3 = <%daeExp(rng.range,isSimulationCode)%>,
        <%iter%>;

      for (<%iter%> = _r1; in_range_<%expTypeShort(type_)%>(<%iter%>, _r1, _r3); <%iter%> += _r2) {
        <%statementLst |> it => algStatement(it) ;separator="\n" /* ??CONTEXT(codeContext,expContext,IN_FOR_LOOP(loopContext)*/ %>
      }
    } /*end for*/
    >>
  */
end algStatement;

//TODO: in_range_integer is not correct against Modelica specification when e.g. 10:1
//TODO: optimize representation for range, so that template can know that the range is ascendent and/or bounded by a constatnt(not necessary)
template algStmtForRange_impl(Exp range, Ident iterator, String type, String shortType, Text body, Context context, SimCode simCode)
 "The implementation of algStmtForRange, which is also used by daeExpReduction."
::=
match range
case RANGE(__) then
  let iterName = iterator
  let &stopVar = buffer ""
  let &preExp = buffer ""
  let startValue = daeExp(start, context, &preExp, simCode)
  let stopValue = daeExp(stop, context, &preExp, simCode)
  match step
  case SOME(eo) then
    let &stepVar = buffer ""
    let stepValue = daeExp(eo, context, &preExp, simCode)
    <<
    <%preExp%>
    <%tempDecl(type, &stepVar)%> = <%stepValue%>; <%tempDecl(type, &stopVar)%> = <%stopValue%>;
    for(<%type%> <%iterName%> = <%startValue%>;(<%stepVar%> > 0? <%iterName%><=<%stopVar%> : <%stopVar%><=<%iterName%>); <%iterName%> += <%stepVar%>) {
      <%body%>
    }
    >>
  else //optimization for 1 step
    <<
    <%preExp%>
    <%tempDecl(type, &stopVar)%> = <%stopValue%>;
    for(<%type%> <%iterName%> = <%startValue%>; <%iterName%><=<%stopVar%>; <%iterName%> += 1) {
      <%body%>
    }
    >>
end algStmtForRange_impl;

template elseExpr(DAE.Else it, Context context, SimCode simCode) ::=
  match it
  case NOELSE(__) then ""
  case ELSEIF(__) then
    let &preExp = buffer ""
    let condExp = daeExp(exp, context, &preExp, simCode)
    <<
    else {
    <%preExp%>
    if (<%condExp%>) {
      <%statementLst |> it => algStatement(it, context, simCode) ;separator="\n"%>
    }
    <%elseExpr(else_, context, simCode)%>
    }
    >>
  case ELSE(__) then
    <<
    else {
      <%statementLst |> it => algStatement(it, context, simCode) ;separator="\n"%>
    }
    >>
end elseExpr;


template algStmtWhen(DAE.Statement when, Context context, SimCode simCode)
 "Generates a when algorithm statement."
::=
match context
case SIMULATION_CONTEXT(genDiscrete=true) then
  match when
  case STMT_WHEN(__) then
    let statements = statementLst |> stmt => algStatement(stmt, context, simCode) ;separator="\n"
    if initialCall then
      <<
      if(Initial<%if conditions then " || " + whenConditions(conditions, simCode)%>) {
        <%statements%>
      }
      <%algStatementWhenElse(elseWhen, simCode)%>
      >>
    else
      <<
      if( ! Initial) {
        if(<%whenConditions(conditions, simCode)%>) {
          <%statements%>
        }
        <%algStatementWhenElse(elseWhen, simCode)%>
      }
      >>

    /*
    <<
    if (<%whenConditions(conditions, simCode)%>) {
      <% statementLst |> stmt =>  algStatement(stmt, context, simCode)
         ;separator="\n" %>
    }
    <%algStatementWhenElse(elseWhen, simCode)%>
    >>
    */
  end match
end algStmtWhen;


template algStatementWhenElse(Option<DAE.Statement> stmt, SimCode simCode)
 "Helper to algStmtWhen."
::=
match stmt
case SOME(when as STMT_WHEN(__)) then
  <<
  else if (<%whenConditions(when.conditions, simCode)%>) {
    <% when.statementLst |> stmt =>  algStatement(stmt, contextSimulationDiscrete, simCode)
       ;separator="\n"%>
  }
  <%algStatementWhenElse(when.elseWhen, simCode)%>
  >>
end algStatementWhenElse;


template scalarLhsCref(Exp ecr, Context context, Text &preExp, SimCode simCode) ::=
match ecr
case ecr as CREF(componentRef=CREF_IDENT(subscriptLst=subs)) then
  if crefNoSub(ecr.componentRef) then
    contextCref(ecr.componentRef, context, simCode)
  else
    daeExpCrefRhs(ecr, context, &preExp, simCode)
case ecr as CREF(componentRef=CREF_QUAL(__)) then
    contextCref(ecr.componentRef, context, simCode)
else
    "ONLY_IDENT_OR_QUAL_CREF_SUPPORTED_SLHS"
end scalarLhsCref;

/*
template scalarLhsCref(ComponentRef it, SimCode simCode) ::=
  match it
  case CREF_IDENT(__) then replaceDollarWorkaround(ident)
  case CREF_QUAL(__)  then '<%ident%>.<%scalarLhsCref(componentRef)%>'
end scalarLhsCref;
*/

//TODO: this wrong for qualified integers !
template rhsCref(ComponentRef it, Type ty, SimCode simCode) ::=
  match it
  case CREF_IDENT(__) then '<%rhsCrefType(ty)%><%replaceDollarWorkaround(ident)%>'
  case CREF_QUAL(__)  then '<%rhsCrefType(ty)%><%ident%>.<%rhsCref(componentRef,ty, simCode)%>'
  case _          then "rhsCref:ERROR"
end rhsCref;

template rhsCrefType(Type it) ::=
  match it
  case T_INTEGER(__) then "(int)"
  case _      then ""
end rhsCrefType;


template replaceDollarWorkaround(String ident) ::=
  stringReplace(
    stringReplace(ident,"$DER","Der_"),
    "$", "")
end replaceDollarWorkaround;

// SECTION: GENERAL TEMPLATES, EXPRESSIONS

template daeExp(Exp inExp, Context context, Text &preExp, SimCode simCode) ::=
  match inExp
  case ICONST(__)     then integer
  case RCONST(__)     then real
  case SCONST(__)     then '"<%Util.escapeModelicaStringToCString(string)%>"'
  case BCONST(__)     then if bool then "true" else "false" //"(1)" else "(0)"
  case ENUM_LITERAL(__) then '<%index%>' //do not include ... 'ENUM:<%dotPath(name)%><%\n%>' may be embedded in a /* */ comment or VarData name
  case CREF(ty = T_FUNCTION_REFERENCE_FUNC(__)) then 'FUNC_REF_NOT_SUPPORTED(cr=<%crefStr(componentRef,simCode)%>)'
  case CREF(ty = T_COMPLEX(complexClassType = RECORD(__))) then
    match context case FUNCTION_CONTEXT(__) then
      daeExpCrefRhs(inExp, context, &preExp, simCode)
    else
      "daeExpRecordCrefRhs_NOT_YET" //daeExpRecordCrefRhs(t, cr, context, preExp, varDecls)
  case CREF(__)       then daeExpCrefRhs(inExp, context, &preExp, simCode)
  case LBINARY(__)
  case BINARY(__)     then daeExpBinary(operator, exp1, exp2, context, &preExp, simCode)
  case LUNARY(__)
  case UNARY(__)      then daeExpUnary(operator, exp, context, &preExp, simCode)
  case RELATION(__)   then daeExpRelation(inExp, context, &preExp, simCode)
  case IFEXP(__)      then daeExpIf(expCond, expThen, expElse, context, &preExp, simCode)
  case CALL(__)       then daeExpCall(inExp, context, &preExp, simCode)
  // PARTEVALFUNCTION
  case ARRAY(__)      then daeExpArray(inExp, context, &preExp, simCode)
  case MATRIX(__)     then daeExpMatrix(inExp, context, &preExp, simCode)
  case RANGE(__)      then "RANGE_NOT_IMPLEMENTED"
  case TUPLE(__)      then "TUPLE_NOT_IMPLEMENTED"
  case CAST(__)       then daeExpCast(inExp, context, &preExp, simCode)
  case ASUB(__)       then daeExpAsub(inExp, context, &preExp, simCode)
  case SIZE(__)       then daeExpSize(inExp, context, &preExp, simCode)
  case CODE(__)       then "CODE_NOT_IMPLEMENTED"
  case REDUCTION(__)  then "REDUCTION_NOT_IMPLEMENTED"
  //case VALUEBLOCK(__) then "VALUEBLOCK_NOT_IMPLEMENTED"
  case LIST(__)       then "LIST_NOT_IMPLEMENTED"
  case CONS(__)       then "CONS_NOT_IMPLEMENTED"
  case SHARED_LITERAL(__) then '_OMC_LIT<%index%>'
  // META_TUPLE
  // META_OPTION
  // METARECORDCALL
  case _          then "UNKNOWN_EXP"
end daeExp;

template daeExpSize(Exp esize, Context context, Text &preExp, SimCode simCode)
 "Generates code for a size expression."
::=
  match esize
  case SIZE(exp=CREF(__), sz=SOME(dim)) then
    let expPart = daeExp(exp, context, &preExp, simCode)
    match dim
    case ICONST(__) then
      '<%expPart%>.size<%integer%>'
    else
      '<%expPart%>.size(<%daeExp(dim, context, &preExp, simCode)%>)'
  else "size_X_NOT_IMPLEMENTED"
end daeExpSize;


template daeExpCrefRhs(Exp ecr, Context context, Text &preExp, SimCode simCode) ::=
  match ecr
  case ecr as CREF(componentRef = cr) then
    let box = daeExpCrefRhsArrayBox(ecr, context, &preExp, simCode)
    if box then
      box
    else if crefIsScalar(cr, context) then
      //match ecr.ty
      //case T_INTEGER(__)  then '(int)<%contextCref(cr, context, simCode)%>'
      //case T_BOOL(__) then
      //    match context
      //    case FUNCTION_CONTEXT(__) then contextCref(cr, context, simCode) //TODO: a hack!
      //    else '(<%contextCref(cr, context, simCode)%> !=0.0)'
      //else
      contextCref(cr, context, simCode)
    else if crefSubIsScalar(cr) then
      // The array subscript results in a scalar
      //let ety = match ecr.ty case T_ARRAY(__) then "array" else "noarray"
      let arrName = contextCref(crefStripLastSubs(cr), context, simCode)
      //let arrayType = expTypeArray(ecr.ty,listLength(crefSubs(cr)))
      //let dimsLenStr = listLength(crefSubs(cr))
      //let dimsValuesStr = (crefSubs(cr) |> INDEX(__) => daeExp(exp, context, &preExp, simCode) ;separator=", ")
      <<
      <%arrName%>[<%crefSubs(cr) |> INDEX(__) => daeExp(exp, context, &preExp, simCode)
                     ;separator=", "
                   %>]
      >>
    else
      // The array subscript denotes a slice
      let arrName = contextCref(crefStripLastSubs(cr), context, simCode)
      //let crsubs = crefSubs(cr)
      //let arrayType = expTypeArray(ecr.ty, listLength(crsubs))
      let spec = daeExpCrefRhsIndexSpec(crefSubs(cr), context, &preExp, simCode)
      '<%arrName%>.Array1Spec(<%spec%>)'
  else
    "UNKNOWN_RHS_CREF"
end daeExpCrefRhs;

template daeExpCrefRhsArrayBox(Exp exp, Context context, Text &preExp, SimCode simCode) ::=
match exp
case ecr as CREF(ty=T_ARRAY(ty=aty,dims=dims)) then
  match context
  case FUNCTION_CONTEXT(__) then ""
  else
  //case SIMULATION_CONTEXT(__) //TODO: ?? is it the same as "else than FUNCTION_CONTEXT" ?
  //case OTHER_CONTEXT(__)      then
    // For context simulation and other array variables must be boxed into a real_array
    // object since they are represented only in a double array.
    let &tmpArr = buffer ""
    let arrType = expTypeArray(aty, listLength(dims))
    let dimsValuesStr = (dims |> dim => dimension(dim) ;separator=", ")
    let &indexTxt = buffer ""
    let reprArray = crefRepresentationArrayAndIndex(ecr.componentRef, &indexTxt, simCode)
    let &preExp +=
        //match cref2simvar(ecr.componentRef, simCode) case SIMVAR(__) then
         '<%tempDecl("var", &tmpArr)%> = new <%arrType%>(<%dimsValuesStr%>, <%indexTxt%>-1, /*<%crefStr(ecr.componentRef, simCode)%>*/<%reprArray%>);<%\n%>'
    tmpArr
end daeExpCrefRhsArrayBox;


template daeExpCrefRhsIndexSpec(list<Subscript> subs, Context context, Text &preExp, SimCode simCode) ::=
  subs |> sub => match sub
       case INDEX(__) then
         'new int[]{<%daeExp(exp, context, &preExp, simCode)%>-1}'
       case WHOLEDIM(__) then
         "null"
       case SLICE(__) then
         match exp
         case ARRAY(scalar=true, ty=T_INTEGER(__)) then
           <<
           new int[]{<% array |> e => '(<%expTypeFromExp(e)%>)<%daeExp(e, context, &preExp, simCode)%>-1'
                        ;separator=","
                      %>
           >>
         else "UKNOWN_SLICE_EXP"
  ;separator=", "
end daeExpCrefRhsIndexSpec;


template daeExpAsub(Exp aexp, Context context, Text &preExp, SimCode simCode)
 "Generates code for an asub expression."
::=
  match aexp
  case ASUB(exp=RANGE(ty=t), sub={idx}) then
    'ASUB_EASY_CASE'
  case ASUB(exp=ASUB(
              exp=ASUB(
                exp=ASUB(exp=e, sub={ICONST(integer=i)}),
                sub={ICONST(integer=j)}),
              sub={ICONST(integer=k)}),
            sub={ICONST(integer=l)}) then
    'ASUB_4D'
  case ASUB(exp=ASUB(
              exp=ASUB(exp=e, sub={ICONST(integer=i)}),
              sub={ICONST(integer=j)}),
            sub={ICONST(integer=k)}) then
    'ASUB_3D'
  case ASUB(exp=ASUB(exp=e, sub={ICONST(integer=i)}),
            sub={ICONST(integer=j)}) then
    '<%daeExp(e, context, &preExp, simCode)%>[<%i%>,<%j%>]'
  case ASUB(exp=e, sub={ICONST(integer=i)}) then
    'ASUB_ARRAY'
  case ASUB(exp=ecr as CREF(componentRef = cr), sub = subs) then
    match context case FUNCTION_CONTEXT(__) then
      daeExpCrefRhs(buildCrefExpFromAsub(ecr, subs), context, &preExp, simCode)
    else //SIMULATION or OTHER contexts
      match ecr.ty
      case T_ARRAY(ty = T_REAL(__), dims = dims) then //daeExpCrefRhsArrayBox
        let &constSum = buffer ""
        let arrayRepr = crefRepresentationArrayAndIndex(cr, &constSum, simCode)
        let baseSub = asubSubsripts(dims, subs, &constSum, context, &preExp, simCode)
        '/*<%crefStr(cr, simCode)%>[]*/<%arrayRepr%>[<%constSum%><%baseSub%>]'
      /*
        <<
        /*<% crefStr(cr, simCode)
           %>[!]*/<% cref2simvar(cr, simCode) |> SIMVAR(__) =>
                     let &constSum = buffer index
                     let baseSub = asubSubsripts(dims, subs, &constSum, context, &preExp, simCode)
                     <<
                     <%representationArrayName(varKind,type_)%>[<%constSum%><%baseSub%>]
                     >>
                  %>
        >>
      */
      else "ASUB_SIMULATION_OTHER_ERROR"


    //cref(ecr.componentRef, simCode)
    /*
    let arrName = daeExpCrefRhs(buildCrefExpFromAsub(ecr, subs), context, &preExp, simCode)
    match context case SIMULATION_CONTEXT(__) then
      arrayScalarRhs(ecr.ty, subs, arrName, context, &preExp, simCode)
    else
      arrName
     */
  case ASUB(exp=exp as ARRAY(scalar=true), sub={idx}) then
      "ASUB_FAST_ONE"
  case ASUB(exp=e, sub=indexes) then
    <<
    <%daeExp(e, context, &preExp, simCode)
    %>[<%indexes |> index => '<%daeExp(index, context, &preExp, simCode)%>' ;separator=", "%>]
    >>
  else
    'OTHER_ASUB__ERROR'
end daeExpAsub;

template asubSubsripts(list<Dimension> dims, list<Exp> subs, Text &constSum,
                       Context context, Text &preExp, SimCode simCode)
 "Helper to daeExpAsub."
::=
  match subs case s :: subsRest then
      match dims  case _ :: dimsRest then
          let subStr = daeExp(s, context, &preExp, simCode)
          if dimsRest then //not last
             let ds = dimsRest |> dim => dimension(dim) ;separator="*"
             //if ds then //TODO: assuming every dimension is SOME, is it true ??
             let &constSum += '-(<%ds%>)' //-1 * ds
             '+<%subStr%>*(<%ds%>)<% asubSubsripts(dimsRest, subsRest, &constSum, context, &preExp, simCode) %>'
          else //the last sub, add it to constSum (better optimization on compilation)
             let &constSum += '-1 + <% subStr %>'
             ""
      else "ERROR_asubSubsripts_not_enough_dims"
end asubSubsripts;

// TODO: Optimize as in Codegen
// TODO: Use this function in other places where almost the same thing is hard
//       coded
// not used yet
template arrayScalarRhs(Type ty, list<Exp> subs, Text arrName, Context context,
                        Text &preExp, SimCode simCode)
 "Helper to daeExpAsub."
::=
  let arrayType = expTypeArray(ty, listLength(subs))
  let dimsValuesStr = (subs |> exp => daeExp(exp, context, &preExp, simCode)
                       ;separator=", ")
  <<
  (*ASR<%arrayType%>_element_addr(&<%arrName%>, <%/*dimsLen*/%>, <%dimsValuesStr%>))
  >>
end arrayScalarRhs;


template daeExpBinary(Operator it, Exp exp1, Exp exp2, Context context, Text &preExp, SimCode simCode) ::=
  let e1 = daeExp(exp1, context, &preExp, simCode)
  let e2 = daeExp(exp2, context, &preExp, simCode)
  match it
  case ADD(__) then '(<%e1%> + <%e2%>)'
  //TODO FIX: bad type of SUB operator when on bools; the following does not work
  //case SUB(ty = T_BOOL(__)) then '((<%e1%>?1.0:0.0) - (<%e2%>?1.0:0.0))' //in InitialResidual() ...
  case SUB(__) then
      //match expTypeFromExp(exp1) //TODO FIX: bool typed CREF returns "double"; the following is not working, too
      //case "bool" then '((<%e1%>?1.0:0.0) - (<%e2%>?1.0:0.0))' //in InitialResidual() ...
      //HACK!!
      let boolConv = daeExpRealConversionPostfix(exp1, simCode)
      '((<%e1%><%boolConv%>) - (<%e2%><%boolConv%>))'
  case MUL(__) then '(<%e1%> * <%e2%>)'
  case DIV(__) then '(<%e1%> / <%e2%>)'
  case POW(__) then 'Math.Pow(<%e1%>, <%e2%>)'
  case AND(__) then '(<%e1%> && <%e2%>)'
  case OR(__)  then '(<%e1%> || <%e2%>)'
  case _   then "daeExpBinary:ERR"
end daeExpBinary;


template daeExpUnary(Operator it, Exp exp, Context context, Text &preExp, SimCode simCode) ::=
  let e = daeExp(exp, context, &preExp, simCode)
  match it
  case UMINUS(__)     then '(-<%e%>)'
  case NOT(__)        then '(!<%e%>)'
  case UMINUS_ARR(__) then "UMINUS_ARR_NOT_IMPLEMENTED"
  case _          then "daeExpUnary:ERR"

end daeExpUnary;


template daeExpRelation(Exp inExp, Context context, Text &preExp, SimCode simCode) ::=
  match inExp
  case RELATION(__) then
    //let &e1e2preExp = buffer ""
    let simrel = daeExpSimRelation(inExp, context, /*e1, e2, e1e2preExp,*/ &preExp, simCode)
    if  simrel then simrel
    else //non-SIMULATION context or precise equality
      //let &preExp += e1e2preExp //if daeExpSimRelation() did not generated anything
      let e1 = daeExp(exp1, context, &preExp, simCode)
      let e2 = daeExp(exp2, context, &preExp, simCode)
      match operator
      case LESS(ty = T_BOOL(__))        then '(!<%e1%> && <%e2%>)'
      case LESS(ty = T_STRING(__))      then "# string comparison not supported\n"
      case LESS(ty = T_INTEGER(__))
      case LESS(ty = T_REAL(__))
      case LESS(ty = T_ENUMERATION(__)) then '(<%e1%> < <%e2%>)'

      case GREATER(ty = T_BOOL(__))     then '(<%e1%> && !<%e2%>)'
      case GREATER(ty = T_STRING(__))   then "# string comparison not supported\n"
      case GREATER(ty = T_INTEGER(__))
      case GREATER(ty = T_REAL(__))
      case GREATER(ty = T_ENUMERATION(__))   then '(<%e1%> > <%e2%>)'

      case LESSEQ(ty = T_BOOL(__))      then '(!<%e1%> || <%e2%>)'
      case LESSEQ(ty = T_STRING(__))    then "# string comparison not supported\n"
      case LESSEQ(ty = T_INTEGER(__))
      case LESSEQ(ty = T_REAL(__))
      case LESSEQ(ty = T_ENUMERATION(__))    then '(<%e1%> <= <%e2%>)'

      case GREATEREQ(ty = T_BOOL(__))   then '(<%e1%> || !<%e2%>)'
      case GREATEREQ(ty = T_STRING(__)) then "# string comparison not supported\n"
      case GREATEREQ(ty = T_INTEGER(__))
      case GREATEREQ(ty = T_REAL(__))
      case GREATEREQ(ty = T_ENUMERATION(__))  then '(<%e1%> >= <%e2%>)'

      case EQUAL(ty = T_BOOL(__))       //***then '((!<%e1%> && !<%e2%>) || (<%e1%> && <%e2%>))'
      case EQUAL(ty = T_STRING(__))
      case EQUAL(ty = T_INTEGER(__))
      case EQUAL(ty = T_REAL(__))
      case EQUAL(ty = T_ENUMERATION(__))    then '(<%e1%> == <%e2%>)'

      case NEQUAL(ty = T_BOOL(__))      //***then '((!<%e1%> && <%e2%>) || (<%e1%> && !<%e2%>))'
      case NEQUAL(ty = T_STRING(__))
      case NEQUAL(ty = T_INTEGER(__))
      case NEQUAL(ty = T_REAL(__))
      case NEQUAL(ty = T_ENUMERATION(__))    then '(<%e1%> != <%e2%>)'
      case _                         then "daeExpRelation:ERR"
end daeExpRelation;

template zeroCrossingValue(Exp inRelationExp, Text e1, Text e2) ::=
 match inRelationExp
 case rel as RELATION(__) then
   //isReal = isReal(exp1) || isReal(exp2)
   //let isReal = if isRealType(typeof(rel.exp1)) then true else (if isRealType(typeof(rel.exp2)) then true)
   let isReal = if boolOr(isRealType(typeof(rel.exp1)), isRealType(typeof(rel.exp2))) then true
   match operator
   case LESS(__)  case GREATEREQ(__)
    // e1 < e2 || e1 >= e2
     then
     if isReal then
       <<relsHE[<%rel.index%>] ? (<%e2%> - <%e1%> - (HystRelTol * Math.Abs(<%e1%>) + HystAbsTol))
         :  (<%e2%> - <%e1%>)>>
     else
        '(<%e2%> - <%e1%>)'
   case LESSEQ(__)  case GREATER(__)
    // e1 <= e2 ||  e1 > e2
     then
     if isReal then
       <<relsHE[<%rel.index%>] ? (<%e1%> - <%e2%> - (HystRelTol * Math.Abs(<%e2%>) + HystAbsTol))
         :  (<%e1%> - <%e2%>)>>
     else
        '(<%e1%> - <%e2%>)'

   case EQUAL(__)  case NEQUAL(__)
     //e1 == e2 ||  e1 != e2  !!! BE AWARE !!! should not be allowed
     then
     if isReal then
       <</*!!! BE AWARE !!! should not be allowed*/relsHE[<%rel.index%>] ? (Math.Abs(<%e2%> - <%e1%>) - (HystRelTol * Math.Abs(<%e1%>) + HystAbsTol))
         :  Math.Abs(<%e2%> - <%e1%>)>>
     else
        'Math.Abs(<%e2%> - <%e1%>)'

   else error(sourceInfo(), "zeroCrossingValue unexpected operator")
end zeroCrossingValue;

template zeroCrossingRelationOperator(Exp inRelationExp) ::=
 match inRelationExp
 case RELATION(__) then
   match operator
   case LESS(__) case GREATER(__) case NEQUAL(__)
     then
      " > "
   case LESSEQ(__) case GREATEREQ(__) case EQUAL(__)
     then
      " <= "
   else error(sourceInfo(), "zeroCrossingRelationOperator unexpected operator")
end zeroCrossingRelationOperator;

template relationWithZeroCrossing(Exp inRelationExp, Text e1, Text e2) ::=
 match inRelationExp
 case RELATION(__) then
   //relation <%ExpressionDump.printExpStr(inRelationExp)%>
   <<
   relsZC[<%index%>] = <%zeroCrossingValue(inRelationExp, e1, e2)%>;
   rels[<%index%>] = relsZC[<%index%>]<%zeroCrossingRelationOperator(inRelationExp)%>0.0;
   >>
end relationWithZeroCrossing;

template daeExpSimRelation(Exp inRelationExp, Context context, /*Text e1, Text e2, Text e1e2preExp,*/ Text &preExp, SimCode simCode) ::=
match context
case SIMULATION_CONTEXT(__)
case ALGLOOP_CONTEXT(__)
case ZEROCROSSINGS_CONTEXT(__)
then
  match inRelationExp
  case rel as RELATION(__) then
     let &preExp +=
        <<
        //relation[<%rel.index%>]:  <%ExpressionDump.printExpStr(inRelationExp)%><%\n%>
        >>
     match rel.index
       case -1 then
         //maybe, we should just return "" to let it handle by daeExpRelation(),
         //but for now, just to see, where those (rel.index=-1) are
         //no, we do so, because we see the relation via &preExp
         ""
       else
       match context
       case sim as SIMULATION_CONTEXT(__) then
         if sim.genDiscrete then
           //ZC with hysteresis
           let e1 = daeExp(rel.exp1, context, &preExp, simCode)
           let e2 = daeExp(rel.exp2, context, &preExp, simCode)
           //let &preExp += e1e2preExp
           let &preExp +=
             relationWithZeroCrossing(inRelationExp, e1, e2) + "\n"
           'rels[<%rel.index%>]'
         else
           //continuous uses preRels only ... funODE, non NLS code
           'preRels[<%rel.index%>]'
       case ALGLOOP_CONTEXT(__) then
         let &e1e2preExp = buffer ""
         let e1 = daeExp(rel.exp1, context, &e1e2preExp, simCode)
         let e2 = daeExp(rel.exp2, context, &e1e2preExp, simCode)
         if genInitialisation then
           let &preExp += e1e2preExp
           let &preExp +=
             relationWithZeroCrossing(inRelationExp, e1, e2) + "\n"
           '(IsContinuousEvaluation ? preRels[<%rel.index%>] : rels[<%rel.index%>])'
         else
           //all the DAE/ODE NLSs
           let &res = buffer ""
           let &preExp +=
             <<
             <%tempDecl("bool", res)%>;
             if(IsDiscreteEvaluation) {
               <%e1e2preExp%>
               <%relationWithZeroCrossing(inRelationExp, e1, e2)%>
               <%res%> = IsContinuousEvaluation ? preRels[<%rel.index%>] : rels[<%rel.index%>];
             } else
               <%res%> = preRels[<%rel.index%>];<%\n%>
             >>
           res
       case ZEROCROSSINGS_CONTEXT(__) then
         //zero crossing context only returns the value and do not set anything
         let e1 = daeExp(rel.exp1, context, &preExp, simCode)
         let e2 = daeExp(rel.exp2, context, &preExp, simCode)
         let &res = buffer ""
         let &preExp +=
           <<
           <%tempDecl("bool", res)%> = (<%zeroCrossingValue(rel, e1, e2)%>)<%zeroCrossingRelationOperator(inRelationExp)%>0.0;<%\n%>
           >>
         res

   /**** old
       case sim as SIMULATION_CONTEXT(__) then
         if sim.genDiscrete then
           if sim.isInitial then
             //no ZC with hysteresis
             let &res = buffer ""
             let &preExp +=
              <<
              <%tempDecl("bool", res)%> = <%e1%><%op%><%e2%>;//no ZC+H
              rels[<%rel.index%>] = <%res%>;<%\n%>
              >>
             res
           else
             //ZC with hysteresis
             let &res = buffer ""
             let &preExp +=
              <<
              <%tempDecl("bool", res)%> = <%e1%><%op%><%e2%>; //ZC+H
              rels[<%rel.index%>] = <%res%>;<%\n%>
              >>
             res
         else
           //continuous uses preRels only ... funODE, non NLS code
           'preRels[<%rel.index%>]'

       case ALGLOOP_CONTEXT(__) then
         if genInitialisation then
           let &res = buffer ""
           let &preExp +=
             <<
             <%tempDecl("bool", res)%>;
             if(IsContinuousEvaluation) {
               <%res%> = preRels[<%rel.index%>];
               rels[<%rel.index%>] = <%e1%><%op%><%e2%>;//no ZC+H
             } else
               rels[<%rel.index%>] = <%res%> = <%e1%><%op%><%e2%>;//no ZC+H<%\n%>
             >>
           res
         else
           //all the DAE/ODE NLSs
           let &res = buffer ""
           let &preExp +=
             <<
             <%tempDecl("bool", res)%>;
             if(IsDiscreteEvaluation) {
               if(IsContinuousEvaluation) {
                 <%res%> = preRels[<%rel.index%>];
                 rels[<%rel.index%>] = <%e1%><%op%><%e2%>;//ZC+H
               } else
                 rels[<%rel.index%>] = <%res%> = <%e1%><%op%><%e2%>;//ZC+H
             } else
               <%res%> = preRels[<%rel.index%>];<%\n%>
             >>
           res
    ****/
/*
case sim as SIMULATION_CONTEXT(__) then
  match inExp
  case RELATION(__) then
     let op =
       match operator
       case LESS(__)      then " < "
       case LESSEQ(__)    then " <= "
       case GREATER(__)   then " > "
       case GREATEREQ(__) then " >= "
       case EQUAL(__)     then " == "
       case NEQUAL(__)    then " != "
       else error(sourceInfo(), "daeExpSimRelation unexpected operator")

     match index
       case -1 then
         '(<%e1%><%op%><%e2%>)/*-1*/ '
       else if sim.genDiscrete then
               let &res = buffer ""
               let &preExp +=
                 <<
                 <%tempDecl("bool", res)%> = <%e1%><%op%><%e2%>;
                 rels[<%index%>] = <%res%>;<%\n%>
                 >>
               res
            else
               'preRels[<%index%>]'
*/
     /*
     let &preExp +=
       match index
       case -1 then
         '<%tempDecl("bool", res)%> = <%e1%><%op%><%e2%>;<%\n%>'
       else if sim.genDiscrete then
               <<
               <%tempDecl("bool", res)%> = <%e1%><%op%><%e2%>;
               rels[<%index%>] = <%res%>;<%\n%>
               >>
            else
               '<%tempDecl("bool", res)%> = rels[<%index%>];<%\n%>'
     res
     */
       /*
       match operator
       case LESS(__)      then SimRelationSimple(inExp, e1, e2, " < ", &preExp)
       case LESSEQ(__)    then SimRelationEqual(e1, e2, " <= ", &preExp)
       case GREATER(__)   then SimRelationSimple(e1, e2, " > ", &preExp)
       case GREATEREQ(__) then SimRelationEqual(e1, e2, " >= ", &preExp)
       end match
       */
end daeExpSimRelation;

/*

    template simpleRelationOperator(Exp inRelationExp) ::=
     match inRelationExp
     case RELATION(__) then
       match operator
       case LESS(__)      then " < "
       case LESSEQ(__)    then " <= "
       case GREATER(__)   then " > "
       case GREATEREQ(__) then " >= "
       case EQUAL(__)     then " == /*!!! BE AWARE !!! should not be allowed*/ "
       case NEQUAL(__)    then " != /*!!! BE AWARE !!! should not be allowed*/ "
       else error(sourceInfo(), "simpleRelationOperator unexpected operator")
    end simpleRelationOperator;

    template SimRelationSimple(Text e1, Text e2, String op, Text &preExp) ::=
      let &res = buffer ""
      let &preExp +=
        <<
        // RELATION( <%e1%><%op%> <%e2%> ) macro expansion

        >>
        //<%tempDecl("bool", res)%> = <%e1%><%op%> <%e2%>; if (!<%res%> && isInUpdate && (<%e1%><%op%>= <%e2%>)) { SwapOldVars(); double res1 = <%e1%> - <%e2%>;  SwapOldVars12(); <%res%> = res1<%op%>= (<%e1%> - <%e2%>); SwapOldVars2(); }<%\n%>
      res
    end SimRelationSimple;


    template SimRelationEqual(Text e1, Text e2, String op, Text &preExp) ::=
      let &res = buffer ""
      let &preExp +=
        <<
        // RELATION( <%e1%><%op%>= <%e2%> ) macro expansion
        <%tempDecl("bool", res)%>;  if (isInUpdate) { <%res%> = <%e1%><%op%> <%e2%>;  if(!<%res%> && (<%e1%><%op%>= <%e2%>)) {  SwapOldVars(); double res1 = <%e1%> - <%e2%>;  SwapOldVars12(); <%res%> = res1<%op%>= (<%e1%> - <%e2%>); SwapOldVars2(); }  } else <%res%> = <%e1%><%op%>= <%e2%>;<%\n%>
        >>
      res
    end SimRelationEqual;
*/

template daeExpIf(Exp cond, Exp then_, Exp else_, Context context, Text &preExp, SimCode simCode) ::=
  let condExp = daeExp(cond, context, &preExp, simCode)
  let &resVar = buffer ""
  let &preExpThen = buffer ""
  let eThen = daeExp(then_, context, &preExpThen, simCode)
  let &preExpElse = buffer ""
  let eElse = daeExp(else_, context, &preExpElse, simCode)
  let &preExp +=
  <<
  <%tempDecl(expTypeFromExpArrayIf(then_), resVar)%>;
  if (<%condExp%><%/*if expTypeFromExp(cond) is "bool" then " != 0.0"*/%>) { //cond type is <%expTypeFromExp(cond)%>
    <%preExpThen%>
    <%resVar%> = <%eThen%>;
  } else {
    <%preExpElse%>
    <%resVar%> = <%eElse%>;
  }<%\n%>
  >>
  resVar

/*<<
(<%daeExp(cond, context, &preExp, simCode)
  %> ? <%daeExp(then_, context, &preExp, simCode)%> : <%daeExp(else_, context, &preExp, simCode)%>)
>>*/
end daeExpIf;


template daeExpCall(Exp inExp, Context context, Text &preExp, SimCode simCode) ::=
  match inExp
  // special builtins
  case CALL(path=IDENT(name="DIVISION"),
            expLst={e1, e2}) then

    let var1 = daeExp(e1, context, &preExp, simCode)
    let var2 = daeExp(e2, context, &preExp, simCode)

    match e2
    case RCONST(__) then
         //match rr case 0.0 then 'DivBy0(<%var1%>,0.0,"<%msg%>")'
         //else
         '<%var1%> / <%var2%>'
    case _ then
         let msg = Util.escapeModelicaStringToCString(printExpStr(e2))
         let &tmpVar2 = buffer ""
         let &preExp +=
            '<%tempDecl("var", &tmpVar2)%> = <%var2%>; if (<%tmpVar2%> == 0.0) throw new DivideByZeroException("<%msg%>");<%\n%>'
         '<%var1%> / <%tmpVar2%>'
    end match

  case CALL(path=IDENT(name="DIVISION"),
    expLst={e1, e2, e3}) then
    <<
      DEPRECATED DIVISION STYLE
    >>



  case CALL(path=IDENT(name="der"), expLst={arg as CREF(__)}) then
    derCref(arg.componentRef, simCode)

  case CALL(path=IDENT(name="pre"), expLst={arg as CREF(__)}) then
    //let retType = expType(arg.ty)
    <<
    <%match arg.ty case T_INTEGER(__) then "(int)"
    %><% preCref(arg.componentRef, simCode) %>
    >>

  case CALL(path=IDENT(name="integer"), expLst={toBeCasted})
  case CALL(path=IDENT(name="Integer"), expLst={toBeCasted}) then
    let castedVar = daeExp(toBeCasted, context, &preExp, simCode)
    '((int)<%castedVar%>)'

  case CALL(path=IDENT(name="$_start"), expLst={arg as CREF(__)}) then
    startCref(arg.componentRef, simCode)

  //'(/*edge(h[<%idx%>])*/H[<%idx%>]!=0.0 && preH[<%idx%>]==0.0)'
  case CALL(path=IDENT(name="edge"), expLst={arg as CREF(__)}) then
    <<
    (/*edge*/<% cref(arg.componentRef, simCode) %> && !(<% preCref(arg.componentRef, simCode) %>))
    >>
  case CALL(path=IDENT(name="change"), expLst={arg as CREF(__)}) then
    <<
    (/*change*/<% cref(arg.componentRef, simCode) %> != <% preCref(arg.componentRef, simCode) %>)
    >>

  case CALL(path=IDENT(name="max"), expLst={e1,e2}) then
    'Math.Max(<%daeExp(e1, context, &preExp, simCode)%>,<%daeExp(e2, context, &preExp, simCode)%>)'

  case CALL(path=IDENT(name="min"), expLst={e1,e2}) then
    'Math.Min(<%daeExp(e1, context, &preExp, simCode)%>,<%daeExp(e2, context, &preExp, simCode)%>)'

  case CALL(path=IDENT(name="mod"), expLst={e1,e2}, attr=attr as CALL_ATTR(__)) then
    let var1 = daeExp(e1, context, &preExp, simCode)
    let var2 = daeExp(e2, context, &preExp, simCode)
    'Mod_<%expTypeShort(attr.ty)%>(<%var1%>,<%var2%>)'

  //TODO: ignoring the format yet
  case CALL(path=IDENT(name="String"), expLst={s, format}) then
    let str = daeExp(s, context, &preExp, simCode)
    '(<%str%>).ToString()'

  case CALL(path=IDENT(name="abs"), expLst={e1}, attr=CALL_ATTR(ty = T_INTEGER(__))) then
    let var1 = daeExp(e1, context, &preExp, simCode)
    'Abs_int(<%var1%>)'

  case CALL(path=IDENT(name="abs"), expLst={s1}) then
    'Math.Abs(<%daeExp(s1, context, &preExp, simCode)%>)'

  case CALL(path=IDENT(name="log"), expLst={s1}) then
    'Math.Log(<%daeExp(s1, context, &preExp, simCode)%>)'

  case CALL(path=IDENT(name="log10"), expLst={s1}) then
    'Math.Log10(<%daeExp(s1, context, &preExp, simCode)%>)'

  case CALL(path=IDENT(name="exp"), expLst={s1}) then
    'Math.Exp(<%daeExp(s1, context, &preExp, simCode)%>)'

  case CALL(path=IDENT(name="sin"), expLst={s1}) then
    'Math.Sin(<%daeExp(s1, context, &preExp, simCode)%>)'

  case CALL(path=IDENT(name="cos"), expLst={s1}) then
    'Math.Cos(<%daeExp(s1, context, &preExp, simCode)%>)'

  case CALL(path=IDENT(name="sqrt"), expLst={s1}) then
    'Math.Sqrt(<%daeExp(s1, context, &preExp, simCode)%>)'

  case CALL(path=IDENT(name="tanh"), expLst={s1}) then
    'Math.Tanh(<%daeExp(s1, context, &preExp, simCode)%>)'

  case CALL(path=IDENT(name="noEvent"), expLst={s1}) then
    '(/*noEvent*/<%daeExp(s1, /*context*/ contextOther, &preExp, simCode)%>)'

  case CALL(path=IDENT(name="initial"), expLst={}) then
    'Initial'

  /* Begin code generation of event triggering math functions */

  case CALL(path=IDENT(name="integer"), expLst={valExp,index}) then
    let &preExp += '//event trigger function: <%ExpressionDump.printExpStr(inExp)%><%\n%>'
    let constIndex = daeExp(index, context, &preExp, simCode)
    match context
    case SIMULATION_CONTEXT(genDiscrete=true)
    case ALGLOOP_CONTEXT(genInitialisation=true)
    case ZEROCROSSINGS_CONTEXT(__)
      then
      let val = daeExp(valExp, context, &preExp, simCode)
      '((int) (mathEVPre[<%constIndex%>] = Math.Floor(<%val%>)))'
    case SIMULATION_CONTEXT(genDiscrete=false) then
      '(int) mathEVPre[<%constIndex%>]'
    case ALGLOOP_CONTEXT(genInitialisation=false) then
      let &res = buffer ""
      let &preExp +=
        let &preExpVal = buffer ""
        let val = daeExp(valExp, context, &preExpVal, simCode)
        <<
        <%tempDecl("int", &res)%>;
        if(IsDiscreteEvaluation && !IsContinuousEvaluation) {
          <%preExpVal%>
          <%res%> = (int) (mathEVPre[<%constIndex%>] = Math.Floor(<%val%>));
        } else {
          <%res%> = (int) mathEVPre[<%constIndex%>];
        }<%\n%>
        >>
      res
    else error(sourceInfo(), 'Unexpected context for: <%ExpressionDump.printExpStr(inExp)%>')

  case CALL(path=IDENT(name="floor"), expLst={valExp,index}, attr=CALL_ATTR(__)) then
    let &preExp += '//event trigger function: <%ExpressionDump.printExpStr(inExp)%><%\n%>'
    let constIndex = daeExp(index, context, &preExp, simCode)
    let retType =
      match attr.ty
      case T_REAL(__)     then ""
      case T_INTEGER(__)  then "(int)"
      else error(sourceInfo(), 'Unexpected return type "<%expTypeShort(attr.ty)%>"  for floor().')
    retType
    + ( match context
        case SIMULATION_CONTEXT(genDiscrete=true)
        case ALGLOOP_CONTEXT(genInitialisation=true)
        case ZEROCROSSINGS_CONTEXT(__)
          then
          let val = daeExp(valExp, context, &preExp, simCode)
          '(mathEVPre[<%constIndex%>] = Math.Floor(<%val%>))'
        case SIMULATION_CONTEXT(genDiscrete=false) then
          'mathEVPre[<%constIndex%>]'
        case ALGLOOP_CONTEXT(genInitialisation=false) then
          let &res = buffer ""
          let &preExp +=
            let &preExpVal = buffer ""
            let val = daeExp(valExp, context, &preExpVal, simCode)
            <<
            <%tempDecl("double", &res)%>;
            if(IsDiscreteEvaluation && !IsContinuousEvaluation) {
              <%preExpVal%>
              <%res%> = (mathEVPre[<%constIndex%>] = Math.Floor(<%val%>));
            } else {
              <%res%> = mathEVPre[<%constIndex%>];
            }<%\n%>
            >>
          res
        else error(sourceInfo(), 'Unexpected context for: <%ExpressionDump.printExpStr(inExp)%>')
      )

  case CALL(path=IDENT(name="ceil"), expLst={valExp,index}, attr=CALL_ATTR(__)) then
    let &preExp += '//event trigger function: <%ExpressionDump.printExpStr(inExp)%><%\n%>'
    let constIndex = daeExp(index, context, &preExp, simCode)
    let retType =
      match attr.ty
      case T_REAL(__)     then ""
      case T_INTEGER(__)  then "(int)"
      else error(sourceInfo(), 'Unexpected return type "<%expTypeShort(attr.ty)%>" for ceil().')
    retType
    + ( match context
        case SIMULATION_CONTEXT(genDiscrete=true)
        case ALGLOOP_CONTEXT(genInitialisation=true)
        case ZEROCROSSINGS_CONTEXT(__)
          then
          let val = daeExp(valExp, context, &preExp, simCode)
          '(mathEVPre[<%constIndex%>] = Math.Ceiling(<%val%>))'
        case SIMULATION_CONTEXT(genDiscrete=false) then
          'mathEVPre[<%constIndex%>]'
        case ALGLOOP_CONTEXT(genInitialisation=false) then
          let &res = buffer ""
          let &preExp +=
            let &preExpVal = buffer ""
            let val = daeExp(valExp, context, &preExpVal, simCode)
            <<
            <%tempDecl("double", &res)%>;
            if(IsDiscreteEvaluation && !IsContinuousEvaluation) {
              <%preExpVal%>
              <%res%> = (mathEVPre[<%constIndex%>] = Math.Ceiling(<%val%>));
            } else {
              <%res%> = mathEVPre[<%constIndex%>];
            }<%\n%>
            >>
          res
        else error(sourceInfo(), 'Unexpected context for: <%ExpressionDump.printExpStr(inExp)%>')
      )

  case CALL(path=IDENT(name="div"), expLst={e1,e2, index}, attr=CALL_ATTR(__)) then
    let &preExp += '//event trigger function: <%ExpressionDump.printExpStr(inExp)%><%\n%>'
    let constIndex = daeExp(index, context, &preExp, simCode)
    let stype = expTypeShort(attr.ty)
    match context
    case SIMULATION_CONTEXT(genDiscrete=true)
    case ALGLOOP_CONTEXT(genInitialisation=true)
    case ZEROCROSSINGS_CONTEXT(__)
      then
      let val1 = daeExp(e1, context, &preExp, simCode)
      let val2 = daeExp(e2, context, &preExp, simCode)
      let &res = buffer ""
      let &preExp +=
        <<
        <%tempDecl(stype, &res)%>
        { var d = <%val2%>;
          if (d = 0) throw new DivideByZeroException("event_div() failed at time " + Time + " because denominator is zero!");
          <%res%> = (int) (<%val1%> / d);
          mathEVPre[<%constIndex%>] = <%res%>;
        }
        >>
      res
    case SIMULATION_CONTEXT(genDiscrete=false) then
      '(<%stype%>) mathEVPre[<%constIndex%>]'
    case ALGLOOP_CONTEXT(genInitialisation=false) then
      let &res = buffer ""
      let &preExp +=
        let &preExpVal = buffer ""
        let val1 = daeExp(e1, context, &preExpVal, simCode)
        let val2 = daeExp(e2, context, &preExpVal, simCode)
        <<
        <%tempDecl(stype, &res)%>;
        if(IsDiscreteEvaluation && !IsContinuousEvaluation) {
          <%preExpVal%>
          var d = <%val2%>;
          if (d = 0) throw new DivideByZeroException("event_div() failed at time " + Time + " because denominator is zero!");
          <%res%> = (int) (<%val1%> / d);
          mathEVPre[<%constIndex%>] = <%res%>;
        } else {
          <%res%> = (<%stype%>) mathEVPre[<%constIndex%>];
        }<%\n%>
        >>
      res
    else error(sourceInfo(), 'Unexpected context for: <%ExpressionDump.printExpStr(inExp)%>')

  /* end codegeneration of event triggering math functions */

  // TODO: add more special builtins (Codegen.generateBuiltinFunction)
  // no return calls
  case CALL(attr=CALL_ATTR(tuple_=false,ty=T_NORETCALL(__))) then
    let argStr = (expLst |> it => daeExp(it, context, &preExp, simCode) ;separator=", ")
    let &preExp += '<%underscorePrefix(attr.builtin)%><%underscorePath(path)%>(<%argStr%>);<%\n%>'
    <<
    /* NORETCALL */
    >>
  // non tuple calls (single return value)
  case CALL(attr=attr as CALL_ATTR(tuple_=false)) then
    let argStr = (expLst |> it => daeExp(it, context, &preExp, simCode) ;separator=", ")
    let funName = underscorePath(path)
    <<
    <%underscorePrefix(attr.builtin)
    %><%funName%>(<%argStr%>)<%if not attr.builtin then '/* !!!TODO:.<%funName%>_rettype_1 */'%>
    >>
  case _ then "daeExpCall:NOT_YT_IMPLEMENTED"
end daeExpCall;

template daeExpArray(Exp aexp, Context context, Text &preExp, SimCode simCode) ::=
  match hackArrayReverseToCref(aexp, context)
  case cr as CREF(__) then daeExpCrefRhs(cr, context, &preExp, simCode)
  case a as ARRAY(__) then
    if scalar then
        let &arrayVar = buffer ""
      let params = a.array |> e => '(<%expTypeFromExp(e)%>)<%daeExp(e, context, &preExp, simCode)%>'
                   ;separator=", "
      let &preExp += '<%tempDecl("var",&arrayVar)%> = new <%expTypeArray(a.ty,1)%>(<%listLength(a.array)%>,-1,new[]{<%params%>});<%\n%>'
      arrayVar
    else
      "NON_SCALAR_ARRAY_notYetImplemeted"
end daeExpArray;

template daeExpMatrix(Exp mexp, Context context, Text &preExp, SimCode simCode)
 "Generates code for a cast expression."
::=
  match hackMatrixReverseToCref(mexp, context)
  case cr as CREF(__) then daeExpCrefRhs(cr, context, &preExp, simCode)
  case MATRIX(matrix={{}}) // special case for empty matrix: create dimensional array Real[0,1]
  case MATRIX(matrix={})   // special case for empty array: create dimensional array Real[0,1]
    then
    let &tmp = buffer ""
    let &preExp += '<%tempDecl("var",&tmp)%> = new <%expTypeArray(ty,2)%>(0,1);<%\n%>'
    tmp
  //only scalar orthogonal matrix for now
  case m as MATRIX(matrix=(row1::_)) then
    let &tmp = buffer ""
    let matArr = m.matrix |> row =>
                       (row |> elem =>
                           daeExp(elem, context, &preExp, simCode)
                        ;separator=", ")
                 ;separator=",\n"
    let &preExp +=
       <<
       <%tempDecl("var",&tmp)%> = new <%expTypeArray(m.ty,2)%>(<%listLength(m.matrix)%>,<%listLength(row1)%>,-1, new[]{
         <%matArr ;anchor%>
       });<%\n%>
       >>
    tmp
end daeExpMatrix;


template daeExpCast(Exp cexp, Context context, Text &preExp, SimCode simCode)
 "Generates code for a cast expression."
::=
match cexp
case CAST(__) then
  let expVar = daeExp(exp, context, &preExp, simCode)
  match ty
  case T_INTEGER(__)   then '((int)<%expVar%>)'
  case T_REAL(__)  then '((double)<%expVar%>)'
  else "NOT_IMPLEMENTED_CAST"
end daeExpCast;



template underscorePrefix(Boolean builtin) ::=
  match builtin
  case true then ""
  case false then "_"
end underscorePrefix;

// SECTION: GENERAL TEMPLATES, TEMPORARY VARIABLES

//newVar parameter is assumed to be empty or it can hold an identifier prefix
template tempDecl(String ty, Text &newVar) ::=
  let &newVar += '_tmp<%System.tmpTick()%>'
  <<
  <%ty%> <%newVar%>
  >>
end tempDecl;



// SECTION: GENERAL TEMPLATES, TYPES

template varType(Variable var)
 "Generates type for a variable."
::=
match var
case VARIABLE(__) then
  if instDims then
    expTypeArray(ty, listLength(instDims))
  else
    expTypeArrayIf(ty)
end varType;

// TODO: Check with Codegen
template expTypeShort(DAE.Type it) ::=
  match it
  case T_INTEGER(__)    then "int"
  case T_REAL(__)   then "double"
  case T_STRING(__) then "string"
  case T_BOOL(__)   then "bool"
  case T_ENUMERATION(__)   then "int"
  case T_UNKNOWN(__)  then "UNKNOWN_TYPE_NOT_SUPPORTED"
  case T_ANYTYPE(__)  then "ANYTYPE_TYPE_NOT_SUPPORTED"
  case T_ARRAY(__)  then expTypeShort(ty)
  case T_COMPLEX(complexClassType=EXTERNAL_OBJ(__)) then "object"
  case T_COMPLEX(__) then '/*struct*/<%underscorePath(ClassInf.getStateName(complexClassType))%>'
  case T_METATYPE(__) case T_METABOXED(__) then "META_TYPE_NOT_SUPPORTED"
  case T_FUNCTION_REFERENCE_VAR(__) then "FN_PTR_NOT_SUPPORTED"
  else "expTypeShort_ERROR"
end expTypeShort;


template expType(DAE.Type ty, Boolean isArray)
 "Generate type helper."
::=
  if isArray
  then 'expType_<%expTypeArray(ty,0)%>_NOT_YET'
  else expTypeShort(ty)
end expType;

template expTypeArray(DAE.Type ty, Integer dims) ::=
<<
SimArray<%dims%><<%expTypeShort(ty)%>>
>>
end expTypeArray;

template expTypeArrayIf(DAE.Type ty)
 "Generate type helper."
::=
  match ty
  case T_ARRAY(__) then expTypeArray(ty, listLength(dims))
  else expTypeShort(ty)
end expTypeArrayIf;


template expTypeFromExpArrayIf(Exp exp) ::=
  expTypeFromExp(exp)
end expTypeFromExpArrayIf;


template expTypeFromExp(Exp it) ::=
  match it
  case ICONST(__)
  case ENUM_LITERAL(__)   then "int"
  case RCONST(__)    then "double"
  case SCONST(__)    then "string"
  case BCONST(__)    then "bool"
  case BINARY(__)
  case UNARY(__)
  case LBINARY(__)
  case LUNARY(__)     then expTypeFromOp(operator)
  case RELATION(__)   then "bool" //TODO: a HACK, it was expTypeFromOp(operator)
  case IFEXP(__)      then expTypeFromExp(expThen)
  case CALL(attr=attr as CALL_ATTR(__)) then expTypeShort(attr.ty)
  case ARRAY(__)
  case MATRIX(__)
  case RANGE(__)
  case CAST(__)
  case CREF(__)
  case CODE(__)       then expTypeShort(ty)
  case ASUB(__)       then expTypeFromExp(exp)
  case REDUCTION(__)  then expTypeFromExp(expr)
  case _          then "expTypeFromExp:ERROR"
end expTypeFromExp;


template expTypeFromOp(Operator it) ::=
  match it
  case ADD(__)
  case SUB(__)
  case MUL(__)
  case DIV(__)
  case POW(__)
  case UMINUS(__)
  case UMINUS_ARR(__)
  case ADD_ARR(__)
  case SUB_ARR(__)
  case MUL_ARR(__)
  case DIV_ARR(__)
  case MUL_ARRAY_SCALAR(__)
  case ADD_ARRAY_SCALAR(__)
  case SUB_SCALAR_ARRAY(__)
  case MUL_SCALAR_PRODUCT(__)
  case MUL_MATRIX_PRODUCT(__)
  case DIV_ARRAY_SCALAR(__)
  case DIV_SCALAR_ARRAY(__)
  case POW_ARRAY_SCALAR(__)
  case POW_SCALAR_ARRAY(__)
  case POW_ARR(__)
  case POW_ARR2(__)
  case LESS(__)
  case LESSEQ(__)
  case GREATER(__)
  case GREATEREQ(__)
  case EQUAL(__)
  case NEQUAL(__)       then  expTypeShort(ty)
  case AND(__)
  case OR(__)
  case NOT(__) then "bool"
  case _ then "expTypeFromOp:ERROR"
end expTypeFromOp;

template dimension(Dimension d)
::=
  match d
  case DAE.DIM_INTEGER(__) then integer
  case DAE.DIM_UNKNOWN(__) then ":"
  else "INVALID_DIMENSION"
end dimension;


template error(builtin.SourceInfo srcInfo, String errMessage)
"Example source template error reporting template to be used together with the sourceInfo() magic function.
Usage: error(sourceInfo(), <<message>>) "
::=
let() = Tpl.addSourceTemplateError(errMessage, srcInfo)
<<

#error <% Error.infoStr(srcInfo) %>  <% errMessage %><%\n%>
>>
end error;

//for completeness; although the error() template above is preferable
template errorMsg(String errMessage)
"Example template error reporting template
 that is reporting only the error message without the usage of source information."
::=
let() = Tpl.addTemplateError(errMessage)
<<

#error <% errMessage %><%\n%>
>>
end errorMsg;

annotation(__OpenModelica_Interface="backend");
end CodegenCSharp;
// vim: filetype=susan sw=2 sts=2
