//==============================================================================
//  Does static if preprocessing
//==============================================================================
unit BlockMagic;

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

procedure DoJasserBlocksMagic();

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

//==============================================================================
// Code blocks magic!!111
const CONDBLOCKS_MAXDEPTH: integer = 50;
var CondBlocks_CurrentStruct: string='';
var
    CondBlocks_ConstantsHash: TStringHash;
    CondBlocks_Stack: array[0 .. 50{CONDBLOCKS_MAXDEPTH}] of integer;

//==============================================================================
procedure CondBlocks_ReadGlobal(const s:string; linenum:integer );
var
   x:integer;
   word, value:string;

begin
    //[constant] <type> [array] <name> [=value]
    if(CompareLineWord('constant', s, x) and CompareLineWord('boolean', s, x,x) ) then begin
        GetLineToken(s,word,x,x);
        if( not GetAssigment( s, value, x) ) then begin
            raise JasserLineException(linenum,'Expected: Assignment');
        end;
        value:= Trim(value);
        if( (value<>'true') and (value<>'false') and ValidIdentifierName(value) ) then begin
            x:=CondBlocks_ConstantsHash.ValueOf(value);
            if(x=0) then value:='false'
            else if(x=1) then value :='true';
        end;
        if(value='false') then
            CondBlocks_ConstantsHash.add(word,0)
        else if (value='true') then
            CondBlocks_ConstantsHash.add(word,1);
    end;
end;

//==============================================================================
procedure CondBlocks_ReadMethodExistence( const s:string; const sname:string; linenum:integer; const nstart:integer; const priv:boolean);
var
    name:string;
    i:integer;
begin
    GetLineWord(s,name,i, nstart);
    CondBlocks_ConstantsHash.add(sname+'@'+name+'.exists',1);
    if(not priv) then
        CondBlocks_ConstantsHash.add(sname+'.'+name+'.exists',1);
end;

//==============================================================================
procedure CondBlocks_ReadStaticMember(const s:string; const sname: string; linenum:integer );
var
   x:integer;
   word, value:string;
   priv:boolean;

begin
    //[constant] <type> [array] <name> [=value]
    x:=1;
    GetLineWord(s,word,x,x);
    priv:=false;
    if ( (word = 'private') or (word='public') ) then begin
        priv := (word = 'private');
        GetLineWord(s,word,x,x);
    end;

    if (word='static') and (CompareLineWord('constant', s, x,x) and CompareLineWord('boolean', s, x,x) ) then begin
        GetLineToken(s,word,x,x);
        if( not GetAssigment( s, value, x) ) then begin
            raise JasserLineException(linenum,'Expected: Assignment');
        end;
        value:= Trim(value);
        if( (value<>'true') and (value<>'false') and ValidIdentifierName(value) ) then begin
            x:=CondBlocks_ConstantsHash.ValueOf(value);
            if(x=0) then value:='false'
            else if(x=1) then value :='true';
        end;
        if(value='false') then
            CondBlocks_ConstantsHash.add(sname+'@'+word,0)
        else if (value='true') then
            CondBlocks_ConstantsHash.add(sname+'@'+word,1);
        if(not priv) then begin
            if(value='false') then
               CondBlocks_ConstantsHash.add(sname+'.'+word,0)
            else if (value='true') then
                CondBlocks_ConstantsHash.add(sname+'.'+word,1);
        end;

    end;
end;

//==============================================================================
function CondBlocks_AcceptedBlock(const s:string; const linenum:integer; const start:integer; debug:boolean):boolean;
var
   x,y, len:integer;
   nt, reqvalue:boolean;
   reqname,word, reqmember, reqmembermember:string;
begin

    //parse the requirement list :( this is hard :(
    len:=Length(s);
    Result:=true;
    x:=start;
    while(true) do begin
        GetLineWord(s,reqname,x,x);
        nt := (reqname='not');
        if(nt) then begin
             GetLineWord(s,reqname,x,x);
        end;
        reqmember:='';
        reqmembermember:='';
        for y := 1 to Length(reqname) do
            if(reqname[y]='.') then begin
                reqmember:=Copy(reqname,y+1, Length(reqname));
                reqname:=Copy(reqname,1, y-1);
                break;
            end;
        if(reqmember<>'') then
          for y := 1 to Length(reqmember) do
            if(reqmember[y]='.') then begin
                reqmembermember:=Copy(reqmember,y+1, Length(reqmember));
                reqmember:=Copy(reqmember,1, y-1);
                break;
            end;
        if( (reqmembermember<>'') and (reqmembermember<>'exists') ) then
             raise JasserLineException(linenum,'Oexists is supported so far.');
        if(reqmember<>'') then begin
            if( (reqname='') or not(ValidIdentifierName(reqname) ) or (not ValidIdentifierName(reqmember) ) )  then
                raise JasserLineException(linenum,'Expected: not, library name or constant boolean name but got "'+reqname+'.'+reqmember+'" instead');
        end else begin
            if (reqname='') or not(ValidIdentifierName(reqname) ) then
                raise JasserLineException(linenum,'Expected: not, library name or constant boolean name but got "'+reqname+'.'+reqmember+'" instead');
        end;

        if(reqmembermember<>'') then
            reqmember:=reqmember+'.'+reqmembermember;

        if(reqmember<>'') then begin
            if(reqname = CondBlocks_CurrentStruct ) then begin
                reqname:=reqname+'@'+reqmember;
            end else begin
                reqname:=reqname+'.'+reqmember;
            end;
        end;

        if( reqname='true') then
            reqvalue:=true
        else if(reqname='false') then
            reqvalue:=false
        else if(reqname='DEBUG_MODE') then begin
            reqvalue:=debug;
        end else begin
            reqvalue:=(CondBlocks_ConstantsHash.ValueOf(reqname)=1);
        end;
        if(reqvalue <> not nt) then Result:=false;

        GetLineWord(s,word,x,x);
        if(word = '') or (word='then' ) then begin

            if(not IsWhiteSpace(s,x) ) then
                raise JasserLineException(linenum,'Unexpected: '+Copy(s,x,len-x+1) );
            if( word='' ) then
                raise JasserLineException(linenum,'Expected: "then" ');

            break;
        end else if(word<>'and') then begin
            raise JasserLineException(linenum,'Unexpected: '+word );
        end;


    end;

end;

//==============================================================================
procedure DoJasserBlocksMagic();
// var inlinefunc
 var i,j,k,x,siz:integer;
     word,redo:string;
     printit, priv:boolean;
     globals:boolean;
     structname:string;
     debug:boolean;
     label MEH;


     procedure verifySupported(const s:string; const lin:integer);
      var
         i:integer;
     begin
         for i := 0 to  Length(s) do begin
             //beware of comments
             if (s[i] = '/') and (i<Length(s)) and (s[i+1] = '/') then
                 break;
             if(s[i] in SEPARATORS) and not(S[i] in [' ',#9,'.'] ) then
                 raise JasserLineException(lin,'Unexpected: "'+s[i]+'"');
         end;

     end;

begin
    debug := DEBUG_MODE();
 try
    //ProMax(ln);
    //ProPosition(0);
    ProStatus('  Conditional Blocks: ');

    //period:= ln div UPDATEVALUE +1 ;
    //nextperiod:=period;

    i:=0;
    globals:=false;
    structname:='';
    CondBlocks_CurrentStruct:=structname;
    CondBlocks_ConstantsHash:= TStringHash.Create;

    siz:=0;
    while(i<inputN) do begin
        //ProPosition(i/inputN);
        //if (i>=nextperiod) then begin

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

        if( (siz>0) and (CondBlocks_Stack[siz-1]<0) ) then
            printit:=false
        else
            printit:=true;

        if(not IsWhitespace(JHInput.Get(i))) then begin
            GetLineToken(JHInput.Get(i),word,j);
            if(globals) then begin

                if(word='endglobals') then begin
                    globals:=false ;
                end else begin
                    CondBlocks_ReadGlobal(JHInput.Get(i), i);
                end;
            end else if(word='struct') then begin
                GetLineToken(JHInput.Get(i),structname,j,j);
                CondBlocks_CurrentStruct:=structname;
            end else if(word='endstruct') then begin
                structname:='';
                CondBlocks_CurrentStruct:=structname;
            end else if(word='globals') then begin
                globals:=true ;
            end else if (structname<>'') then begin
                //This code will come back and haunt me.
                redo := word;
                k:=j;
                priv := (word='private');
                if priv or (word='public') then
                    GetLineWord(JHInput.Get(i), word, k,k);

                if (word='static')  then begin
                    GetLineWord(JHInput.Get(i), word, k, k );
                    if (word = 'constant') then begin
                        GetLineWord(JHInput.Get(i), word, k, k );
                        if word='boolean' then
                            CondBlocks_ReadStaticMember(JHInput.Get(i),structname, i)
                        else if word = 'method' then
                            CondBlocks_ReadMethodExistence(JHInput.Get(i),structname, i,k, priv);
                    end else if (word='method') then
                        CondBlocks_ReadMethodExistence(JHInput.Get(i),structname, i,k, priv);

                end else  if (word='method') then begin
                    CondBlocks_ReadMethodExistence(JHInput.Get(i),structname, i,k, priv);

                end else if(word ='constant') then begin
                    GetLineWord(JHInput.Get(i), word, k, k );
                    if( word ='method') then
                         CondBlocks_ReadMethodExistence(JHInput.Get(i),structname, i,k, priv);
                end;
                word:=redo;
                goto MEH;


            end else begin
                MEH:

                //-2 : static, remove
                //-1 : non-static, remove
                // 1 : non-static, keep
                // 2 : static, keep

                //block start/end?
                if(word='static') and CompareLineToken('if',JHInput.Get(i),j,j) then begin
                    JHInput.Rewrite(i, StringReplace(JHInput.Get(i),'(',' ',[]));
                    JHInput.Rewrite(i, StringReplace(JHInput.Get(i),')',' ',[]));
                    VerifySupported(JHInput.Get(i),i);

                    if(siz= CondBlocks_MAXDEPTH ) then
                        raise JasserLineException(i, 'Too many nested ifs (Max is '+IntToStr(siz) +')' );
                    if CondBlocks_AcceptedBlock(JHInput.Get(i),i,j, debug) and ( (siz=0) or (CondBlocks_Stack[siz-1]>0) ) then
                        CondBlocks_Stack[siz] := 2
                    else
                        CondBlocks_Stack[siz] := -2;
                    siz:=siz+1;
                    printit:=false;
                end else if(word='elseif') then begin
                    if(siz=0) then
                        raise JasserLineException(i,'Unexpected: elseif');
                    if (CondBlocks_Stack[siz-1] =2) or (CondBlocks_Stack[siz-1] =-2) then begin
                        if( CondBlocks_Stack[siz-1] = 2 ) then begin
                            CondBlocks_Stack[siz-1] := -3;
                        end else if( CondBlocks_Stack[siz-1] = -2 ) then begin
                            JHInput.Rewrite(i, StringReplace(JHInput.Get(i),'(',' ',[]));
                            JHInput.Rewrite(i, StringReplace(JHInput.Get(i),')',' ',[]));
                            VerifySupported(JHInput.Get(i),i);
                            if CondBlocks_AcceptedBlock(JHInput.Get(i),i,j, debug) and ( (siz<=1) or (CondBlocks_Stack[siz-2]>0) ) then begin
                                CondBlocks_Stack[siz-1] := 2
                            end else begin
                                CondBlocks_Stack[siz-1] := -2;
                            end;

                        end;
                        printit:=false;
                    end else begin
                        //do not change the stack's content...
                    end;

                end else if(word='else') then begin
                    if(siz=0) then
                        raise JasserLineException(i,'Unexpected: elseif');
                    if (CondBlocks_Stack[siz-1] =2) or (CondBlocks_Stack[siz-1] =-2) then begin
                        x:=1;
                        if(siz>1) then
                            x:=CondBlocks_Stack[siz-2];
                        if (x>=0) then
                            CondBlocks_Stack[siz-1]:=CondBlocks_Stack[siz-1]*-1;
                        printit:=false;

                    end;



                end else if(word='if') then begin
                    if(siz= CondBlocks_MAXDEPTH ) then
                        raise JasserLineException(i, 'Too many nested ifs (Max is '+IntToStr(siz) +')' );
                    if(not printit) then
                        CondBlocks_Stack[siz]:=-1
                    else
                        CondBlocks_Stack[siz]:=1;
                    siz:=siz+1;
                end else if (word='endif') then begin
                    if(siz=0) then raise JasserLineException(i, 'Unexpected: endif');
                    siz:=siz-1;
                    if(CondBlocks_Stack[siz]=2) or (CondBlocks_Stack[siz]=-2) then
                        printit:=false;
                end;

            end;

        end;

        if(printit and (JHInput.Get(i)<>'#') ) then begin
            if debug then begin
                k:=0;
                for j := 0 to siz - 1 do
                     if(CondBlocks_Stack[j]=2) or ( CondBLocks_Stack[j]=-2) then
                         k:=k+1;
                if(k>0)then
                    JHInput.Prepend('    ', i)
            end else
                DoNothing();

        end else begin
            if(debug) then
                JHInput.Prepend('//# ', i)
                
            else
                JHInput.Rewrite(i, '');
        end;
        i:=i+1;
    end;
    if(siz<>0) then raise JasserLineException(i, 'Missing: endblock');

    ProEnd();
 finally
     CondBlocks_ConstantsHash.Destroy;
 end;


end;

end.
