unit Inliner;

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

function Run(sinput:string):string;
//procedure Run(const f1:string; const f2:string);overload;

//==============================================================================
//  IMPLEMENTATION
//==============================================================================
implementation
uses StringHash, TextOnly, TextCommon, JHInput, JASSerException, Constants,
     SysUtils, vxFileSys, JHInterface;


var
    InlineFuncHash:TStringHash;

    InlineFuncReplaceText:array of array of string;
    InlineFuncContents:array of string;
    InlineFuncReplaceArgs:array of array of integer;
    InlineFuncReplaceN:array of integer;
    InlineFuncArgumentType: array of array of string;
    InlineFuncArgumentN: array of integer;
    InlineFuncN:integer;
    NoStateFunctionsHash:TStringHash;

//=======================================================================
// Resize the inline arrays so they can hold at least n elements.
procedure inlineResizeArrays(n:integer);
var ns:integer;
begin
    if(Length(InlineFuncReplaceText)<n) then begin
         ns:=n+(n div 5) + 5;
         SetLength(InlineFuncReplaceText,ns);
         SetLength(InlineFuncReplaceArgs,ns);
         SetLength(InlineFuncArgumentType,ns);
         SetLength(InlineFuncArgumentN,ns);
         SetLength(InlineFuncReplaceN,ns);
         SetLength(InlineFuncContents,ns);
    end;
end;

//===========================================================================
// processes a function that might be a return bug exploiter
function inline_process_returnbugcandidate(const p:integer; const c:integer):boolean;
var j,k:integer;
 argname,funcname, functype,argtype:string;
begin
    Result:=false;
    j:=1;
    if(CompareLineWord('constant',JHInput.Get(p),k)) then j:=k;
    if not CompareLineWord('function',JHInput.Get(p),j,j) then exit;
    GetLineWord(JHInput.Get(p),funcname,j,j);
    if not CompareLineWord('takes',JHInput.Get(p),j,j) then exit;
    GetLineWord(JHInput.Get(p),argtype,j,j);
    if (argtype='nothing') then exit;
    GetLineWord(JHInput.Get(p),argname,j,j);
    if not CompareLineWord('returns',JHInput.Get(p),j,j) then exit;
    GetLineWord(JHInput.Get(p),functype,j,j);
    if ((functype=argtype) or (functype='nothing') ) then exit;

    j:=1;
    if(not CompareLineWord('return',JHInput.Get(c),j,j)) then exit;
    if(not CompareLineWord(argname,JHInput.Get(c),j,j)) then exit;
    if(not IsWhiteSpace(JHInput.Get(c),j) ) then exit;

    //It is a return bug exploiter!, the second line does not matter at all BTW.


    NoStateFunctionsHash.add(funcname,1);
    Result:=true;
end;


//===========================================================================
// processes a function that was marked as inlineable, add to hash, and create
// replace data. p:function declaration line. c: function content line.
procedure inline_process_inleanable(const p:integer; const c:integer);
var start,j,k,r:integer;
    funcname,aname,atype,tok,nonarg:string;
    ArgumentHash:TStringHash;
    F,T,x,L,an:integer;
    ctr:boolean;
begin
    if(c<0) then exit;       //need a way to handle empty functions, *coming soon*

    j:=1;
    if(CompareLineWord('constant',JHInput.Get(p),k)) then j:=k;
    CompareLineWord('function',JHInput.Get(p),j,j);
    GetLineWord(JHInput.Get(p),funcname,j,j);

    if(not CompareLineWord('takes',JHInput.Get(p),j,j)) then begin
        raise JasserLineException(p,'Expected: "takes"');
    end;
    //we'll just blatantly assume the syntax is all right...
    ArgumentHash:=TStringHAsh.Create;
    F:=InlineFuncN;
    InlineFuncHash.Add(funcname,F);
    InlineFuncN:=InlineFuncN+1;
    inlineResizeArrays(InlineFuncN);
    InlineFuncArgumentN[F]:=0;
    an:=0;
    if (CompareLineWord('nothing',JHInput.Get(p),k,j)) then begin
        j:=k;
        CompareLineWord('returns',JHInput.Get(p),j,j);
        //uh, the function takes nothing, be happy?

    end else begin
        while(not CompareLineToken('returns',JHInput.Get(p),k,j)) do begin
            GetLineToken(JHInput.Get(p),atype,j,j);
            GetLineToken(JHInput.Get(p),aname,j,j);

            if(atype='') or (aname='') then break;
            an:=an+1;
            if(Length(InlineFuncArgumentType[F])<an) then SetLength(InlineFuncArgumentType[F],an+3); //all right, how many arguments does this thing need?
            InlineFuncArgumentType[F][an-1]:=atype;
            ArgumentHash.Add(aname,an-1);
        end;
    end;
    InlineFuncArgumentN[F]:=an;
    // a chain of the sort type name
    // arguments are parsed, we don't need return type, at least not yet...
    // let's mess with the contents...
    T:=0;
    SetLength(InlineFuncReplaceText[F],an+1); //often?
    SetLength(InlineFuncReplaceArgs[F],an); //often?

    start:=1;
    if(CompareLineToken('return',JHInput.Get(c),j)) then start:=j;


    L:=GetEndOfUsefulLine(JHInput.Get(c))-1;
    SkipUselessBrackets(JHInput.Get(c),start,L);
    j:=start;
    InlineFuncContents[F]:=Copy(JHInput.Get(c),start,L-start+1);


    nonarg:='';

    while(j<=L) do begin

        k:=j;
        while(k<=L) and (JHInput.Get(c)[k] in SEPARATORS) do begin
            if(JHInput.Get(c)[k]='"') then begin //handle strings...
                k:=k+1;
                ctr:=false;
                while(k<=L) do begin
                    if(ctr) then ctr:=false
                    else if(JHInput.Get(c)[k]='"') then break
                    else if(JHInput.Get(c)[k]='\') then ctr:=true;
                    k:=k+1;
                end;
                k:=k+1;
            end else if(JHInput.Get(c)[k]='''') then begin //handle "rawcodes"...
                if(k+2<=L) and (JHInput.Get(c)[k+2]='''')then k:=k+3
                else k:=k+6;
            end else k:=k+1;
        end;
        if(k>L) then begin
            nonarg:=nonarg+Copy(JHInput.Get(c),j,L-j+1);
            break;
        end;
        GetLineToken(JHInput.Get(c),tok,r,k);

        x:=ArgumentHash.ValueOf(tok);
        if(x<>-1) then begin
            nonarg:=nonarg+Copy(JHInput.Get(c),j,k-j);

            if(Length(InlineFuncReplaceArgs[F])<=T) then begin
                SetLength(InlineFuncReplaceText[F],T+6);
                SetLength(InlineFuncReplaceArgs[F],T+5);
            end;
            InlineFuncReplaceArgs[F][T]:=x;
            InlineFuncReplaceText[F][T]:=nonarg;
            nonarg:='';
            T:=T+1;

        end else begin
            nonarg:=nonarg+Copy(JHInput.Get(c),j,r-j);
        end;
        j:=r;

    end;
    ArgumentHash.Destroy;
    InlineFuncReplaceText[F][T]:=nonarg;
    InlineFuncReplaceN[F]:=T;
    //debug STUFF
    //nonarg:='';
    //for j := 0 to T do begin
    //    nonarg:=nonarg+'{'+InlineFuncReplaceText[F][j]+'}';
    //    if(j<>T) then nonarg:=nonarg+IntToStr(InlineFuncReplaceArgs[F][j]);
    //end;
    //JHInput.Get(c):=JHInput.Get(c)+' //'+nonarg;//' //This gets inlined!';
end;



//====================================================================================================
//memo-ized bracket-string handling:
//
var
   InlineBracketEnds: array of integer;
   TEMPDEBUG2:boolean=false;

function inline_dobracket(const s:string;const p:integer; i:integer; L:integer; bend:char):integer;
var j:integer;
ctr:boolean;
begin

    if(InlineBracketEnds[i]<>-1) then
    else if(bend='"') then begin
        j:=i+1;
        ctr:=false;
        while(j<=L-1) do begin
            if(ctr) then ctr:=false
            else if(s[j]='"') then break
            else if(s[j]='\') then ctr:=true;
            j:=j+1;
        end;
        InlineBracketEnds[i]:=j;
    end else if(bend='''') then begin
        j:=i;
        if(j+2<=L) and (s[j+2]='''') then j:=j+2
        else if(j+5>L) then j:=L
        else j:=j+5;
        InlineBracketEnds[i]:=j;


    end else begin
        j:=i+1;
        while(j<=L) and (s[j]<>bend) do begin
             if(s[j]='(') then j:=inline_dobracket(s,p,j,L,')')
             else if(s[j]='[') then j:=inline_dobracket(s,p,j,L,']')
             else if(s[j]='"') then j:=inline_dobracket(s,p,j,L,'"')
             else if(s[j]='''') then  j:=inline_dobracket(s,p,j,L,'''');
             j:=j+1;
        end;
        if(j>L) then raise JasserLineException(p,'Expected: '+bend);
        InlineBracketEnds[i]:=j;
    end;
    Result:=InlineBracketEnds[i];
end;

// This calls and prepares the ground for the above function.
procedure inline_dobracketStuff(const s:string;const p:integer; ini:integer; L:integer);
var i:integer;
begin

    if (L+1>Length(InlineBracketEnds)) then SetLength(InlineBracketEnds,L+(L div 2)+1);
    for i := ini to L do InlineBracketEnds[i]:=-1;
    i:=ini;
    while(i<=L) do begin
        
        if(s[i]='(') then i:=inline_dobracket(s,p,i,L,')')
        else if(s[i]='[') then i:=inline_dobracket(s,p,i,L,']')
        else if(s[i]='"') then i:=inline_dobracket(s,p,i,L,'"')
        else if(s[i]='''') then i:=inline_dobracket(s,p,i,L,'''');
        i:=i+1;
    end;

end;


//===========================================================================
// Is the function inlineable?
//
function Inline_IsInlineableFunc(const p:integer; const c:integer):boolean;
var start,j,k:integer;

    funcname,aname,atype,tok:string;
    ArgumentHash:TStringHash;
    L,an:integer;
    usedarguments:integer;

        function rec(a:integer; b:integer):boolean;
        var ri,rj,rk,rx:integer;
            rtok:string;
        begin
            result:=false;
            ri:=a;
            while(ri<=b) do begin
                while (ri<=b) and (JHInput.Get(c)[ri] in SEPARATORS) do begin
                    if (JHInput.Get(c)[ri]='"') then ri:=inlinebracketends[ri]
                    else if(JHInput.Get(c)[ri]='''') then ri:=inlinebracketends[ri];
                    ri:=ri+1;
                end;
                GetLineToken(JHInput.Get(c),rtok,ri,ri);
                if(ri>b) then begin
                    if(ri=b+1) then begin
                        rk:=ArgumentHash.ValueOf(rtok);
                        if(rk<>-1) then begin //an argument
                            if rk<>usedarguments then  exit;
                            usedarguments:=usedarguments+1;
                         end;
                    end;     
                    break;
                end;

                rk:=ArgumentHash.ValueOf(rtok);
                if(rk<>-1) then begin //an argument
                    if rk<>usedarguments then  exit;
                    usedarguments:=usedarguments+1;
                end else begin //probable function!
                    rk:=NoStateFunctionsHash.ValueOf(rtok);
                    if(rk=-1) then begin //probable bad function!
                        rj:=ri;
                        while (rj<b) and (( JHInput.Get(c)[rj]=' ') or ( JHInput.Get(c)[rj]=#9)) do rj:=rj+1;
                        if(JHInput.Get(c)[rj]='(') then begin
                           //bad function, bad!

                           rx:=inlinebracketends[rj];
                           if (not rec(rj+1,rx-1)) then exit;
                           if(an<>usedarguments) then  exit;
                           ri:=rx+1;
                        end else if (JHInput.Get(c)[rj]='"') or (JHInput.Get(c)[rj]='''') then begin
                            ri:=inlinebracketends[rj]+1;
                            if(ri=0) then begin
                                //messageBox(0,'#','!',0);
                                raise Exception.Create(JHInput.Get(c)+#13#10#13#10'Something broke, report this bug. '+IntToStr(rj));
                            end;
                        end else begin
                            ri:=rj;
                        end;

                    end;
                end;
            end;
            result:=true;
        end;

begin
    result:=false;
    if(c<0) then exit;       //need a way to handle empty functions, *coming soon*

    j:=1;
    if(CompareLineWord('constant',JHInput.Get(p),k)) then j:=k;
    CompareLineWord('function',JHInput.Get(p),j,j);
    GetLineWord(JHInput.Get(p),funcname,j,j);

    if(not CompareLineWord('takes',JHInput.Get(p),j,j)) then begin
        raise JasserLineException(p,'Expected: "takes"');
    end;
    //we'll just blatantly assume the syntax is all right...
    ArgumentHash:=TStringHAsh.Create;
    an:=0;
    if (CompareLineWord('nothing',JHInput.Get(p),k,j)) then begin
        j:=k;
        CompareLineWord('returns',JHInput.Get(p),j,j);
        //uh, the function takes nothing, be happy?

    end else begin
        while(not CompareLineToken('returns',JHInput.Get(p),k,j)) do begin
            GetLineToken(JHInput.Get(p),atype,j,j);
            GetLineToken(JHInput.Get(p),aname,j,j);

            if(atype='') or (aname='') then break;
            an:=an+1;
            ArgumentHash.Add(aname,an-1);
        end;
    end;
    start:=1;
    if(CompareLineToken('return',JHInput.Get(c),j)) then start:=j;


    L:=GetEndOfUsefulLine(JHInput.Get(c))-1;
    SkipUselessBrackets(JHInput.Get(c),start,L);
    j:=start;
    usedarguments:=0;

    if (CompareLineWord('set',JHInput.Get(c),k,j) ) then begin
        GetLineToken(JHInput.Get(c),tok,k,k);
        if(ArgumentHash.ValueOf(tok)<>-1) then begin
            result:=false;
            ArgumentHash.Destroy;
            exit;
        end;
    end;

TEMPDEBUG2:=true;
    inline_dobracketStuff(JHInput.Get(c),c,j,L);
TEMPDEBUG2:=false;
    //nline_dobracketStuff(JHInput.Get(c),c,1,length(JHInput.Get(c)));
    result:=rec(j,L);
    result:=result and (usedarguments=an);

    ArgumentHash.Destroy;


end;

//=======================================================================
// process a line, based on InlineFuncReplace data (does the inlining)

//==============================================================================
// That the function is marked as inlineable is not enough, sometimes
// it also depends on how you call it.
//
function canInlineInstance(const F:integer; argcontents:array of string; incall:boolean):boolean;
var k,i,L,par:integer;
s:string;
begin
    result:=false;
    if(incall) then begin //is it good for a call?
        s:=InlineFuncContents[F];
        if(length(s)=0) then begin result:=true;exit; end;
        //contents ought not to have any initial bracket
        if(s[1]='(') then exit;
        if(compareLineWord('call',s,k) or compareLineWord('set',s,k)) then begin
            result:=true;
            exit;
        end;

        //is it a function call?
        i:=1; L:=Length(s);
        if(s[L]<>')') then exit;
        CompareLineToken('',s,i,i);
        if(i>L) then exit;
        while (i<L) and ((s[i]=' ') or(s[i]=#9)) do i:=i+1;
        if(s[i]<>'(') then exit;
        par:=0;
        k:=i+1;
        while(k<L) and (par>=0) do begin
            if(s[k]='(') or (s[k]='[') then par:=par+1
            else if (s[k]=')') or (s[k]=']') then par:=par-1
            else if(s[k]='"') or (s[k]='''') then SkipString(s,k,L-1);
            k:=k+1;
        end;
        if(par<>0) then exit;

    end;
    result:=true;
end;

//the one function that inlines functions:
// processes the stuff in [a,b]

var TEMP_DEBUG_INLINE:boolean;

//==============================================================================
function inlineMaximus(const s:string; const p:integer; const funcname:string; const a:integer; const b:integer):string;
var i,j,k,r,t,F,last,cN:integer;
argcontents:array of string;
tem,TEMFNAME:string;
incall:boolean;
whitespc:boolean;
begin
     result:='';
     i:=a;
     k:=a;
     while(i<=b) do begin
         if(s[i]='(') then begin
            r:=i-1;
            while(r>=a) and ((s[r]=' ') or (s[r]=#9)) do r:=r-1;
            if(r<a) or (s[r] in SEPARATORS) then i:=i+1
            else begin
                t:=r-1;
                while(t>=a) and not(s[t] in SEPARATORS) do t:=t-1;
                t:=t+1;
                if(s[t] in NUMBERS) then i:=i+1
                else begin //it is a function
                    TEMFNAME:=Copy(s,t,r-t+1);
                    F:=InlineFuncHash.ValueOf(Copy(s,t,r-t+1));
                    if(F=-1) then i:=i+1
                    else begin
                        //inlineable!
                        //Joy! Happiness...

                        incall:=false;
                        //verify if it is a function call... (begins with call)
                        j:=t-1;
                        while(j>=a) and (s[j] in SEPARATORS) do j:=j-1;

                        //call is always a four letter word, for god's sake...
                        if(j-3>=a) and CompareSubString(s,j-3,j,'call') then begin
                            t:=j-3;
                            incall:=true;
                        end;

                        j:=i+1;
                        last:=j-1;
                        SetLength(argcontents,InlineFuncArgumentN[F]);//duh, it shouldn't go above that
                        cN:=0;
                        whitespc:=true;
                        while(j<=InlineBracketEnds[i]) do begin
                            if(s[j]<>' ') and (s[j]<>#9) and (s[j]<>')') then whitespc:=false;
                            if(s[j]='(') or(s[j]='[') or (s[j]='"') or(s[j]='''') then j:=InlineBracketEnds[j]
                            else if(s[j]=',') or (s[j]=')') then begin
                                if(cN=Length(argcontents)) and ((cN>1) or (not whitespc)) then begin
                                    //argument mismatch error, let a later phase popup it.
                                    result:=Copy(s,a,b-a+1);
                                    exit;
                                end;
                                if(whitespc and (cN=0) and (s[j]=')')) then
                                else begin
                                    argcontents[cN]:=InlineMaximus(s,p,funcname,last+1,j-1); //Copy(s,last+1,j-last-1);
                                    cN:=cN+1;
                                    last:=j;
                                end;
                            end;
                            j:=j+1;
                        end;
                        if(cN<InlineFuncArgumentN[F]) then begin
                            //argument mismatch error, let a later phase popup it.
                            result:=Copy(s,a,b-a+1);
                            exit;
                        end;

                        if(not canInlineInstance(F,argcontents,incall)) then i:=i+1
                        else begin
                            TEMP_DEBUG_INLINE:=true;
                            //do the actual deal!
                            //from k to t-1
                            result:=result+Copy(s,k,t-k);


                            if(not incall) then Result:=Result+'('
                            else begin
                                if(not(comparelinetoken('call',InlineFuncReplaceText[F][0],r)) and not(comparelinetoken('set',InlineFuncReplaceText[F][0],r)) ) then begin
                                    Result:='call ';
                                end;
                            end;
                            for j := 0 to InlineFuncReplaceN[F] do begin
                                result:=Result+InlineFuncReplaceText[F][j];
                                if(j<>InlineFuncReplaceN[F]) then begin
                                    if(InlineFuncArgumentType[F][InlineFuncReplaceArgs[F][j]]='real') then begin
                                        Concatenate3(tem,'((',argcontents[InlineFuncReplaceArgs[F][j]],')*1.0)');
                                    end else begin
                                        Concatenate3(tem,'(',argcontents[InlineFuncReplaceArgs[F][j]],')');
                                    end;
                                    Result:=Result+tem;
                                end;
                            end;
                            if(not incall) then Result:=Result+')';

                            k:=InlineBracketEnds[i]+1;
                            i:=k;
                        end;
                    end;
                end;
            end;

         end else  i:=i+1;

     end;
     Result:=Result+Copy(s,k,b-k+1);
end;

//==============================================================================
procedure inline_handle_line(var s:string; const p:integer; const funcname:string);
var L:integer;
begin
    L:=GetEndOfUsefulLine(s)-1;
    inline_DoBracketStuff(s,p,1,L);
    TEMP_DEBUG_INLINE:=false;
    s:=inlineMaximus(s,p,funcname,1,L)+Copy(s,L+1);
    if(TEMP_DEBUG_INLINE) then s:=s+' // INLINED!!';

    //s:=s+' //inlined!!?';
end;


//===========================================================================
// Process a function (line #p), does inline, and verifies if the function
// Can be inlined later.
procedure inline_handle_function( var p:integer; var output:string);
var
   fend,i,j,k,goodlines,content:integer;
   caninline:boolean;
   funcname:string;
   ctemp:string;

begin
    j:=1;
    if(CompareLineWord('constant',JHInput.Get(p),k)) then j:=k;
    CompareLineWord('function',JHInput.Get(p),j,j);
    GetLineWord(JHInput.Get(p),funcname,j,j);


    //caninline:=true;
    i:=p+1;
    goodlines:=0;
    content:=-1;
    while(i<inputN) do begin
        if(CompareLineWord('endfunction',JHInput.Get(i),j)) then break;
        if(CompareLineWord('function',JHInput.Get(i),j) or (CompareLineWord('constant',JHInput.Get(i),j) and CompareLineWord('function',JHInput.Get(i),j,j) ) ) then i:=inputN-1
        else if(not IsWhiteSpace(JHInput.Get(i))) then begin
            if(content=-1) then content:=i;

            ctemp := JHInput.Get(i);
            inline_handle_line(ctemp,i,funcname);
            JHInput.Rewrite(i, ctemp);
            goodlines:=goodlines+1
        end;
        i:=i+1;
    end;
    if(i>inputN)        then  raise JasserLineException(p,'Unclosed function.')
    else                  fend:=i;

    if(goodlines<>1) then caninline:=false
    else begin
         caninline:=Inline_IsInlineableFunc(p,content);
    end;

    if(goodlines<=2) then begin
        if ( inline_process_returnbugcandidate(p,content) ) then begin
            caninline := false;
        end;
    end;

    if(caninline) then begin
        inline_process_inleanable(p, content);

    end;


    for i := p to fend do begin
        SWriteLn(output,JHInput.Get(i));
    end;
    p:=fend;

end;

//========================================================================
procedure InlineInitExceptionsHash;
//Generated, add some natives considered harmless to the nostate hash.
begin
NoStateFunctionsHash.Add('Deg2Rad',1);
NoStateFunctionsHash.Add('Rad2Deg',1);
NoStateFunctionsHash.Add('Sin',1);
NoStateFunctionsHash.Add('Cos',1);
NoStateFunctionsHash.Add('Tan',1);
NoStateFunctionsHash.Add('Asin',1);
NoStateFunctionsHash.Add('Acos',1);
NoStateFunctionsHash.Add('Atan',1);
NoStateFunctionsHash.Add('Atan2',1);
NoStateFunctionsHash.Add('SquareRoot',1);
NoStateFunctionsHash.Add('Pow',1);
NoStateFunctionsHash.Add('I2R',1);
NoStateFunctionsHash.Add('R2I',1);
NoStateFunctionsHash.Add('I2S',1);
NoStateFunctionsHash.Add('R2S',1);
NoStateFunctionsHash.Add('R2SW',1);
NoStateFunctionsHash.Add('S2I',1);
NoStateFunctionsHash.Add('S2R',1);
NoStateFunctionsHash.Add('SubString',1);
NoStateFunctionsHash.Add('StringLength',1);
NoStateFunctionsHash.Add('StringCase',1);
NoStateFunctionsHash.Add('Player',1);
NoStateFunctionsHash.Add('GetPlayerId',1);
NoStateFunctionsHash.Add('GetUnitAbilityLevel',1);
NoStateFunctionsHash.Add('GetUnitUserData',1);
NoStateFunctionsHash.Add('GetHeroLevel',1);
NoStateFunctionsHash.Add('TimerGetElapsed',1);
NoStateFunctionsHash.Add('TimerGetRemaining',1);

NoStateFunctionsHash.Add('GetHandleId',1);
NoStateFunctionsHash.Add('StringHash',1);

NoStateFunctionsHash.Add('HaveStoredInteger', 1);
NoStateFunctionsHash.Add('HaveStoredReal', 1);
NoStateFunctionsHash.Add('HaveStoredBoolean', 1);
NoStateFunctionsHash.Add('HaveStoredUnit', 1);
NoStateFunctionsHash.Add('HaveStoredString', 1);
NoStateFunctionsHash.Add('GetStoredInteger', 1);
NoStateFunctionsHash.Add('GetStoredReal', 1);
NoStateFunctionsHash.Add('GetStoredBoolean', 1);
NoStateFunctionsHash.Add('GetStoredString', 1);
NoStateFunctionsHash.Add('LoadInteger', 1);
NoStateFunctionsHash.Add('LoadReal', 1);
NoStateFunctionsHash.Add('LoadBoolean', 1);
NoStateFunctionsHash.Add('LoadStr', 1);
NoStateFunctionsHash.Add('LoadPlayerHandle', 1);
NoStateFunctionsHash.Add('LoadWidgetHandle', 1);
NoStateFunctionsHash.Add('LoadDestructableHandle', 1);
NoStateFunctionsHash.Add('LoadItemHandle', 1);
NoStateFunctionsHash.Add('LoadUnitHandle', 1);
NoStateFunctionsHash.Add('LoadAbilityHandle', 1);
NoStateFunctionsHash.Add('LoadTimerHandle', 1);
NoStateFunctionsHash.Add('LoadTriggerHandle', 1);
NoStateFunctionsHash.Add('LoadTriggerConditionHandle', 1);
NoStateFunctionsHash.Add('LoadTriggerActionHandle', 1);
NoStateFunctionsHash.Add('LoadTriggerEventHandle', 1);
NoStateFunctionsHash.Add('LoadForceHandle', 1);
NoStateFunctionsHash.Add('LoadGroupHandle', 1);
NoStateFunctionsHash.Add('LoadLocationHandle', 1);
NoStateFunctionsHash.Add('LoadRectHandle', 1);
NoStateFunctionsHash.Add('LoadBooleanExprHandle', 1);
NoStateFunctionsHash.Add('LoadSoundHandle', 1);
NoStateFunctionsHash.Add('LoadEffectHandle', 1);
NoStateFunctionsHash.Add('LoadUnitPoolHandle', 1);
NoStateFunctionsHash.Add('LoadItemPoolHandle', 1);
NoStateFunctionsHash.Add('LoadQuestHandle', 1);
NoStateFunctionsHash.Add('LoadQuestItemHandle', 1);
NoStateFunctionsHash.Add('LoadDefeatConditionHandle', 1);
NoStateFunctionsHash.Add('LoadTimerDialogHandle', 1);
NoStateFunctionsHash.Add('LoadLeaderboardHandle', 1);
NoStateFunctionsHash.Add('LoadMultiboardHandle', 1);
NoStateFunctionsHash.Add('LoadMultiboardItemHandle', 1);
NoStateFunctionsHash.Add('LoadTrackableHandle', 1);
NoStateFunctionsHash.Add('LoadDialogHandle', 1);
NoStateFunctionsHash.Add('LoadButtonHandle', 1);
NoStateFunctionsHash.Add('LoadTextTagHandle', 1);
NoStateFunctionsHash.Add('LoadLightningHandle', 1);
NoStateFunctionsHash.Add('LoadImageHandle', 1);
NoStateFunctionsHash.Add('LoadUbersplatHandle', 1);
NoStateFunctionsHash.Add('LoadRegionHandle', 1);
NoStateFunctionsHash.Add('LoadFogStateHandle', 1);
NoStateFunctionsHash.Add('LoadFogModifierHandle', 1);
NoStateFunctionsHash.Add('LoadHashtableHandle', 1);

NoStateFunctionsHash.Add('HaveSavedInteger', 1);
NoStateFunctionsHash.Add('HaveSavedReal', 1);
NoStateFunctionsHash.Add('HaveSavedBoolean', 1);
NoStateFunctionsHash.Add('HaveSavedString', 1);
NoStateFunctionsHash.Add('HaveSavedHandle', 1);


end;

//==============================================================================
// More inliner stuff:
//==============================================================================
procedure InlineDo( var Result:string);
// var inlinefunc
 var i,j:integer;
     word:string;
     printit:boolean;
     globals:boolean;

begin
//nextperiod:=0;
//period:=0;

    InlineFuncHash:=TStringHash.Create;
    NoStateFunctionsHash:=TStringHash.Create;
    InlineInitExceptionsHash;
    InlineResizeArrays(5);
    InlineFuncN:=0;

 try
        //ProMax(ln);
        //ProPosition(0);
        //ProStatus('  Processing:');
        //period:= ln div UPDATEVALUE +1 ;
        //nextperiod:=period;

    Result:='';
    // final script rumble, normalized! There are only two things we can expect, the first globals block
    // and a bunch of functions, everything else is probably a syntax error, but it might be a better idea
    // to let PJASS handle it, so we try to ignore it.

    //by this time, unclosed function/globals errors should have been caught by libraries or struct parser
    i:=0;
    globals:=false;
    while(i<inputN) do begin
        //ProPosition(i/inputN);
        //if (i>=nextperiod) then begin

        //    nextperiod:=i+period;
        //end;

        printit:=true;
        if(not IsWhitespace(JHInput.Get(i))) then begin
            GetLineWord(JHInput.Get(i),word,j);
            if(globals) then begin
                if(word='endglobals') then globals:=false;
            end else if(word='globals') then globals:=true
            else begin
                //[constant] function?
                if((word='constant') and (compareLineWord('function',JHInput.Get(i),j,j))) or (word='function') then begin
                    printit:=false;
                    inline_handle_function(i,Result);
                end;
            end;
        end;

        if(printit) then SWriteLn(Result,JHInput.Get(i));
        i:=i+1;
    end;
    //ProEnd();
 finally
     InlineFuncHash.Destroy;
     NoStateFunctionsHash.Destroy;
 end;


end;

//==============================================================================
function Run(sinput:string):string;
begin
    ProStatus('Function Inliner: ');
    Result:='ERROR';

    JHInput.Reset2();
    JHInput.Load(sinput);

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

    JHinput.ClearOffsets();

    InlineDo(Result);
    ProEnd();
end;

//==============================================================================
{*
procedure Run(const f1:string;const f2:string);overload
var
   ff2:textfile;
   bff2:boolean;
   i,o:string;
begin

    bff2:=false;
    try
        i := LoadFile(f1);
        o := Run(i);

        AssignFile(ff2,f2);bff2:=true;

        filemode:=fmOpenWrite;
        Rewrite(ff2);
        Write(ff2,o);
    finally

        if(bff2) then Close(ff2);
    end;
end;
*}

end.
