//==============================================================================
// Module magic, a phase before main struct stuff,
// does text-manip-related struct features
// input: input array,
// output : processed input array.
//==============================================================================
unit ModuleMagic;

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

procedure DoStructModuleMagic();

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


var modoutput: array of String;
var modline: array of Integer;
var modtrace: array of Integer;
type TModule = class
    name:string;
    decl, declend:integer;
    ImplementedStructId: integer;
    onInit:string;
    privateMembers:Tstringhash;
end;


//==============================================================================
function ReplaceIdentifier(const s:string; const orig:string; const newt:string):string;
var
    i,L,k:integer;
    inraw,instring,ctr, incom,add:boolean;
//    IDENTIFCHARS

begin
    instring:=false; ctr:=false;
    inraw:=false;
    incom:=false;
    Result:='';
    L:=Length(s);
    i:=1;
    while(i<=L) do begin
        add:=true;
        if(incom) then
        else if( inraw) then begin
            if( s[i]='''') then inraw:=false;
        end else if (instring) then begin
            if(ctr) then ctr:=false
            else if(s[i] = '\') then ctr:=true
            else if( s[i]='"') then instring:=false;
        end else if(s[i]='/') and (i<L) and (s[i+1]='/')  then
            incom:=true
        else if (s[i] in IDENTIFCHARS) then begin
            k:=i+1;
            while( (k<=L) and (s[k] in IDENTIFCHARS) ) do k:=k+1;
            if(CompareSubString(s,i,k-1, orig) ) then
                Insert(newt,result,VERYBIG)
            else
                Insert(Copy(s,i,k-i),result,VERYBIG);
            add:=false;
            i:=k-1;
        end;
        if(add) then
            Insert(s[i],result,VERYBIG);
        i:=i+1;
    end;


end;

//==============================================================================
function ReplaceIdentifiersByHash(const s:string; var hash:TStringHash; const prefix:string):string;
var
    i,L,k:integer;
    inraw,instring,ctr, incom,add:boolean;
    tem:string;
//    IDENTIFCHARS

begin
    instring:=false; ctr:=false;
    inraw:=false;
    incom:=false;
    Result:='';
    L:=Length(s);
    i:=1;
    while(i<=L) do begin
        add:=true;
        if(incom) then
        else if( inraw) then begin
            if( s[i]='''') then inraw:=false;
        end else if (instring) then begin
            if(ctr) then ctr:=false
            else if(s[i] = '\') then ctr:=true
            else if( s[i]='"') then instring:=false;
        end else if(s[i]='/') and (i<L) and (s[i+1]='/')  then
            incom:=true
        else if (s[i] in IDENTIFCHARS) then begin
            k:=i+1;
            while( (k<=L) and (s[k] in IDENTIFCHARS) ) do k:=k+1;
            tem:=Copy(s,i,k-i);
            if( hash.ValueOf(tem)<>-1 ) then
                Insert(prefix+tem,result,VERYBIG)
            else
                Insert(tem,result,VERYBIG);
            add:=false;
            i:=k-1;
        end;
        if(add) then
            Insert(s[i],result,VERYBIG);
        i:=i+1;
    end;


end;



//==============================================================================
procedure DoStructModuleMagic();
var
   i,j,x,oln:integer;
   ModuleHash:TStringHash;
   ModuleN:integer;
   Modules: array of TModule;
   cmodule: TModule;
   modname, privateprefix:string;
   structopen,addit,optional:boolean;
   structname:string;
   structid:integer;
   word:string;

      function replaceThisType(const s:string):string;
      begin
          if(not structopen) then
             Result:=s
          else
              Result:=ReplaceIdentifier(s,'thistype',structname);
      end;

      procedure writeOutputLine( const s:string; fromline:integer; trace:integer);
      begin
          if Length(modoutput) <= oln then begin
              SetLength(modoutput, oln+20 + (oln div 5) );
              SetLength(modline, Length(modoutput) );
              SetLength(modtrace, Length(modoutput) );
          end;

          modoutput[oln]:=Replacethistype(s);
          modline[oln]:=fromline;
          modtrace[oln]:=trace;
          oln:=oln+1;
      end;

      procedure implementModule( const cmod: TModule; fromline:integer);
      var i,j,x:integer;
          optional:boolean;
          modname: String;
      begin
          if(cmod.implementedStructId = structid) then exit;
          cmod.implementedStructId := structid;
          for i := cmod.decl+1 to cmod.declend - 1 do begin
              if(CompareLineWord('implement',JHInput.Get(i),x) ) then begin
                  GetLineWord(JHInput.Get(i),modname,x,x);
                  optional:=false;
                  if(modname='optional') then begin
                      optional:=true;
                      GetLineWord(JHInput.Get(i),modname,x,x);
                  end;
                  VerifyEndOfLine(JHInput.Get(i),x,i);
                  j:=ModuleHash.ValueOf(modname);
                  if(j<0) then begin
                      if (not optional) then
                          raise JasserLineException(i,'Could not find module: '+modname);
                  end else begin
                       implementModule(Modules[j],i);
                  end;
              end else writeOutputLine(JHInput.Get(i), i, fromline);
          end;
          if(cmod.onInit<>'') then WriteOutputLine('//! ModuleOnInit '+cmod.onInit,cmod.decl, fromline);
      end;

      procedure parsePrivate( const line:string; x:integer; var cmodule:Tmodule);
       var
           word:string;
      begin
          GetLineWord(line,word,x,x);
          if(word='static') then begin
              GetLineWord(line,word,x,x);
          end;
          if(word='delegate') then begin
              GetLineWord(line,word,x,x);
          end;
          if(word='constant') then begin
              GetLineWord(line,word,x,x);
          end;

          if(word='method') then begin
              GetLineToken(line,word,x,x);
              if(word='operator') then
                  raise JasserLineException(j,'module private operators are not supported');

              if(word='onInit') then
                  cmodule.onInit:=privateprefix+'onInit';
              if(word='onDestroy') or {(word='onInit') or} (word='create') or (word='destroy') then
                  exit;
                  //raise JasserLineException(j,'private '+word+' not supported yet in modules.');

              cmodule.privateMembers.Add(word,1);

          end else begin
               GetLineToken(line,word,x,x);
               if(word='array') then
                   GetLineToken(line,word,x,x);
               cmodule.privateMembers.Add(word,1);
          end;

      end;
      procedure processPrivate( var line:string; var cmodule:Tmodule);
      begin
           line:=ReplaceIdentifiersByHash(line, cmodule.privateMembers, privateprefix);

      end;



begin
    ProStatus('  Modules: ');


    ModuleHash:=TStringHash.Create;
    ModuleN:=0;
    SetLength(modoutput, ((JHInput.StorageLength()*12) div 10) );
    SetLength(modline, Length(modoutput) );
    SetLength(modtrace, Length(modoutput) );



    //hmnnn:
    //* find modules, store them in array?
    //* find "implement" cnp modules.
    //* replace thisstruct with struct name?

    //--------------------------------------------------------------------------
    //parse Modules
    i:=0;
    while(i<inputN) do begin
        //ProPosition(i/inputN);

        if( CompareLineWord('module',JHInput.Get(i),x) ) then begin
            GetLineWord(JHInput.Get(i),modname,x,x);
            if( not ValidIdentifierName(modname) ) then
                raise JasserLineException(i,'Invalid identifier name: '+modname);
            VerifyEndOfLine(JHInput.Get(i),x, i);
            if (Length(Modules) <= ModuleN) then
               SetLength(Modules, ModuleN+5+ModuleN div 5);
            cmodule := TModule.Create;
            cmodule.onInit:='';
            cmodule.name := modname;
            cmodule.implementedStructId := -1;
            cmodule.privateMembers:=TStringHash.create;
            Modules[ModuleN]:=cmodule;
            ModuleHash.Add(modname, ModuleN);
            ModuleN:=ModuleN+1;
            j:=i+1;
            cmodule.decl := i;
            cmodule.declend := -1;
            while(j<inputN) do begin
                if(CompareLineWord('endmodule',JHInput.Get(j), x) ) then begin
                    cmodule.declend:=j;
                    break;
                end;
                j:=j+1;
            end;

            if(cmodule.declend < 0) then
                 raise JasserLineException(i,'Could not find endmodule.');
            privateprefix:=cmodule.name+GetPrivatePrefix();

            //detect private things:
            for j := cmodule.decl+1 to cmodule.declend-1 do begin
                if(CompareLineWord('private',JHInput.Get(j), x) ) then
                    parsePrivate(JHInput.Get(j), x, cmodule);
                cmodule.privateMembers.Add('',-1);
            end;
            for j := cmodule.decl+1 to cmodule.declend-1 do begin
                word := JHInput.Get(j);
                processPrivate(word, cmodule);
                JHInput.Rewrite(j, word);
            end;


            i:=cmodule.declend+1;
        end
        else i:=i+1;
    end;

    //--------------------------------------------------------------------------
    //implement modules:
    i:=0;
    oln:=0;
    structopen:=false;
    structname:='';
    structid:=-9222; //Magic number!
    while(i<inputN) do begin
        //ProPosition(i/inputN);
        addit:=true;
        if( CompareLineWord('module',JHInput.Get(i),x) ) then begin
            GetLineWord(JHInput.Get(i),modname,x,x);
            j:=ModuleHash.ValueOf(modname);
            if( j<0) then
                raise JasserLineException(i, '[internal error], report it.');
            i:=Modules[j].declend;
            addit:=false;
        end else if(CompareLineWord('struct',JHInput.Get(i),x) ) then begin
            structid:=i;
            structopen:=true;
            GetLineToken(JHInput.Get(i),structname,x,x);
        end else if(CompareLineWord('endstruct',JHInput.Get(i),x) ) then
            structopen:=false
        else if( CompareLineWord('implement',JHInput.Get(i),x) ) then begin
            if(not structopen) then
                raise JasserLineException(i, 'Cannot call implement outside structs or modules');

            addit:=false;
            GetLineWord(JHInput.Get(i),modname,x,x);
            optional:=false;
            if(modname = 'optional') then begin
                optional:=true;
                GetLineWord(JHInput.Get(i),modname,x,x);
            end;
            VerifyEndOfLine(JHInput.Get(i),x, i);
            j := ModuleHash.ValueOf(modname);
            if ( (not optional) and (j<0) ) then
                raise JasserLineException(i,'Unable to find module: '+modname);
            if(j>=0) then begin
                writeOutputLine('//Implemented from module '+modname+':', i, -1);
                implementModule(Modules[j],i);
            end;
        end;

        if(addit) then
            writeOutputLine(JHInput.Get(i), i, -1);


        i:=i+1;
    end;

    JHInput.Reset2();
    for i := 0 to oln - 1 do begin
        JHinput.AddLineEx(modoutput[i], modline[i]-i, modtrace[i]+1);
    end;

    ProEnd();
end;


end.
