//==============================================================================
//  This is only needed for wc3 < 1.24b
//==============================================================================
unit ReturnFix;

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

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

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

////////////////////////////////////////////////
// Return fixer begins here
var
    ReturnFixer_Globals: string;
    ReturnFixer_GlobalsHash: TStringHash;

//==============================================================================
procedure ReturnFixer_AddGlobal(const typename:string);
var
   tem:string;
begin
    if(ReturnFixer_GlobalsHash.ValueOf(typename) = -1) then begin
        ReturnFixer_GlobalsHash.Add(typename,1);
        Concatenate4(tem,'    ',typename,' rf__return_',typename);
        SWriteLn(ReturnFixer_Globals, tem);
    end;
end;

//==============================================================================
function ReturnFixer_ParseFunction( const fstart:integer; out fend:integer): boolean;
var
   constant:boolean;
   rec:boolean;
   typename:string;
   funcname,tok,tem:string;
   i,x, returncount:integer;
   ctemp : string;
begin
    ParseFunctionSignature( JHInput.Get(fstart), constant, funcname, x, typename);
    Result := (typename <> 'nothing');
    if(not result) then exit;

    returncount := 0;

    // find end of function, count return statments, also verify it isn't recursive...
    i:=fstart + 1;
    fend:=fstart+1;
    while (i<inputN) do begin

        if CompareLineWord('endfunction', JHInput.Get(i),x) then begin
            fend:=i;
            break;
        end;
        rec:=false;
        if    not(IsWhiteSpace(JHInput.Get(i)) ) then begin
          x:=1;
          while(x<=Length(JHInput.Get(i)) ) do begin
            GetLineToken(JHInput.Get(i),tok,x,x);
            if(tok ='return') then returncount:=returncount+1
            else if(tok=funcname) then rec:=true;
          end;
        end;
        if(rec) then begin
            tem:=JHInput.Get(i);

            ctemp := '';
            SWriteLn(ctemp,tem);
            SWriteLn(ctemp,'//Recursion + many return statements found, if you see a compile error in the next lines');
            SWriteLn(ctemp,'//Then you should really, really fix this function as it is probably a return bug false possitive. ');
            SWriteLn(ctemp,'//If you are sure it is fine, and want to be able to compile your map, disable the "return bug fix"');
            JHInput.Rewrite(i,ctemp);

        end;

        i:=i+1;
    end;
    result:= ( (fend<>-1) and (returncount>1) { and not(recursive)} );

end;

//==============================================================================
function FirstNoWhiteSpace( const s:string; const x:integer=1): integer;
begin
    Result:=x;
    while ( (Result<=Length(s)) and (s[Result] in WHITESPACE_SEPARATORS ) ) do
        Result:=Result+1;
end;

//==============================================================================
procedure ReturnFixer_FixFunction( const fstart:integer; const fend:integer; var Result:string);
var
    constant:boolean;
    argn, i, x,y,z: integer;
    name, tem, returntype,takes,arguments, word: string;
    recpattern:boolean;
begin
    ParseFunctionSignature(JHInput.Get(fstart), constant, name, argn, returntype);

    //Add the global:
    ReturnFixer_AddGlobal(returntype);

    //Begin by writing the returns nothing function:
    //constant will be ignored (it is useless anyway, it has always been...)
    takes:='';
    arguments:='';
    if(argn=0) then takes := 'nothing'
    else for i := 0 to argn-1 do begin
        if(i=argn-1) then begin
            Concatenate4(tem,takes,ParseFunctionSignature_ArgTypesBuf[i],' ',ParseFunctionSignature_ArgNamesBuf[i]);
            arguments:= arguments+ParseFunctionSignature_ArgNamesBuf[i];
        end else begin
            Concatenate3(tem,arguments, ParseFunctionSignature_ArgNamesBuf[i],',');
            arguments:=tem;
            Concatenate5(tem,takes,ParseFunctionSignature_ArgTypesBuf[i],' ',ParseFunctionSignature_ArgNamesBuf[i],',');
        end;
        takes := tem;
    end;
    Concatenate5(tem,'function rf__',name,' takes ', takes, ' returns nothing');
    SWriteLn(Result,tem);

    //Now write the 'fixed' contents of the function
    for i := fstart+1 to fend - 1 do  begin

        recpattern:=false;
        //recursion pattern A:
        // set foo = funcname(...
        if CompareLineWord('set', JHInput.Get(i), y) then begin
             GetLineToken(JHInput.Get(i), word, y,y);
             y:=FirstNoWhiteSpace(JHInput.Get(i),y);
             if (y<=Length(JHInput.Get(i))) and (JHInput.Get(i)[y]='=') and CompareLineToken(name,JHInput.Get(i),z,y) then begin
                 recpattern:= true;
                 SWriteLn(Result, 'call rf__'+Copy(JHInput.Get(i),z-Length(name), Length(JHInput.Get(i)) ) );
                 SWriteLn(Result, 'set '+word+'=rf__return_'+returntype);
             end;
        end;

        //recursion pattern B:
        // call funcname(...
        if CompareLineWord('call', JHInput.Get(i), y) then begin
             if (y<=Length(JHInput.Get(i))) and CompareLineToken(name,JHInput.Get(i),z,y) then begin
                 recpattern:= true;
                 SWriteLn(Result, 'call rf__'+Copy(JHInput.Get(i),z-Length(name), Length(JHInput.Get(i)) ) );
             end;
        end;

        //recursion pattern C:
        // return funcname(...
        if CompareLineWord('return', JHInput.Get(i), y) then begin
             if (y<=Length(JHInput.Get(i))) and CompareLineToken(name,JHInput.Get(i),z,y) then begin
                 recpattern:= true;
                 SWriteLn(Result, 'call rf__'+Copy(JHInput.Get(i),z-Length(name), Length(JHInput.Get(i)) ) );
                 SWriteLn(Result, 'return');
             end;
        end;



        if(recpattern) then
        else if(   not(IsWhiteSpace(JHInput.Get(i)) ) and CompareLineToken('return', JHInput.Get(i), x) ) then begin
            Concatenate4(tem,'set rf__return_',returntype,'=', Copy(JHInput.Get(i),x, Length(JHInput.Get(i))-x+1));
            SWriteLn(Result, tem);
            SWriteLn(Result, 'return');
        end else SWriteLn(Result, JHInput.Get(i));
    end;

    SWriteLn(Result, 'endfunction');

    // Replace the function's contents with #
    for i := fstart to fend do JHInput.Rewrite(i,'#');

    // Write a dummy function:
    Concatenate6(tem,'function ',name,' takes ', takes, ' returns ', returntype);
    SWriteLn(Result,tem);
    Concatenate5(tem,'    call rf__',name,'(', arguments, ')');
    SWriteLn(Result,tem);
    SWriteLn(Result,'    return rf__return_'+returntype);
    SWriteLn(Result,'endfunction');

    //


end;

//==============================================================================
procedure ReturnFixer_HandleFunction( const fstart: integer; var Result:string);
 var
    fend:integer;

begin
    //Parse the function, should we do it?
    if (  ReturnFixer_ParseFunction(fstart, fend) ) then
        ReturnFixer_FixFunction(fstart, fend, Result);



end;

//==============================================================================
procedure ReturnFixDo( var Result:string);
// var inlinefunc
 var i,j:integer;
     tem,word, insideGlobals:string;
     printit:boolean;
     globals:boolean;

begin

 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;
    insideglobals:='';
    ReturnFixer_GlobalsHash:= TStringHash.Create;
    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
                printit:=false;
                if(word='endglobals') then globals:=false
                else SWriteLn(InsideGlobals, JHInput.Get(i));
            end else if(word='globals') then begin
                globals:=true ;
                printit:=false;
            end
            else begin
                //[constant] function?
                if((word='constant') and (compareLineWord('function',JHInput.Get(i),j,j))) or (word='function') then begin
                    ReturnFixer_HandleFunction(i, Result);
            //        printit:=false;
                    //JHInput.Get(i) := JHInput.Get(i);
                    //inline_handle_function(i,Result);
                end;
            end;

        end;

        if(printit and (JHInput.Get(i)<>'#') ) then SWriteLn(Result,JHInput.Get(i));
        i:=i+1;
    end;
    tem := '';
    SWriteLn(tem, 'globals');
    SWriteLn(tem, insideglobals);
    SWriteLn(tem, ReturnFixer_Globals);
    SWriteLn(tem, 'endglobals');
    SWriteLn(tem, Result);
    Result:=tem;


 finally
     ReturnFixer_GlobalsHash.Destroy;
 end;


end;


//==============================================================================
function Run(const sinput:string):String;
begin
    ProStatus('Return Fixer (wc3 < 1.24b):');
    Result:='ERROR';

    JHInput.Reset2;
    JHInput.Load(sinput);

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

    JHinput.ClearOffsets();

    ReturnFixDo(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.
