//==============================================================================
//  Structs and other stuff
//==============================================================================
unit Structs;

//==============================================================================
//  INTERFACE
//==============================================================================
interface

function Run(sinput:string):string;

//==============================================================================
//  IMPLEMENTATION
//==============================================================================
implementation
uses
  Windows, SysUtils, Classes, StringHash, slk, PseudoRandom,
  GrammarReader,  jasslib,
  TextOnly, vxFilesys, ScopeStack, JASSerException, Member,
  TextMacro, TextMacroException, JFunction, JStruct, Constants,
  ExternalUsage, JHInput, JHParams, Prototypes, JType, BlockMagic, ModuleMagic,
  ZincParser, JassHelperConfigFile, Translate, JHInterface, ImportPaths,
  TextCommon, StructVariables, Structs00, Structs01, Structs02, StructsSLK,
  InitializerFunctions;





//==============================================================================
//Actually method globals
procedure generateFuncPassGlobals( var outp:string);
var
   i,j,k,m,c:integer;
   hash:Tstringhash;
   types: array of string;
   dothis:boolean;
   counts: array of integer;

   typ:string;

   memb:Tmember;
   prot:Tfunctionprototype;
   tn:integer;
   checked: array of boolean;


   procedure inichecked(const it:integer);
   var
      x:integer;
   begin
       if(Length(checked)<it) then SetLength(checked,it);
       for x := 0 to it - 1 do checked[x]:=false;

   end;

   procedure addType(const s:string; const sc:integer);
   var
      x:integer;
   begin
       x:=0;
       while(x<tn) do begin
           if(types[x]=s) then break;
           x:=x+1;
       end;
       if(x=tn) then begin
           SetLength(types,x+1);
           SetLength(counts,x+1);
           tn:=tn+1;
           types[x]:=s;
           counts[x]:=0;
       end;
       if(counts[x]<sc) then counts[x]:=sc;
   end;
begin

    hash:=Tstringhash.Create;
    dothis:=false;
    tn:=0;
    for i := 1 to StructN do begin

        //is it an interface with do factory?
        if(StructList[i].isinterface and StructList[i].dofactory) then begin
            //then add a single integer argument
            addtype('integer',1);

            //don't forget the factory trigger:
            Swrite(outp,'trigger st__');
            Swrite(outp,StructList[i].name);
            SWriteLn(outp,'__factory');
        end;

        if(StructList[i].parentname<>'') then dothis:=true;



        for j := 0 to StructList[i].membern-1 do  begin
            memb:=StructList[i].members[j];
            if(memb.name='onDestroy') then dothis:=true;
            if(memb.fromparent) then continue;



            if(memb.abuse) then begin
               if (not StructList[i].isinterface)
                  and (not memb.stub)
                  and ( (StructList[i].parentstruct=-1) or (memb.name<>'onDestroy')   )
                  and ( (StructList[i].gotstructchildren=false) or (memb.name<>'onDestroy') )  then begin

                   Swrite(outp,'trigger st__');
                   Swrite(outp,StructList[i].name);
                   Swrite(outp,'_');
                   SWriteLn(outp,memb.name);

               end;

                dothis:=(dothis or not(memb.isstatic));
                inichecked(memb.argnumber);
                for k := 0 to memb.argnumber- 1 do if(not checked[k]) then begin
                    typ:=memb.argtypes[k];
                    if(structhash.ValueOf(typ)<>-1) then typ:='integer';

                    c:=1;
                    for m := k+1 to memb.argnumber-1 do begin
                        if(memb.argtypes[m]=typ) or( (typ='integer') and (structhash.ValueOf(memb.argtypes[m])<>-1)) then begin
                            c:=c+1;
                            checked[m]:=true;
                        end;
                    end;

                    addType(typ,c);
                end;
            end;

        end;
    end;


    //Do prototypes' globals:
    for i := 1 to PrototypeN do if(Prototype[i].abuse) then begin
        Swrite(outp,'trigger array st___prototype');
        SWriteLn(outp,IntToStr(i));
        prot:=Prototype[i];
        inichecked(prot.argn);
        for k := 0 to prot.argn - 1 do begin
            typ:=prot.args[k];
            if(structhash.ValueOf(typ)<>-1) then typ:='integer';

            c:=1;
            for m := k+1 to prot.argn-1 do begin
                if(prot.args[m]=typ) or ( (typ='integer') and (structhash.ValueOf(prot.args[m])<>-1)) then begin
                    c:=c+1;
                    checked[m]:=true;
                end;
            end;
            addType(typ,c);
        end;

        //prototypes are normalized
        typ:=prot.res;
        if(typ<>'nothing') then begin
            if (hash.ValueOf(typ)=-1) then begin
                 hash.Add(typ,5);
                 Swrite(outp,typ);
                 Swrite(outp,' f__result_');
                 SWriteLn(outp,typ);
            end;
        end;
    end;

    for i := 0 to tn-1 do begin
         for j := 1 to counts[i] do begin
             Swrite(outp,types[i]);
             Swrite(outp,' ');
             Swrite(outp,'f__arg_');
             Swrite(outp,types[i]);
             SWriteLn(outp,IntToStr(j));
         end;

    end;
    if(dothis) then begin
         SWriteLn(outp,'integer f__arg_this');
    end;


    for i := 1 to StructN do begin
       //is it an interface with do factory?
        if(StructList[i].isinterface and StructList[i].dofactory) then begin
            //then add f__result_integer if it wasn't added yet:
            if (hash.ValueOf('integer')=-1) then begin
                 hash.Add('integer',1);
                 SWriteLn(outp,'integer f__result_integer');
            end;


        end;

        j:=0;
        while(j<StructList[i].membern) do begin
            memb:=StructList[i].members[j];
            if(memb.abuse) then begin
             typ:=memb.returntype;
             if( structhash.ValueOf(typ)<>-1) then typ:='integer';
             if(typ<>'nothing') and (hash.ValueOf(typ)=-1) then begin
                 hash.Add(typ,1);
                 Swrite(outp,typ);
                 Swrite(outp,' f__result_');
                 SWriteLn(outp,typ);
             end;
            end;

            j:=j+1;
        end;
    end;
    hash.Destroy;
end;


//==============================================================================
//  Creates a default struct allocate function
//==============================================================================
procedure writechilddefaultinit(var st:tstruct; var parentstruct:tstruct;  var os:string; const dodestroy:boolean=false );
var
    s,tem:string;

begin

        s:= st.oninit;

        if((s<>'') or (st.containsarraymembers) ) then begin

            if (st.containsarraymembers) then begin

                Concatenate3(tem,'    if (si__',st.name,'_arrN==0) then');SWriteLn(os,tem);
                Concatenate5(tem,'        set si__',st.name,'_arrI=si__',st.name,'_arrI+1');SWriteLn(os,tem);
                Concatenate3(tem,'        set kthis=si__',st.name,'_arrI');SWriteLn(os,tem);
                Concatenate3(tem,'        if (kthis>',IntToStr(st.maximum),') then');SWriteLn(os,tem);

                if(dodestroy) then begin

                    if(st.requiredspace>=JASS_ARRAY_SIZE) then begin
                        GenerateMultiArrayPicker(RConcatenate3('si__',parentstruct.GetSuperParentName(StructList),'_'),'type',st.requiredspace, 'this',12,'set ','='+IntToStr(parentstruct.typeid),os);
                    end else begin
                        Concatenate4(tem,'            set si__',parentstruct.GetSuperParentName(StructList),'_type[this]=',IntToStr(parentstruct.typeid));SWriteLn(os,tem);
                    end;
                    Concatenate3(tem,'            call sc__',parentstruct.GetSuperParentName(StructList),'_deallocate(this)');SWriteLn(os,tem);
                end else begin
                    if(st.requiredspace>=JASS_ARRAY_SIZE) then begin
                        GenerateMultiArrayPicker(RConcatenate3('si__',parentstruct.name,'_'),'V',st.requiredspace, 'this',12,'set ',RConcatenate3('=si__',parentstruct.name,'_F'),os);
                    end else begin
                        Concatenate5(tem,'            set si__',parentstruct.name,'_V[this]=si__',parentstruct.name,'_F');SWriteLn(os,tem);
                    end;
                    Concatenate3(tem,'            set si__',parentstruct.name,'_F=this');SWriteLn(os,tem);
                end;

                if (DEBUG_MODE()) then begin
                     SWriteLn(os,'            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Unable to allocate id for an object of type: '+st.name+'")');
                end;
                SWriteLn(os,     '            return 0');
                SWriteLn(os     ,'        endif');
                SWriteLn(os     ,'    else');
                if(st.requiredspace>=JASS_ARRAY_SIZE) then begin
                    GenerateMultiArrayPicker(RConcatenate3('si__',st.name,'_'),'arrV',st.requiredspace,RConcatenate3('si__',st.name,'_arrN'),8,'set kthis=','',os);
                end else begin
                    Concatenate5(tem,'        set kthis=si__',st.name,'_arrV[si__',st.name,'_arrN]');SWriteLn(os,tem);
                end;
                Concatenate5(tem,'        set si__',st.name,'_arrN=si__',st.name,'_arrN-1');SWriteLn(os,tem);
                SWriteLn(os,     '    endif');

                if(st.requiredspace>=JASS_ARRAY_SIZE) then begin
                    GenerateMultiArrayPicker( RConcatenate3('si__',st.name,'_'),'arr',st.requiredspace,'this',4,'set ','=kthis',os);
                end else begin
                    Concatenate3(tem,'    set si__',st.name,'_arr[this]=kthis');SWriteLn(os,tem);
                end;

            end else SWriteLn(os,'    set kthis=this');

            SWriteLnSmart(os,s);

        end;



end;



















//==============================================================================
procedure DoCrazyRecursiveTriggerAssign( var iter:Tstruct; var curr: Tstruct; var memb:Tmember; var outs:string);
var
   i,n,k:integer;
   mname:string;
   tem:string;
   tem2:string;
   tememb:Tmember;
begin
    if(not curr.gotstructchildren) and (not curr.isinterface) then Exit;
    if( memb = nil) then mname := 'onDestroy' //default
    else mname := memb.name;
    //if(memb.name='onDestroy') then Exit;

    n:=curr.nchildren;
    for i := 0 to n-1 do begin
        k:=curr.children[i];

        if(StructList[k].getmember(mname,tememb) and ((iter<>curr)           or not iter.isinterface) ) then            continue;
       Concatenate6(tem,'    set st__',iter.name,'_',mname,'[',IntToStr(k));
        Concatenate8(tem2,tem,']=st__',iter.name,'_',mname,'[',IntToStr(curr.typeid),']');
        SWriteLn(outs,tem2);
        DoCrazyRecursiveTriggerAssign(iter,StructList[k],memb,outs);
    end;
end;

//==============================================================================
procedure DoCrazyRecursiveNullTriggerAssign( var iter:Tstruct; var curr: Tstruct; const membname:string; var outs:string);
var
   i,n,k:integer;
   tem:string;
   tememb:Tmember;
begin

    if(not curr.gotstructchildren) and (not curr.isinterface) then Exit;

    //if(memb.name='onDestroy') then Exit;

    n:=curr.nchildren;
    for i := 0 to n-1 do begin
        k:=curr.children[i];
        if(StructList[k].getmember(membname,tememb) or ((membname='onDestroy') and (StructList[k].containsarraymembers) )) then continue;
        Concatenate7(tem,'    set st__',iter.name,'_',membname,'[',IntToStr(k),']=null');
        SWriteLn(outs,tem);
        DoCrazyRecursiveNullTriggerAssign(iter,StructList[k],membname,outs);

    end;


end;


//==============================================================================
procedure WriteFactoryFunction(var st:Tstruct; var outs:string);
var
 tem:string;
 tmstruct2:Tstruct;
 childs:Array of integer;
 cn,i,c,x,b : integer;
 memb:Tmember;


     //================================
     function gotWeirdAllocate(var ist:Tstruct): boolean;
     var
         par:Tstruct;
         pmemb:Tmember;
     begin
         if(ist.parentstruct=-1) then Result:=false
         else begin
             par:=StructList[ist.parentstruct];
             if(par.customcreate) and par.getmember('create',pmemb)  then begin
                 if (pmemb.argnumber<>0)  then Result:=true
                 else result:=false;
             end else begin
                 result:=gotWeirdAllocate(par);
             end;
         end;

     end;


begin

    cn:=st.nchildren;
    SetLength(childs,cn+10);

    for i := 0 to cn - 1 do childs[i]:=st.children[i];

    i:=0;
    while(i<cn) do begin
        c:=childs[i];
        x:=StructList[c].nchildren;
        if (x>0) then begin
            if(x+cn>Length(childs)) then SetLength(childs,x+cn+20);
            x:=x-1;
            while (x>=0) do begin
                childs[cn]:=StructList[c].children[x];
                cn:=cn+1;
                x:=x-1;
            end;
        end;
        i:=i+1;
    end;

    Concatenate3(tem,#13#10'function sa__',st.name,'__factory takes nothing returns boolean');SWriteLn(outs,tem);

    for c := 0 to cn - 1 do begin
        b:=childs[c];
        tmstruct2:=StructList[b];
        if(c=0) then begin
            Concatenate3(tem,'    if (f__arg_integer1==',IntToStr(b),') then');
        end else begin
            Concatenate3(tem,'    elseif (f__arg_integer1==',IntToStr(b),') then');
        end;
        SWriteLn(outs,tem);

        if(tmstruct2.customcreate) then begin

            tmstruct2.getmember('create',memb);
            if(memb.argnumber=0) then begin //all is fine
                Concatenate3(tem,'        set f__result_integer=s__',tmstruct2.name,'_create()');
                SWriteLn(outs,tem);
            end else if (gotWeirdAllocate(tmstruct2)) then begin
                if(DEBUG_MODE()) then begin
                    SWriteLn(outs, '        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Warning: Unable to use allocate()  when trying to create a '+tmstruct2.name+' object using the interface create method.")');
                end;
                SWriteLn(outs,'        set f__result_integer=0');

            end else begin
                //not all is fine...
                if(DEBUG_MODE()) then begin
                    SWriteLn(outs, '        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Warning: Forced to use allocate() over .create() when trying to create a '+tmstruct2.name+' object using the interface create method.")');
                end;
                SWriteLn(outs,   '        set f__result_integer=0');
            end;
        end else begin
            if (gotWeirdAllocate(tmstruct2)) then begin
                if(DEBUG_MODE()) then begin
                    SWriteLn(outs, '        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Warning: Unable to use allocate()  when trying to create a '+tmstruct2.name+' object using the interface create method.")');
                end;
                SWriteLn(outs,'        set f__result_integer=0');

            end else begin //all is fine
                Concatenate3(tem,'        set f__result_integer=s__',tmstruct2.name,'__allocate()');
                SWriteLn(outs,tem);
            end;

        end;

    end;
    if(DEBUG_MODE()) then begin
        SWriteLn(outs,'    else');
        SWriteLn(outs, '        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Unable to allocate id for an object of type: '+st.name+', given a wrong typeid: "+I2S(f__arg_integer1))');
    end;
    SWriteLn(outs,'    endif');
    SWriteLn(outs,'    return true');
    SWriteLn(outs,'endfunction'#13#10);

end;

//==============================================================================
//I was going to make it parse common.j for this but there are issues with that:
//* WEHelper's plugin.pas does not allow me to get a good common.j file.
//* Certain handle types like player are not ref counted.
//==============================================================================
procedure setupBadHandleHash(var badHandleHash:TStringHash);
begin
    badHandleHash:=TStringHash.create();
    badHandleHash.add('event',1);
    badHandleHash.add('widget',1);
    badHandleHash.add('unit',1);
    badHandleHash.add('destructable',1);
    badHandleHash.add('item',1);
    badHandleHash.add('force',1);
    badHandleHash.add('group',1);
    badHandleHash.add('trigger',1);
    badHandleHash.add('triggercondition',1);
    badHandleHash.add('triggeraction',1);
    badHandleHash.add('timer',1);
    badHandleHash.add('location',1);
    badHandleHash.add('region',1);
    badHandleHash.add('rect',1);
    badHandleHash.add('boolexpr',1);
    badHandleHash.add('sound',1);
    badHandleHash.add('conditionfunc',1);
    badHandleHash.add('filterfunc',1);
    badHandleHash.add('unitpool',1);
    badHandleHash.add('itempool',1);
    badHandleHash.add('eventid',1);
    badHandleHash.add('unittype',1);
    badHandleHash.add('camerasetup',1);
    badHandleHash.add('effect',1);
    badHandleHash.add('weathereffect',1);
    badHandleHash.add('terraindeformation',1);
    badHandleHash.add('dialog',1);
    badHandleHash.add('button',1);
    badHandleHash.add('quest',1);
    badHandleHash.add('questitem',1);
    badHandleHash.add('timerdialog',1);
    badHandleHash.add('leaderboard',1);
    badHandleHash.add('multiboard',1);
    badHandleHash.add('multiboarditem',1);
    badHandleHash.add('trackable',1);
    badHandleHash.add('gamecache',1);
    badHandleHash.add('texttag',1);
    badHandleHash.add('lightning',1);
    badHandleHash.add('image',1);
    badHandleHash.add('ubersplat',1);


end;

//==============================================================================
//TODO: make this ignore blizzard.j / common.j function calls.
//==============================================================================
function DoesLineContainFunctionCalls(const s:string; linenumber:integer):boolean;
var k,L:integer;
tok:string;
func:TJassFunc;
begin
    result:=false;
    if(s='') then begin
        result:=true;
        exit;
    end;
     {if(compareLineToken('call',s,k) ) then
        result:=true
     else begin}
        L:=Length(s);
        k:=1;

        while (k>=1) and (k<=L) do begin
             GetLineToken(s,tok,k,k);
             if( tok = 'function') then result:=true
             else begin
                 //if the next non-whitespace character is (...
                 while (k>=1) and (k<=L) do begin
                      if s[k]='(' then begin
                          func := GetJassFuncDefinition(tok);
                          if(func = nil) then begin
                              //it is not a native/blizzard.j func, so it is a map func, ban
                              result:=true; exit;
                          end;
                      end;
                      if (s[k] in NONNUMBERSIDENTIFCHAR ) then
                          break;
                      k:=k+1;
                 end;
             end;

        end;


     //end;

end;













//==============================================================================
procedure WriteHooks(var output:string);
var
   i,j,x:integer;
   victim: THookedNative;
   hook:Thook;
   native: TJassFunc;
   tem:string;
   argumentspart : string;
begin
   for i := 0 to HookedNatives.Count - 1 do begin
       victim:=HookedNatives[i];
       Swrite(output,'function h__'+victim.nativename+' takes ');
       native := GetJassFuncDefinition(victim.nativename);
       if ( native.arguments.Count = 0) then begin
           argumentspart:='';
           Swrite(output,'nothing ');
       end else begin
           argumentspart:='';
           for j := 0 to native.arguments.Count - 1 do
               if(j+1<native.arguments.Count) then begin
                   Concatenate4(tem,native.arguments[j],' a',IntToStr(j),', ');
                   Swrite(output, tem);
                   Concatenate3(tem,'a',IntToStr(j),',');
                   Swrite(argumentspart,tem);

               end else begin
                   Concatenate4(tem,native.arguments[j],' a',IntToStr(j),' ');
                   Swrite(output, tem);
                   Swrite(argumentspart,'a'+IntToStr(j));
               end;

       end;
       SWriteLn(output, 'returns '+native.returntype);

       for j := 0 to victim.hooks.Count - 1 do begin
           hook:=Thook(victim.hooks[j]);
           if(hook.isMethod) then begin
               Concatenate4(tem,'    //hook: ',hook.struct.name,'.',hook.memb.name);
               SWriteLn(output,tem);
               Concatenate7(tem,'    call sc__',hook.struct.name,'_',hook.memb.name,'(',argumentspart,')');
           end else begin

               x:=Prototype[functiondata[hook.funcid].prototypeid].GetId(functiondata[hook.funcid].name);
               SWriteLn(output,'    //hook: '+functiondata[hook.funcid].name);

               if(native.arguments.Count = 0) then
                   Concatenate5(tem,'    call sc___prototype',IntToStr( functiondata[hook.funcid].prototypeid),'_evaluate(',IntToStr(x),')' )
               else
                   Concatenate7(tem,'    call sc___prototype',IntToStr( functiondata[hook.funcid].prototypeid),'_evaluate(',IntToStr(x),',',argumentspart,')');
           end;
           SWriteLn(output,tem);
       end;


       if(native.returntype='nothing') then
           Concatenate5(tem,'call ',native.name,'(',argumentspart,')')
       else
           Concatenate5(tem,'return ',native.name,'(',argumentspart,')');
       SWriteLn(output, tem);
       SWriteLn(output,'endfunction');
   end;
end;



//==============================================================================
//  MIDDLE
//==============================================================================


















//==============================================================================
// Generate functions for BigArrays that use more than one Jass array.
//==============================================================================
procedure Step03(var firstblock:string; var secondblock:string);
var
    i:integer;
    tem:string;
    typen:string;
begin
    if(BigArrayN>0) then begin
        SWriteLn(secondblock,#13#10'//Functions for BigArrays:');
        for i := 0 to BigArrayN-1 do if(BigArraySizes[i]>JASS_ARRAY_SIZE) then begin
            typen:=BigArrayTypes[i];
            if(StructHash.ValueOf(typen)<>-1) then typen:='integer';
            concatenate4(tem,#13#10'function sg__',BigArrayNames[i],'_get takes integer i returns ',typen);
            SWriteLn(firstblock,tem);
            generateMultiArrayPicker('s__',BigArrayNames[i],BigArraySizes[i],'i',4,'return ','',firstblock);
            SWriteLn(firstblock,'endfunction');

            concatenate5(tem,#13#10'function sg__',BigArrayNames[i],'_set takes integer i,',typen,' v returns nothing');
            SWriteLn(firstblock,tem);
            generateMultiArrayPicker('s__',BigArrayNames[i],BigArraySizes[i],'i',4,'set ','=v',firstblock);
            SWriteLn(firstblock,'endfunction');
        end;

    end;
end;

//==============================================================================
//Generate functions for structs/dynamic arrays that use more than one Jass arrays.
//==============================================================================
procedure Step04(var firstblock:string);
var
    i,j,TS:integer;
    memb:Tmember;
    tmstruct:TStruct;
    tem:string;
    typen:string;
begin
    for TS := 1 to StructN do begin
        i:=TopSortedStructList[TS];
        tmstruct:=StructList[i];

        if (tmstruct.customarray>0) and (tmstruct.requiredspace>=JASS_ARRAY_SIZE) then begin
            typen:=tmstruct.customarraytype;
            if(StructHash.ValueOf(typen)<>-1) then typen:='integer';
            concatenate4(tem,#13#10'function sg__',tmstruct.name,'_get takes integer i returns ',typen);
            SWriteLn(firstblock,tem);
            generateMultiArrayPicker('s__',tmstruct.name,tmstruct.requiredspace,'i',4,'return ','',firstblock);
            SWriteLn(firstblock,'endfunction');

            concatenate5(tem,#13#10'function sg__',tmstruct.name,'_set takes integer i,',typen,' v returns nothing');
            SWriteLn(firstblock,tem);
            generateMultiArrayPicker('s__',tmstruct.name,tmstruct.requiredspace,'i',4,'set ','=v',firstblock);
            SWriteLn(firstblock,'endfunction');
        end;

        if( (tmstruct.customarray<=0) and (tmstruct.requiredspace>=JASS_ARRAY_SIZE) ) then begin
            for j := 0 to tmstruct.membern-1 do if(not tmstruct.members[j].ignore) and (not tmstruct.members[j].ismethod) and (not tmstruct.members[j].isstatic) then begin
                memb:=tmstruct.members[j];
                typen:=tmstruct.members[j].returntype;
                if(StructHash.ValueOf(typen)<>-1) then typen:='integer';

                concatenate6(tem,#13#10'function sg__',tmstruct.name,'_get_',memb.name,' takes integer i returns ',typen);
                SWriteLn(firstblock,tem);
                generateMultiArrayPicker( RConcatenate3('s__',tmstruct.name,'_'),memb.name ,tmstruct.requiredspace,'i',4,'return ','',firstblock);
                SWriteLn(firstblock,'endfunction');

                concatenate7(tem,#13#10'function sg__',tmstruct.name,'_set_',memb.name,' takes integer i,',typen,' v returns nothing');
                SWriteLn(firstblock,tem);
                generateMultiArrayPicker(RConcatenate3('s__',tmstruct.name,'_'),memb.name,tmstruct.requiredspace,'i',4,'set ','=v',firstblock);
                SWriteLn(firstblock,'endfunction');

            end;
        end;

        if((tmstruct.gotstructchildren and (tmstruct.parent=-1)) or tmstruct.isinterface) and (tmstruct.requiredspace>=JASS_ARRAY_SIZE)  then begin
             Concatenate3(tem,#13#10'function si__',tmstruct.name,'_getType takes integer this returns integer');
             SWriteLn(firstblock,tem);
             generateMultiArrayPicker( RConcatenate3('si__',tmstruct.name,'_'),'type' ,tmstruct.requiredspace,'this',4,'return ','',firstblock);
             SWriteLn(firstblock,'endfunction');
        end;

    end;
end;

//==============================================================================
//Generate usual functions.
//==============================================================================
procedure Step05(var inilines:string; var execblock:string; var firstblock:string; var secondblock:string);
var
    i,j,k,a,b,c,TS,wordend:integer;
    badHandlesUsed:boolean;
    makeMethodCopy:boolean;
    doExec:boolean;
    ret:boolean;
    useallocforname:boolean;
    memb,memb2:Tmember;
    tmstruct,tmstruct2:TStruct;
    funcall,callstuff:string;
    tem,tem2:string;
    typ:string;
begin
    for TS := 1 to StructN do begin
        i:=TopSortedStructList[TS];
        tmstruct:=StructList[i];

        if( (tmstruct.parent<>-1) or (tmstruct.parentstruct<>-1)) and (tmstruct.containsarraymembers) then begin
            Concatenate3(tem,'function sa__',tmstruct.name,'__disposeArrays takes nothing returns boolean');
            SWriteLn(secondblock,tem);
            SWriteLn(secondblock,' local integer this=f__arg_this');
            SWriteLn(secondblock,' local integer kthis');

            Concatenate3(tem2,'si__',tmstruct.name,'_arrN');
            Concatenate5(tem,'    set ',tem2,'=',tem2,'+1');
            SWriteLn(secondblock,tem);
            if(tmstruct.requiredspace>=JASS_ARRAY_SIZE) then begin
                GenerateMultiArrayPicker( RConcatenate3('si__',tmstruct.name,'_'),'arr',tmstruct.requiredspace,'this',4,'set kthis=','',secondblock);
                GenerateMultiArrayPicker( RConcatenate3('si__',tmstruct.name,'_'),'arrV',tmstruct.requiredspace,tem2,4,'set ','=kthis',secondblock);

            end else begin
                Concatenate3(tem,'    set kthis=si__',tmstruct.name,'_arr[this]');
                SWriteLn(secondblock,tem);
                Concatenate5(tem,'    set si__',tmstruct.name,'_arrV[',tem2,']=kthis');
                SWriteLn(secondblock,tem);
            end;


            SWriteLn(secondblock,' return true');
            SWriteLn(secondblock,'endfunction');

        end;



        for j := 0 to tmstruct.membern - 1 do if(  (tmstruct.members[j].abuse) and(not tmstruct.members[j].destruct)  ) then begin

            execblock:='';

            memb:=tmstruct.members[j];
            makeMethodCopy := {false}not(tmstruct.isinterface)  and not (memb.stub);
            doExec := ( memb.abuseexec or tmstruct.isinterface);


            //write caller
            Concatenate4(tem,#13#10'//Generated method caller for ',tmstruct.name,'.',memb.name);
            SWriteLn(firstblock,tem);
            Concatenate5(tem,'function sc__',tmstruct.name,'_',memb.name,' takes ');
            swrite(firstblock,tem);

            if(doExec) then begin
                Concatenate4(tem,#13#10'//Generated method executor for ',tmstruct.name,'.',memb.name);
                SWriteLn(execblock,tem);
                Concatenate5(tem,'function sx__',tmstruct.name,'_',memb.name,' takes ');
                swrite(execblock,tem);
            end;



            if(memb.isstatic) then begin
                if(memb.argnumber=0) then begin
                    Swrite(firstblock,'nothing returns ');
                    if(doExec) then SWriteLn(execblock,'nothing returns nothing');
                    typ:=memb.returntype;if(StructHash.ValueOf(typ)<>-1) then typ:='integer';
                    SWriteLn(firstblock,typ);

                end else begin
                    typ:=memb.argtypes[0];if(StructHash.ValueOf(typ)<>-1) then typ:='integer';
                    Swrite(firstblock,typ);
                    Swrite(firstblock,' ');
                    Swrite(firstblock,memb.argnames[0]);
                    if(doExec) then begin
                        Swrite(execblock,typ);
                        Swrite(execblock,' ');
                        Swrite(execblock,memb.argnames[0]);

                    end;
                    for a := 1 to memb.argnumber-1 do begin
                        typ:=memb.argtypes[a];if(StructHash.ValueOf(typ)<>-1) then typ:='integer';

                        Concatenate4(tem,',',typ,' ',memb.argnames[a]);
                        swrite(firstblock,tem);
                        if(doExec) then Swrite(execblock,tem);
                    end;
                    if(doExec) then SWriteLn(execblock,' returns nothing');
                    swrite(firstblock,' returns ');
                    typ:=memb.returntype;if(StructHash.ValueOf(typ)<>-1) then typ:='integer';
                    SWriteLn(firstblock,typ);
                end;

            end else begin
                swrite(firstblock,'integer this');
                if(doExec) then swrite(execblock,'integer this');
                for a := 0 to memb.argnumber-1 do begin
                    typ:=memb.argtypes[a];if(StructHash.ValueOf(typ)<>-1) then typ:='integer';
                    Concatenate4(tem,',',typ,' ',memb.argnames[a]);
                    swrite(firstblock,tem);
                    if(doExec) then swrite(execblock,tem);
                end;
                if(doExec) then SWriteLn(execblock,' returns nothing');
                swrite(firstblock,' returns ');
                typ:=memb.returntype;if(StructHash.ValueOf(typ)<>-1) then typ:='integer';
                SWriteLn(firstblock,typ);
            end;


            callstuff:='';
            funcall:='';
            badHandlesUsed:=false;
            Concatenate5(funcall,'s__',tmstruct.name,'_',memb.name,'(');


            if(makeMethodCopy) then begin
                k:= memb.decl+1;
                while not CompareLineToken('endfunction',JHInput.Get(k),b) do begin
                     if (DoesLineContainFunctionCalls(JHInput.Get(k),k) ) then begin
                          makeMethodCopy:=false;
                          break;
                     end;
                     k:=k+1;
                end;

                k:= memb.decl+1;
                while makeMethodCopy and not CompareLineToken('endfunction',JHInput.Get(k),b) do begin
                     SWriteLn(firstblock, JHInput.Get(k));
                     k:=k+1;
                end;
            end;


            if(not memb.isstatic) then begin
                 if(not makeMethodCopy) then SWriteLn(firstblock,'    set f__arg_this=this');
                 //callstuff:='f__arg_this';
                 if(doExec) then SWriteLn(execblock,'    set f__arg_this=this');

                 callstuff:='local integer this=f__arg_this';
                 funcall:=funcall+'f__arg_this';

            end;

            for a := 0 to memb.argnumber-1 do begin
                typ:=memb.argtypes[a];
                if(StructHash.ValueOf(typ)<>-1) then typ:='integer';

                if(badHandleHash.ValueOf(typ)=1) then badHandlesUsed:=true;

                c:=1;
                for b := 0 to a - 1 do begin
                    if(memb.argtypes[b]=typ) or((typ='integer') and ( StructHash.ValueOf(memb.argtypes[b])<>-1 )) then c:=c+1;
                end;

                Concatenate3(tem,'f__arg_',typ,IntToStr(c));

                if( (a<>0) or not memb.isstatic) then begin
                    Concatenate3(tem2,funcall,',',tem);
                    funcall:=tem2;

                end else begin
                    funcall:=funcall+tem;
                end;


                Concatenate6(tem2,'local ',typ,' ',memb.argnames[a],'=',tem);
                if(callstuff<>'') then begin
                    swrite(callstuff,#13#10);
                    swrite(callstuff,tem2);

                end else callstuff:=tem2;

                if(not makeMethodCopy) then begin
                    Swrite(firstblock,'    set ');
                    Swrite(firstblock,tem);
                    Swrite(firstblock,'=');
                    SWriteLn(firstblock,memb.argnames[a]);
                end;

                if(doExec) then begin
                    Swrite(execblock,'    set ');
                    Swrite(execblock,tem);
                    Swrite(execblock,'=');
                    SWriteLn(execblock,memb.argnames[a]);
                end;

            end;

            funcall:=funcall+')';

            if( tmstruct.isinterface or memb.stub) then begin
                if ( memb.stub or (tmstruct.nchildren>0) ) then begin

                    if ((memb.interdefault <> '') and (memb.returntype<>'nothing') ) then begin
                        Concatenate7(tem,'    //An error in the next line would mean declaration for ', tmstruct.name,'.',memb.name,' had a wrong default (',memb.interdefault,')');
                        if(not makeMethodCopy) then SWriteLn(firstblock,tem);
                        typ:=memb.returntype;
                        if(StructHash.ValueOf(typ)<>-1) then typ:='integer';
                        Concatenate4(tem,'    set f__result_',typ,'=',memb.interdefault);
                        if(not makeMethodCopy) then SWriteLn(firstblock,tem);
                    end;

                    //it is impossible to have execute() on < or []
                    if (memb.name='_lessthan') then begin
                        Concatenate5(tem,'  if (si__',tmstruct.name,'_type[this]==si__',tmstruct.name,'_type[dummy]) then');
                        if(not makeMethodCopy) then SWriteLn(firstblock,tem);
                    end;

                    if(tmstruct.requiredspace>=JASS_ARRAY_SIZE) then begin
                        Concatenate5(tem,'    call TriggerEvaluate(st__',tmstruct.GetSuperParentName(StructList),'_',memb.name,'[');
                        if(not makeMethodCopy) then GenerateMultiArrayPicker( RConcatenate3('si__',tmstruct.name,'_'), 'type', tmstruct.requiredspace, 'this',4,tem,'])',firstblock);
                        Concatenate5(tem,'    call TriggerExecute(st__',tmstruct.name,'_',memb.name,'[');
                        GenerateMultiArrayPicker( RConcatenate3('si__',tmstruct.GetSuperParentName(StructList),'_'), 'type', tmstruct.requiredspace, 'this',4,tem,'])',execblock);
                    end else begin
                        Concatenate7(tem,'    call TriggerEvaluate(st__',tmstruct.name,'_',memb.name,'[si__',tmstruct.GetSuperParentName(StructList),'_type[this]])');
                        SWriteLn(firstblock,tem);
                        Concatenate7(tem,'    call TriggerExecute(st__',tmstruct.name,'_',memb.name,'[si__',tmstruct.GetSuperParentName(StructList),'_type[this]])');
                        SWriteLn(execblock,tem);
                    end;

                    if(not makeMethodCopy) and (memb.name='_lessthan') then begin
                           SWriteLn(firstblock,'  else');
                           if(DEBUG_MODE()) then
                              SWriteLn(firstblock,'    call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Warning: overloaded compare operator used between structs of different types.")');

                              Concatenate5(tem,'    call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Warning: "+I2S(si__',tmstruct.name,'_type[this])+" vs "+I2S(si__',tmstruct.name,'_type[dummy]))');
                              SWriteLn(firstblock,tem);

                           SWriteLn(firstblock,'    return false');
                        SWriteLn(firstblock,'  endif');
                    end;

                end else begin
                    if(not makeMethodCopy) then SWriteLn(firstblock,'    //no struct used this interface. We should probably avoid adding it...');
                end;

            end else if(tmstruct.gotstructchildren and (tmstruct.parentname='') and ((memb.name='onDestroy') or (memb.fromparent))  ) then begin
                Concatenate7(tem,'    call TriggerEvaluate(st__',tmstruct.name,'_',memb.name,'[',IntToStr(i),'])');
                if(not makeMethodCopy) then SWriteLn(firstblock,tem);
                if(doExec) then begin
                    Concatenate7(tem,'    call TriggerExecute(st__',tmstruct.name,'_',memb.name,'[',IntToStr(i),'])');
                    SWriteLn(execblock,tem);
                end;
            end else begin


               if (tmstruct.parentstruct<>-1) and ((memb.name='onDestroy') or (memb.fromparent)) then begin

                   tmstruct2:=tmstruct;
                   repeat
                       if(tmstruct2.parentstruct=-1) then
                           tmstruct2:=StructList[tmstruct2.parent]
                       else
                           tmstruct2:=StructList[tmstruct2.parentstruct];
                       if tmstruct2.getmember(memb.name,memb2)
                          and (memb2.stub) and not memb2.fromparent then
                           break;

                   until (tmstruct2.parentstruct=-1) and (tmstruct2.parent=-1);
                   { i holds the struct type id }
                   Concatenate7(tem,'    call TriggerEvaluate(st__',tmstruct2.name,'_',memb.name,'[',IntToStr(i),'])');
                   if(not makeMethodCopy) then SWriteLn(firstblock,tem);
                   if(doExec) then begin
                       Concatenate7(tem,'    call TriggerExecute(st__',tmstruct2.name,'_',memb.name,'[',IntToStr(i),'])');
                       SWriteLn(execblock,tem);
                   end;

               end else if (tmstruct.parent<>-1) and (memb.fromparent) then begin
                   tmstruct2:=StructList[tmstruct.parent];
                   { i holds the struct type id }
                   Concatenate7(tem,'    call TriggerEvaluate(st__',tmstruct2.name,'_',memb.name,'[',IntToStr(i),'])');
                   if(not makeMethodCopy) then
                       SWriteLn(firstblock,tem);
                   if(doExec) then begin
                       Concatenate7(tem,'    call TriggerExecute(st__',tmstruct2.name,'_',memb.name,'[',IntToStr(i),'])');
                       SWriteLn(execblock,tem);
                   end;

               end else begin
                   Concatenate5(tem,'    call TriggerEvaluate(st__',tmstruct.name,'_',memb.name,')');
                   if(not makeMethodCopy) then SWriteLn(firstblock,tem);
                   if(doExec) then begin
                       Concatenate5(tem,'    call TriggerExecute(st__',tmstruct.name,'_',memb.name,')');
                       SWriteLn(execblock,tem);
                   end;
               end;
            end;

            if(not tmstruct.isinterface) then begin
                //on inilines:
                if (  ((tmstruct.parentname<>'') or memb.stub ) and (memb.fromparent or memb.stub) and (memb.name<>'onDestroy') ) then begin

                    tmstruct2:=tmstruct;
                    while (tmstruct2.parent<>-1) or (tmstruct2.parentstruct<>-1) do begin
                         if tmstruct2.getmember(memb.name,memb2)
                            and (memb2.stub) and not memb2.fromparent then
                                break;

                         if(tmstruct2.parent=-1) then begin
                             if( memb.stub and not StructList[tmstruct2.parentstruct].getmember(memb.name, memb2)) then begin
                                 break;
                             end;
                             tmstruct2:=StructList[tmstruct2.parentstruct];
                         end
                         else begin
                             tmstruct2:=StructList[tmstruct2.parent];
                         end;
                    end;

                    { i holds the struct type id }
                    Concatenate7(tem,'    set st__',tmstruct2.name,'_',memb.name,'[',IntToStr(i),']=CreateTrigger()');
                    SWriteLn(inilines,tem);
                    DoCrazyRecursiveTriggerAssign(tmstruct2,tmstruct,memb,inilines);
                    Concatenate11(tem,'    call TriggerAddCondition(st__',tmstruct2.name,'_',memb.name,'[',IntToStr(i),'],Condition( function sa__',tmstruct.name,'_',memb.name,'))');
                    SWriteLn(inilines,tem);
                    Concatenate11(tem,'    call TriggerAddAction(st__',tmstruct2.name,'_',memb.name,'[',IntToStr(i),'], function sa__',tmstruct.name,'_',memb.name,')');
                    SWriteLn(inilines,tem);
                end else if ((memb.name='onDestroy') and (tmstruct.gotstructchildren) and (tmstruct.parentname='') ) then begin
                    Concatenate7(tem,'    set st__',tmstruct.name,'_',memb.name,'[',IntToStr(i),']=CreateTrigger()');
                    SWriteLn(inilines,tem);
                    DoCrazyRecursiveTriggerAssign(tmstruct,tmstruct,memb,inilines);
                    Concatenate11(tem,'    call TriggerAddCondition(st__',tmstruct.name,'_',memb.name,'[',IntToStr(i),'],Condition( function sa__',tmstruct.name,'_',memb.name,'))');
                    SWriteLn(inilines,tem);

                end else if (memb.name<>'onDestroy') or (tmstruct.parentname='') then begin

                    Concatenate5(tem,'    set st__',tmstruct.name,'_',memb.name,'=CreateTrigger()');
                    SWriteLn(inilines,tem);
                    Concatenate9(tem,'    call TriggerAddCondition(st__',tmstruct.name,'_',memb.name,',Condition( function sa__',tmstruct.name,'_',memb.name,'))');
                    SWriteLn(inilines,tem);

                    if(doExec) then begin
                        Concatenate9(tem,'    call TriggerAddAction(st__',tmstruct.name,'_',memb.name,', function sa__',tmstruct.name,'_',memb.name,')');
                        SWriteLn(inilines,tem);
                    end;
                end;
            end;

            typ:=memb.returntype;
            if(typ<>'nothing') then begin
                if(not makeMethodCopy) then Swrite(firstblock,' return f__result_');
                if( StructHash.ValueOf(typ)<>-1) then typ:='integer';
                if(not makeMethodCopy) then SWriteLn(firstblock,typ);
            end;

            SWriteLn(firstblock,'endfunction');
            if(doExec) then begin
                SWriteLn(execblock,'endfunction');
                swrite(firstblock,execblock);
            end;



            {typ holds return type}
            //write the trigger action:
            if(not tmstruct.isinterface) then begin
                ret:=False;
                Concatenate5(tem,'function sa__',tmstruct.name,'_',memb.name,' takes nothing returns boolean'#13#10);
                swrite(secondblock,tem);

                if (not badHandlesUsed) then begin
                  SWriteLn(secondblock,callstuff);

                  b:=memb.decl+1;


                  while ( not CompareLineToken('endfunction',JHInput.Get(b),wordend)) do begin
                    if(ret) then begin
                        SWriteLn(secondblock,'return true');
                        ret:=false;
                    end;
                    tem:=JHInput.Get(b);


                    if (Iswhitespace(tem)) then //nothing
                    else if(CompareLineToken('return',tem,wordend) ) then begin
                        if (memb.returntype<>'nothing') then begin
                            tem:=Copy(tem,wordend,Length(tem)) ;
                            if(Iswhitespace(tem)) then raise JasserLineException(b,'Expected a return value');
                            SWriteLn(secondblock,'set f__result_'+typ+'='+tem);
                        end else begin
                            tem:=Copy(tem,wordend,Length(tem)) ;
                            if(not Iswhitespace(tem)) then raise JasserLineException(b,'Unexpected return value. (method declared to return nothing)');
                        end;
                        ret:=true;
                        if (memb.name='onDestroy') and ((tmstruct.parent<>-1) or (tmstruct.parentstruct<>-1)) then begin
                            SWriteLn(secondblock,'    set f__arg_this=this');
                        end;

                    end else SWriteLn(secondblock,tem);
                    b:=b+1;
                  end;
                  if (memb.name='onDestroy') and ((tmstruct.parent<>-1) or (tmstruct.parentstruct<>-1)) then begin
                      SWriteLn(secondblock,'    set f__arg_this=this');
                  end;

                end else begin
                    if (memb.name='onDestroy') and ((tmstruct.parent<>-1) or (tmstruct.parentstruct<>-1)) then begin
                         SWriteLn(secondblock,' local integer this=f__arg_this');
                    end;
                    if(memb.returntype<>'nothing') then begin
                        concatenate4(tem,'    set f__result_',typ,'=',funcall);
                    end else tem:='    call '+funcall;
                    SWriteLn(secondblock,tem);
                    if (memb.name='onDestroy') and ((tmstruct.parent<>-1) or (tmstruct.parentstruct<>-1)) then begin
                         SWriteLn(secondblock,'    set f__arg_this=this');
                    end;

                end;



                {if(typ<>'nothing') then begin
                    Concatenate3(tem,'    set f__result_',typ,'=');
                    swrite(secondblock,tem);
                end else swrite(secondblock,'    call ');
                Concatenate7(tem,'s__',tmstruct.name,'_',memb.name,'(',callstuff,')');
                SWriteLn(secondblock,tem);}
                SWriteLn(secondblock,'   return true');
                SWriteLn(secondblock,'endfunction');
            end;


        end;

        //onDestroy for interfaces/structs extending structs
        memb:=nil;
        if (tmstruct.parentname<>'') and (tmstruct.getmember('onDestroy',memb) or tmstruct.containsarraymembers ) then  begin
            tmstruct2:=tmstruct;
            while (tmstruct2.parent<>-1) or (tmstruct2.parentstruct<>-1) do begin
                if(tmstruct2.parent=-1) then tmstruct2:=StructList[tmstruct2.parentstruct]
                else tmstruct2:=StructList[tmstruct2.parent];
            end;

            Concatenate5(tem2,'st__',tmstruct2.name,'_onDestroy[',IntToStr(i),']');

            { i holds the struct type id }
            Concatenate3(tem,'    set ',tem2,'=CreateTrigger()');
            SWriteLn(inilines,tem);
            DoCrazyRecursiveTriggerAssign(tmstruct2,tmstruct,memb,inilines);
            tmstruct2:=tmstruct;

            while true do begin
                if (tmstruct2.getmember('onDestroy',memb2)) then begin
                    Concatenate5(tem,'    call TriggerAddCondition(',tem2,',Condition( function sa__',tmstruct2.name,'_onDestroy))');
                    SWriteLn(inilines,tem);
                end;

                if (tmstruct2.parent=-1) and (tmstruct2.parentstruct=-1) then break;
                if (tmstruct2.containsarraymembers) then begin
                    Concatenate5(tem,'    call TriggerAddCondition(',tem2,',Condition( function sa__',tmstruct2.name,'__disposeArrays))');
                    SWriteLn(inilines,tem);
                end;

                if(tmstruct2.parent=-1) then tmstruct2:=StructList[tmstruct2.parentstruct]
                else tmstruct2:=StructList[tmstruct2.parent];
            end;

        end;

        //factory stuff::
        if(tmstruct.isinterface and tmstruct.dofactory) then begin
             Concatenate3(tem,'    set st__',tmstruct.name,'__factory=CreateTrigger()');SWriteLn(inilines,tem);
             Concatenate5(tem,'    call TriggerAddCondition(st__',tmstruct.name,'__factory,Condition(function sa__',tmstruct.name,'__factory))');SWriteLn(inilines,tem);

             WriteFactoryFunction(tmstruct,secondblock);
        end;

        if(tmstruct.parent<>-1) and (tmstruct.ondestroy=nil) and not( tmstruct.containsarraymembers ) then begin
            tmstruct2:=StructList[tmstruct.parent];
            { i holds the struct type id }
            // Add a CreateTrigger() line but don't add the condition.
            //Concatenate5(tem,'    set st__',tmstruct2.name,'_onDestroy[',IntToStr(i),']=CreateTrigger()');
            Concatenate5(tem,'    set st__',tmstruct2.name,'_onDestroy[',IntToStr(i),']=null');
            SWriteLn(inilines,tem);
            DoCrazyRecursiveNullTriggerAssign( tmstruct2, tmstruct, 'onDestroy' , inilines);
        end
        else if(tmstruct.parentstruct=-1) and (tmstruct.gotstructchildren) and (tmstruct.ondestroy=nil) and not( tmstruct.containsarraymembers ) then begin
            { i holds the struct type id }
            // Add a CreateTrigger() line but don't add the condition.
            //Concatenate5(tem,'    set st__',tmstruct2.name,'_onDestroy[',IntToStr(i),']=CreateTrigger()');
            Concatenate5(tem,'    set st__',tmstruct.name,'_onDestroy[',IntToStr(i),']=null');
            SWriteLn(inilines,tem);
            DoCrazyRecursiveNullTriggerAssign( tmstruct, tmstruct, 'onDestroy' , inilines);
        end;

        if (tmstruct.isinterface) then  begin

            for j := 0 to tmstruct.membern-1 do begin
                memb:=tmstruct.members[j];
                if(memb.ismethod) then begin
                    DoCrazyRecursiveNullTriggerAssign(tmstruct,tmstruct,memb.name,inilines);
                end;

            end;

        end;

        if (not tmstruct.forInternalUse) then begin

            if(not tmstruct.isinterface) and (tmstruct.FunctionInterfacePrototype=-1) and not(tmstruct.isArrayStruct) then begin
                     //write constructor:
                     swrite(firstblock,#13#10'//Generated allocator of ');
                     SWriteLn(firstblock,tmstruct.name);

                     tmstruct2:=tmstruct;
                     memb:=nil;
                     useallocforname:=false;
                     if(tmstruct.parentstruct=-1) then begin
                         Concatenate3(tem,'function s__',tmstruct.name,'__allocate takes nothing returns integer');
                         SWriteLn(firstblock,tem);
                     end else begin
                         tmstruct2:=StructList[tmstruct.parentstruct];
                         Concatenate3(tem,'function s__',tmstruct.name,'__allocate takes ');

                         if(tmstruct2.getmember('create',memb)) then begin
                             k:=tmstruct2.typeid;

                             while ((memb.construct) and (StructList[k].parentstruct<>-1)) do begin
                                 useallocforname:=true;
                                 k:=StructList[k].parentstruct;
                                 if (not StructList[k].getmember('create',memb)) then raise JasserLineException(StructList[k].decl,'[internal error] No create method???');
                             end;

                             if(memb.construct) then begin
                                 Swrite(tem,'nothing ');
                             end else begin
                                 if(memb.argnumber=0) then begin
                                     Swrite(tem,'nothing ');
                                 end else for j := 0 to memb.argnumber - 1 do begin
                                     typ:=memb.argtypes[j];
                                     if(StructHash.ValueOf(typ)<>-1) then typ:='integer';
                                     if(j=0) then begin
                                         Swrite(tem,typ+' '+memb.argnames[j]+' ');
                                     end else begin
                                         Swrite(tem,','+typ+' '+memb.argnames[j]+' ');
                                     end;
                                 end;
                             end;
                             Swrite(tem,'returns integer');
                             SWriteLn(firstblock,tem);
                         end else raise JasserLineException(tmstruct2.decl,'[internal error] No create method???');
                     end;

                     if(tmstruct.parent<>-1) then begin
                        SWriteLn(firstblock,' local integer kthis');
                        tmstruct2:=StructList[tmstruct.parent];
                     end;

                     if(tmstruct.parentstruct<>-1) then begin

                         if(memb<>nil) then begin
                             if(memb.construct or  useallocforname ) then begin
                                 Concatenate3(tem,' local integer this=s__',tmstruct2.name,'__allocate('); //just allocator...
                             end else begin
                                 Concatenate3(tem,' local integer this=sc__',tmstruct2.name,'_create(');
                             end;
                             for j := 0 to memb.argnumber-1 do begin
                                 if((memb.argnames[j]='this')or(memb.argnames[j]='kthis')) then begin
                                      raise JasserLineException(memb.decl,'Illegal argument name for constructor: '+memb.argnames[j]);
                                 end;
                                 if(j=0) then begin
                                     Swrite(tem,memb.argnames[j]);
                                 end else begin
                                     Swrite(tem,','+memb.argnames[j]);
                                 end;
                             end;
                             //if(memb.argnumber=1) then tem:=tem+'##';
                             Swrite(tem,')');
                             SWriteLn(firstblock,tem);

                         end else raise JasserLineException(tmstruct2.decl,'[internal error] No create method!!!');
                         SWriteLn(firstblock,' local integer kthis');
                         SWriteLn(firstblock,'    if(this==0) then');
                         SWriteLn(firstblock,'        return 0');
                         SWriteLn(firstblock,'    endif');


                         if(tmstruct.requiredspace>=JASS_ARRAY_SIZE) then begin
                             GenerateMultiArrayPicker(RConcatenate3('si__',tmstruct.GetSuperParentName(StructList),'_'),'type',tmstruct.requiredspace,'this',4,'set ','='+IntToStr(tmstruct.typeid),firstblock);
                         end else begin
                             Concatenate4(tem,'    set si__',tmstruct.GetSuperParentName(StructList),'_type[this]=',IntToStr(tmstruct.typeid));
                             SWriteLn(firstblock,tem);
                         end;

                         if(tmstruct2.parent=-1) then
                              writechilddefaultinit(tmstruct,tmstruct2,firstblock,true)
                         else
                              writechilddefaultinit(tmstruct,StructList[tmstruct2.parent],firstblock,true);

                         //SWriteLnSmart(firstblock,tmstruct.oninit);
                     end else begin
                         Concatenate3(tem,' local integer this=si__',tmstruct2.name,'_F');SWriteLn(firstblock,tem);
                         SWriteLn(firstblock,'    if (this!=0) then');
                         if(tmstruct.requiredspace>=JASS_ARRAY_SIZE) then begin
                             Concatenate3(tem,'set si__',tmstruct2.name,'_F=');
                             generateMultiArrayPicker(RConcatenate3('si__',tmstruct2.name,'_'),'V',tmstruct2.requiredspace,'this',8,tem,'',firstblock);
                         end else begin
                             Concatenate5(tem,   '        set si__',tmstruct2.name,'_F=si__',tmstruct2.name,'_V[this]');SWriteLn(firstblock,tem);
                         end;
                         SWriteLn(firstblock,'    else');

                         if (tmstruct.customarray>0) then begin
                            Concatenate6(tem,'        set si__',tmstruct2.name,'_I=si__',tmstruct2.name,'_I+',IntToStr(tmstruct2.customarray));
                         end else begin
                            Concatenate5(tem,'        set si__',tmstruct2.name,'_I=si__',tmstruct2.name,'_I+1');
                         end;
                         SWriteLn(firstblock,tem);
                         Concatenate3(tem,   '        set this=si__',tmstruct2.name,'_I');SWriteLn(firstblock,tem);
                         SWriteLn(firstblock,'    endif');

                         if (tmstruct.customarray>0) then begin
                             Concatenate3(tem,   '    if (this>',IntToStr(tmstruct2.maximum-(tmstruct2.customarray-1)),') then');
                         end else begin
                             Concatenate3(tem,   '    if (this>',IntToStr(tmstruct2.maximum),') then');
                         end;
                         SWriteLn(firstblock,tem);
                         if (DEBUG_MODE()) then begin
                             SWriteLn(firstblock,'        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Unable to allocate id for an object of type: '+tmstruct.name+'")');
                         end;
                         SWriteLn(firstblock,'        return 0');
                         SWriteLn(firstblock,'    endif');

                         SWriteLnSmart(firstblock,tmstruct2.oninit);
                         if(tmstruct.parent<>-1) then begin

                             if(tmstruct.requiredspace>=JASS_ARRAY_SIZE) then begin
                                 GenerateMultiArrayPicker(RConcatenate3('si__',tmstruct2.name,'_'),'type',tmstruct.requiredspace,'this',4,'set ','='+IntToStr(i),firstblock);
                             end else begin
                                 Concatenate4(tem,'    set si__',tmstruct2.name,'_type[this]=',IntToStr(i));
                                 SWriteLn(firstblock,tem);
                             end;

                             writechilddefaultinit(tmstruct,tmstruct2,firstblock)
                         end else if (tmstruct.gotstructchildren) then begin
                             if(tmstruct.requiredspace>=JASS_ARRAY_SIZE) then begin
                                 GenerateMultiArrayPicker(RConcatenate3('si__',tmstruct.name,'_'),'type',tmstruct.requiredspace,'this',4,'set ','='+IntToStr(i),firstblock);
                             end else begin
                                 Concatenate4(tem,'    set si__',tmstruct.name,'_type[this]=',IntToStr(i));
                                 SWriteLn(firstblock,tem);
                             end;
                         end;

                         if(tmstruct.requiredspace>=JASS_ARRAY_SIZE) then begin
                             generateMultiArrayPicker(RConcatenate3('si__',tmstruct2.name,'_'),'V',tmstruct.requiredspace,'this',4,'set ','=-1',firstblock);
                         end else begin
                             Concatenate3(tem,'    set si__',tmstruct2.name,'_V[this]=-1');SWriteLn(firstblock,tem);
                         end;



                     end;

                     SWriteLn(firstblock,' return this');
                     SWriteLn(firstblock,'endfunction'#13#10);
             end;

             if(tmstruct.IsInterface and tmstruct.dofactory) then begin
                 swrite(firstblock,'//generated factory of ');
                 SWriteLn(firstblock,tmstruct.name);

                 Concatenate3(tem,   'function sc__',tmstruct.name,'__factory takes integer typeid returns integer'); SWriteLn(firstblock,tem);
                 SWriteLn(firstblock,'    set f__result_integer=0');
                 SWriteLn(firstblock,'    set f__arg_integer1=typeid');
                 Concatenate3(tem,   '    call TriggerEvaluate(st__',tmstruct.name,'__factory)'); SWriteLn(firstblock,tem);
                 SWriteLn(firstblock,'    return f__result_integer');
                 SWriteLn(firstblock,'endfunction');
             end;

             if   (not tmstruct.isArrayStruct) and (tmstruct.parent=-1) and (tmstruct.parentstruct=-1) and (tmstruct.FunctionInterfacePrototype=-1) then begin
                     //write destructor:
                     swrite(firstblock,'//Generated destructor of ');
                     SWriteLn(firstblock,tmstruct.name);
                 if (tmstruct.ondestroy<>nil) or(tmstruct.isinterface) or (tmstruct.gotstructchildren) then
                     Concatenate3(tem,'function sc__',tmstruct.name,'_deallocate takes integer this returns nothing')
                 else
                     Concatenate3(tem,'function s__',tmstruct.name,'_deallocate takes integer this returns nothing');
                     SWriteLn(firstblock,tem);

                     if(tmstruct.requiredspace>=JASS_ARRAY_SIZE) then begin
                         SWriteLn(firstblock,' local integer used');
                     end;


                     SWriteLn(firstblock,'    if this==null then');
                     if(DEBUG_MODE()) then begin
                         SWriteLn(firstblock,'            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Attempt to destroy a null struct of type: '+tmstruct.name+'")');
                     end;
                     SWriteLn(firstblock,'        return');

                   if(tmstruct.requiredspace>=JASS_ARRAY_SIZE) then begin
                     SWriteLn(firstblock,'    else');
                     generateMultiArrayPicker(RConcatenate3('si__',tmstruct.name,'_'),'V',tmstruct.requiredspace,'this',8,'set used=','',firstblock);

                     SWriteLn(firstblock,'        if (used!=-1) then');
                     if(DEBUG_MODE()) then begin
                         SWriteLn(firstblock,'            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Double free of type: '+tmstruct.name+'")');
                     end;
                     SWriteLn(firstblock,'            return');
                     SWriteLn(firstblock,'        endif');



                   end else begin
                     Concatenate3(tem,'    elseif (si__',tmstruct.name,'_V[this]!=-1) then');
                     SWriteLn(firstblock,tem);
                     if(DEBUG_MODE()) then begin
                         SWriteLn(firstblock,'            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1000.,"Double free of type: '+tmstruct.name+'")');
                     end;
                     SWriteLn(firstblock,'        return');
                   end;
                     SWriteLn(firstblock,'    endif');


                if(( tmstruct.isinterface) or (tmstruct.gotstructchildren)) then begin
                    if(tmstruct.nchildren>0) then begin
                        SWriteLn(firstblock,'    set f__arg_this=this');

                        if(tmstruct.requiredspace>=JASS_ARRAY_SIZE) then begin
                            GenerateMultiArrayPicker( RConcatenate3('si__',tmstruct.name,'_'), 'type', tmstruct.requiredspace,'this',4, 'call TriggerEvaluate(st__'+tmstruct.name+'_onDestroy[','])',firstblock);
                        end else begin
                            Concatenate5(tem,'    call TriggerEvaluate(st__',tmstruct.name,'_onDestroy[si__',tmstruct.name,'_type[this]])');
                            SWriteLn(firstblock,tem);
                        end;



                    end else begin
                        SWriteLn(firstblock,'    //no struct used this interface. We should probably avoid adding it...');
                    end;
                end else if(tmstruct.ondestroy<>nil) then begin
                         SWriteLn(firstblock,'    set f__arg_this=this');
                         Concatenate3(tem,'    call TriggerEvaluate(st__',tmstruct.name,'_onDestroy)');
                         SWriteLn(firstblock,tem);
                end;

                 if(tmstruct.requiredspace>=JASS_ARRAY_SIZE) then begin
                     Concatenate3(tem,'=si__',tmstruct.name,'_F');
                     generateMultiArrayPicker(RConcatenate3('si__',tmstruct.name,'_'),'V',tmstruct.requiredspace,'this',4,'set ',tem,firstblock);
                 end else begin
                     Concatenate5(tem,'    set si__',tmstruct.name,'_V[this]=si__',tmstruct.name,'_F');SWriteLn(firstblock,tem);
                 end;
                     Concatenate3(tem,'    set si__',tmstruct.name,'_F=this');SWriteLn(firstblock,tem);

                     SWriteLn(firstblock,'endfunction');
             end;
        end;
    end;
end;

//==============================================================================
procedure BuildMainInitFunction(var secondblock:string; const inilines:string);
var
    i:integer;
    memb:TMember;
    //tem:string;
begin
    SWriteLn(secondblock,#13#10'function jasshelper__init'+structmagic_time+' takes nothing returns nothing');
    SWriteLn(secondblock,inilines);

    {
    for i := 1 to StructN do begin
        SWriteLn(secondblock,StructList[i].modulesOnInit);
    end;

    for i := 1 to StructN do begin
        if StructList[i].getmember('onInit',memb) then begin
            Concatenate3(tem,'    call ExecuteFunc("s__',StructList[i].name,'_onInit")');
            SWriteLn(secondblock,tem);
        end;
    end;
    }
    // add struct initializers
    for i := 1 to StructN do begin
        if StructList[i].getmember('onInit',memb) then begin
            InitializerFunctions.AddStructInit(
                StructList[i].libraryName,
                StructList[i].name,
                's__'+StructList[i].name+'_onInit'
            );
        end else begin
            InitializerFunctions.AddStructInit(
                StructList[i].libraryName,
                StructList[i].name,
                ''
            ); //  we insert empty onInit because we need struct as position marker
        end
    end;
    // add module intializers
    for i := 1 to StructN do begin
        InitializerFunctions.AddModuleInit(
            StructList[i].libraryName,
            StructList[i].name,
            StructList[i].modulesOnInit
        );
    end;

    SWriteln(secondblock, InitializerFunctions.GetExecuteList);
    //Writeln(InitializerFunctions.GetExecuteList);

    SWriteLn(secondblock,'endfunction');
end;

//==============================================================================
// Expects a file that has been already processed by JASSHelper
//==============================================================================
function Run(sinput:string):string;
var
    i:integer;
    globalsblock:string;
    inilines:string;
    nativesblock: string;
    execblock:string;
    firstblock:string;
    secondblock:string;
begin
    //This allows Jasshelper to execute twice on a map even if both instances had structs
    // There is a chance both will get same pseudorandom, but we trust in out luck :)
    structmagic_time:=IntToStr(GetPseudoRandom());

    Result:='ERROR';

    ProStatus('STRUCTS');
    ProStatus('  Loading: ');

    JHInput.Reset2();
    JHInput.Load(sinput);
    if (inputN<2) then raise Exception.Create('Input file seems too small / unclosed string issues');

    ProEnd();

    JHInput.ClearOffsets();

    // module processing
    ModuleMagic.DoStructModuleMagic();

    // static if processing
    BlockMagic.DoJasserBlocksMagic();

    StructN:=0;

    StructHash:=TStringHash.Create;
    RequiredSizeHash:=TStringHash.Create;
    IntegerConstants:=TStringHash.Create;
    IdentifierTypes:=TStringHash.Create;
    LocalIdentifierTypes:=TStringHash.Create;

    setupBadHandleHash(badHandleHash);

    functionLines:=Tlist.create();
    nativeLines:=Tlist.create();
    hookLines := TList.Create();

    hookedNatives := TList.Create();
    hookedNativeHash := TStringHash.create();


    BigArrayHash:=Tstringhash.Create();
    BigArrayN:=0;

    Prototypes.InitFunctionPrototypes;  // simple reset
    nativesblock:='';

    InitNewGlobals;

try
    ProStatus('  Parsing: ');
    Step00(nativesblock);
    ProEnd();

    ProStatus('  Verifying: ');
    Step01;
    ProEnd();

    result:='';

    ProStatus('  Evaluation: ');
    Step02(globalsblock, execblock, firstblock, secondblock);
    ProEnd();

    ProStatus('  Writing: ');
    NormalizeFunctionArguments;
    generateFuncPassGlobals(globalsblock);
    inilines:='';

    SWriteLn(secondblock,#13#10'//All initializer functions called in proper order:');

    Step03(firstblock, secondblock);

    Step04(firstblock);



    Step05(inilines, execblock, firstblock, secondblock);

    MakeFunctionCallers(firstblock);
    buildFunctionActions(secondblock);
    buildIniFunctionActions(inilines);

    // jasshelper_init
    buildMainInitFunction(secondblock, inilines);

    WriteDataFunctions(secondblock);

    WriteHooks(firstblock);

    SWriteLn(result,globalsblock);
    SWriteLn(result,'endglobals');
    SWriteLn(result, nativesblock);
    SWriteLn(result,firstblock);
    SWriteLn(result,secondblock);
    ProEnd();
finally
    //Close(DEBUGFILE);
    for i := 1 to StructN do StructList[i].Free;
    functionLines.Free;
    hookedNatives.Free;
    hookedNativeHash.Free;

    nativeLines.Free;
    hookLines.Free;
    BigArrayHash.Free;
    StructHash.Free;
    RequiredSizeHash.Free;
    BadHandleHash.Free;
    IntegerConstants.Free;
    IdentifierTypes.Free;
    LocalIdentifierTypes.Free;
    CleanFunctionPrototypes;
end;


end;

end.
