unit Translate;

//==============================================================================
//  INTERFACE
//==============================================================================
interface
uses JType, StringHash, JStruct;

var
    currentLibrary : string;

    ParseFunctionSignature_ArgNamesBuf:array of string;
    ParseFunctionSignature_ArgTypesBuf:array of string;

   StructList: TDynamicStructArray;
   TopSortedStructList: array of integer;
   StructN: integer;

   //globals for global big arrays:
   BigArrayN:integer;
   BigArrayHash:Tstringhash;
   BigArrayTypes: array of string;
   BigArrayNames: array of string;
   BigArraySizes: array of integer;
   BigArrayWidths: array of integer;
   BigArrayStructs: array of integer;
   BigArrayDeclLines: array of integer;




procedure ParseFunctionSignature( const s:string; out constant: boolean; out name:string; out argn:integer; out returntype:string);


procedure parseFunction(const s:string; decl:integer);
function translateDotMethod(const obj:string; const memb:string; const args:string; styp:Tvtype; var res:string; var typ:Tvtype;   fromstruct:integer; const pos:integer; const execute:boolean=false; const fromsuper:boolean=false; const evaluate:boolean=false ):boolean;
function translateDotVar(const obj:string; const memb:string; sid:integer; styp: Tvtype; var res:string; var typ:Tvtype;   fromstruct:integer; const pos:integer; const insetstatement:boolean; const assign:string; const assigntyp: Tvtype; var converttocall:boolean ):boolean;overload;
function translateDotVar(const obj:string; const memb:string; sid:integer; var res:string; var typ:Tvtype;   fromstruct:integer; const pos:integer; const insetstatement:boolean; const assign:string; const assigntyp: Tvtype; var converttocall:boolean ):boolean;overload;
function TranslateFunctionName(const f:string; var res:string; var typ:Tvtype):boolean;
function translateIndexSet( const obj:string; const sid:integer; const ind:string; const val:string;
          var res:string; var typ:Tvtype;  fromstruct:integer; const pos:integer):boolean;
function translateArray(const arr:string; const ind:string; sid:integer; var res:string; var typ:Tvtype;   fromstruct:integer; const pos:integer; const insetstatement:boolean; const allowgetstructindex:boolean=false):boolean;
function translateMethodCode( const clas:string; const meth:string; var res:string; fromstruct:integer; const pos:integer):boolean;
function translateLessThan( const m1:string; i1:integer; const m2:string; i2:integer; var res:string; fromstruct:integer; const pos:integer):boolean;
function translateEqualTo( const m1:string; i1:integer; const m2:string; i2:integer; var res:string; fromstruct:integer; const pos:integer; const negate:boolean):boolean;




//==============================================================================
//  IMPLEMENTATION
//==============================================================================
implementation
uses JHInput, JASSerException, Member, JFunction, TextOnly, TextCommon,
     Prototypes, Constants, SysUtils, jasshelperConfigFile;

//==============================================================================
// parses function header (does not parse body)
//==============================================================================
procedure parseFunction(const s:string; decl:integer);
var word:string;
    name:string;
    ret:string;
    k:integer;
    fun:Tfunction;
    consta:boolean;
    interf:boolean;

    a:integer;
    argsbuff:array of string;

    procedure addtype(const ss:string);
    begin
        if (Length(argsbuff)<=a) then SetLength(argsbuff,2*a+1);
        argsbuff[a]:=ss;
        a:=a+1;

    end;

begin
    //constant? function
    //yes I think we should support constant functions here huh?

    GetLineWord(s,word,k);
    consta:=(word='constant');
    if (consta) then GetLineWord(s,word,k,k);



    if(word<>'function') then raise JasserLineException(decl,'internal error (not a function???');

    GetLineWord(s,name,k,k);

    interf:=(name='interface');
    if(interf) then begin
        if(consta) then raise JasserLineException(decl,'Function interfaces do not use the constant prefix');
        GetLineWord(s,name,k,k);
        //set the Structhash so if an argument equals the function interface¡s name , the prototype works correctly 
        if(StructHash.ValueOf(name)<>-1) then raise JasserLineException(decl,'Identifier already in use: '+name);
        StructHash.Add(name,StructN+1);
    end else begin
        if(StructHash.ValueOf(name)<>-1) then raise JasserLineException(decl,'Identifier already in use: '+name);    
    end;
    if(FunctionHash.ValueOf(name)<>-1) then begin

        raise JasserLineDoubleException(decl,'Function redeclared: '+name,
               FunctionData[FunctionHash.ValueOf(name)].decl, '---- Previous declaration.' );
    end;


    if (not compareLineWord('takes',s,k,k)) then raise JasserLineException(decl,'Expected: "takes"');

    // collect arguments in argsbuff
    GetLineWord(s,word,k,k);
    a:=0;
    if (word<>'nothing') then begin
         //word holds first type

         repeat
            addtype(word);
            GetLineToken(s,word,k,k);
            GetLineToken(s,word,k,k);
            if(word='returns') then break;
            if(word='') then raise JasserLineException(decl,'Expected: "returns"');
         until (word='returns');

    end else if (not compareLineWord('returns',s,k,k)) then raise JasserLineException(decl,'Expected: "returns"');

    ret:='#_~';
    GetLineWord(s,ret,k,k);

    k:=GetFunctionPrototype(argsbuff,a,ret);

    if(interf) then begin
        StructN:=StructN+1;
        if(Length(StructList)<=StructN) then SetLength(StructList,StructN+1+StructN div 5);
        StructList[StructN]:=Tstruct.create(currentLibrary, decl,false);
        StructList[StructN].typeid:=StructN;
        StructList[StructN].FunctionInterfacePrototype:=k;
        StructList[StructN].name:=name;
        JHInput.Prepend('//processed: ', decl);

        input[decl]:=+input[decl];
        StructHash.Add(name,StructN);


    end else begin
        fun:=AddFunctionData(name,k,decl);
        fun.abuse:=false;
    end;
end;

//==============================================================================
procedure parsemethod(const s:string; var obj:string; var sid:integer; var memb:string);
var
   i,k,L:integer;
begin
    //s__obj_methodname
    //structname.getMethod(methodname, obj)
    i:=1;
    L:=Length(s);
    while(i<=L) and (s[i]<>'.') do i:=i+1;
    sid:=StructHash.ValueOf(Copy(s,1,i-1));
    if(sid<0) then raise JasserLineException(RecCurrentLine,'[internal error] Malformed method passing data, report this bug.'#13#10#13#10+s);

    while(i<=L) and (s[i]<>'(') do i:=i+1;
    i:=i+1;
    k:=i+1;
    while(k<=L) and (s[k]<>',') do k:=k+1;

    memb:=Copy(s,i,k-i);

    obj:=Copy(s,k+1,L-k-1);
end;


//==============================================================================
function translateDotFunctionName(const s:string; var res:string; var typ:Tvtype; fromstruct:integer; const pos:integer):boolean;
var obj,memb:string;
    sid:integer;
begin
    parseMethod(s,obj,sid,memb);
    res:='"s__'+StructList[sid].name+'_'+memb+'"';
    typ:=MakeType(-1);

    result:=true;
end;


//==============================================================================
function translateDotMethodPointer(const s:string; var res:string; var typ:Tvtype; fromstruct:integer; const pos:integer):boolean;
var obj,memb:string;
    //k:integer;
    smemb:Tmember;
    sid:integer;
begin
    parseMethod(s,obj,sid,memb);
    res:='s__'+StructList[sid].name+'_'+memb+'';
    result:=false;
    if not StructList[sid].getmember(memb, smemb) then begin
        res:='Cannot find member: '+memb;
        exit;
    end;
    {if not smemb.isstatic then begin
        res:=StructList[sid].name+'.'+memb+' must be static.';
        exit;
    end;

    }
    if(( StructList[sid].name <> obj ) and (obj<>'this') )then begin
        res:='Correct usage is : structname.methodname, non-static methods DO NOT pass the instance when used as function pointers.';
        result:=false;
        exit;
    end;
    obj:=res;
    if not structList[sid].isinterface and  (FunctionHash.ValueOf(obj)=-1) then begin
        if(smemb.decl = -1) then
            raise JasserLineDoubleException(reccurrentline,smemb.name+' is an internal method that cannot be used for function pointers.',
                                            reccurrentline,'\--- Perhaps you meant: '+smemb.name+'()');
        parseFunction(input[smemb.decl] , smemb.decl);
    end;

    result:=TranslateFunctionName(obj, res, typ);
    typ:=MakeType(METHODTYPE,'method',s);
end;

//==============================================================================
function translateDotMethodExists(const s:string; var res:string; var typ:Tvtype; fromstruct:integer; const pos:integer):boolean;
var obj,memb:string;
    sid:integer;
    k,r:integer;
    smemb:Tmember;

begin
    parseMethod(s,obj,sid,memb);
    k:=StructList[sid].membershash.ValueOf(memb);
    if(k=-1) then begin
        res:='[Internal error] The method is not a member!? report this bug';
        result:=false;
        exit;
    end;
    if(not StructList[sid].members[k].ismethod) then begin
        res:='[Internal error] The method is not a method!? report this bug';
        result:=false;
        exit;
    end;
    if(StructList[sid].members[k].fromparent or StructList[sid].isinterface) then begin
        r:=StructHash.ValueOf(obj);
        if(-1 <> r ) then begin
            {if( r<>k ) then begin
                res:='false';
            end else begin
                res:='true';
            end;}
            if( StructList[r].getmember(memb,smemb) ) then
                res:='true'
            else
                res:='false';

        end else begin
            res:='si__'+StructList[sid].GetSuperParentName(StructList)+'_type['+obj+']';
            res:='(st__'+StructList[sid].GetSuperParentName(StructList)+'_'+memb+'['+res+']!=null)';
        end;
    end else begin
        res:='true';
    end;
    typ:=MakeType(-1);

    result:=true;
end;


//==============================================================================
function translateDotVar(const obj:string; const memb:string; sid:integer; var res:string; var typ:Tvtype;   fromstruct:integer; const pos:integer; const insetstatement:boolean; const assign:string; const assigntyp: Tvtype; var converttocall:boolean ):boolean;overload;
begin
    resulT:=translateDotVar(obj,memb,sid,MakeType(sid),res,typ,fromstruct,pos,insetstatement,assign,assigntyp,converttocall);
end;

//==============================================================================
function translateDotVar(const obj:string; const memb:string; sid:integer; styp: Tvtype; var res:string; var typ:Tvtype;   fromstruct:integer; const pos:integer; const insetstatement:boolean; const assign:string; const assigntyp: Tvtype; var converttocall:boolean ):boolean;overload;
var
   tem:string;
   allowinstance:boolean;
   membd:Tmember;
   h:integer;


    function TryDelegate(const dmemb:Tmember):boolean;
     var typ2,typ3:Tvtype;
     var res2,res3:string;
     var converttocall3:boolean;
    begin
        if not translateDotVar(obj,dmemb.name,sid,styp,res2,typ2,{fromstruct}sid,pos,false,'',MakeType(0),converttocall) then begin
            raise Exception.Create('BAd internal error : '+res2);
        end;
        converttocall:=false;
        result:=false;
        if(translateDotVar(res2,memb,typ2.id,typ2,res3,typ3,fromstruct,pos,insetstatement,assign,assigntyp,converttocall3)) then begin
            result:=true;
            res:=res3;
            typ:=typ3;
            converttocall:=converttocall3;
        end;

    end;

begin
     converttocall:=false;

    {maybe later if(sid=FUNCTION_NAME_TYPE) then begin
        result:=translateDotVar(FunctionData[StrToInt(obj)].name,memb,-1,res,typ,fromstruct,pos,insetstatement,assign,assigntyp,converttocall);
        exit;
    end;}

    if (obj='') then begin//default
        if (fromstruct=-1) then begin
            res:='Unable to find default object ''.'' used without a preffix outside struct declaration';
            result:=false;exit;
        end;
        result:=translateDotVar('this',memb,fromstruct,MakeType(fromstruct),res,typ,fromstruct,pos,insetstatement,assign,assigntyp,converttocall);
        exit;
    end;
    h:=FunctionHash.ValueOf(obj);
    if(h<>-1) then begin
        result:=translateMemberOfFunction(obj,h,memb,res, typ);
        exit;

    end;


    allowinstance:=true;

    if(sid>=ARRAYEDTYPE) then begin
        res:=obj+' requires an index '+IntToStr(sid)+' ..';
        result:=false;
        exit;
    end;

    if(sid=METHODTYPE) then begin
        if(memb='name') then begin
           if(insetstatement) then begin
               result:=false;
               res:='.name is read-only';
               exit;
           end;

           translateDotFunctionName(styp.tag,res,typ,fromstruct,pos);
           result:=true;
           exit;
        end;
        if (memb='exists') then begin
           if(insetstatement) then begin
               result:=false;
               res:='.exists is read-only';
               exit;
           end;
           if(styp.name = 'method') then
               translateDotMethodExists(styp.tag,res,typ,fromstruct,pos)
           else
               translateDotMethodExists(obj,res,typ,fromstruct,pos);
           result:=true;
           exit;

        end;

        if(memb='execute') then raise JasserLineException(RecCurrentLine,'Expected (argument list) after execute');
        if(memb='_pointer') then begin
           if(insetstatement) then begin
               result:=false;
               res:='._pointer is read-only';
               exit;
           end;
           result:=translateDotMethodPointer(styp.tag,res,typ,fromstruct,pos);
           exit;

        end;

        raise JasserLineException(RecCurrentLine,'method has two members: .name , .exists');
    end;

    if(sid=-1) then begin
        h:=BigArrayHash.ValueOf(obj);
        if(h<>-1) then begin
            typ:=MakeType(-1);
        
            //It's a big array.
            if(memb='size') then begin

                result:=true;
                res:=IntToStr(BigArraySizes[h]);
            end else if(memb='width') then begin
                if(BigArrayWidths[h]=-1) then begin
                    raise JasserLineException(RecCurrentLine,'.width invalid since the array is one-dimensional.');
                end;
                result:=true;
                res:=IntToStr(BigArraySizes[h] div BigArrayWidths[h]);


            end else if(memb='height') then begin
                if(BigArrayWidths[h]=-1) then begin
                    raise JasserLineException(RecCurrentLine,'.width invalid since the array is one-dimensional.');
                end;
                result:=true;
                res:=IntToStr(BigArrayWidths[h]);

            end else begin
                raise JasserLineException(RecCurrentLine,'vJass Arrays only accept fields: size, width, height');
            end;
            exit;
        end;
    end;

    if (sid=-1) then begin
        //could be an attempt to use a static member
        sid:=structhash.ValueOf(obj);
        if (sid=-1) then begin
            res:=obj+' is not of a type that allows . syntax';
            result:=false;exit;
        end;
        allowinstance:=false;
    end;
    if( StructList[sid].FunctionInterfacePrototype<>-1) then begin
        result:=translateMemberOfFunctionInterface(obj,StructList[sid],memb, res, typ);
        exit;
    end;

    if(memb='typeid') then begin
        typ:=MakeType(-1);

        res:='si__'+StructList[sid].name;
        result:=true;
        exit;
    end;

    h:=StructList[sid].membershash.ValueOf(memb);



    if( h=-1) then begin
       // "properties"
       if (insetstatement) then begin
          h:=StructList[sid].membershash.ValueOf('_set_'+memb);
          if(h<>-1) then begin
              if(StructList[sid].members[h].returntype=StructList[sid].name) then begin

                  if translateDotMethod(obj,'_set_'+memb,assign,styp,res,typ,fromstruct,pos,false) then begin
                      //Concatenate4(res,'set ',obj,'=',tem);
                      //That line up there is faster
                      // but probably causes bugs
                      res:='set '+obj+'='+res;
                      result:=true;
                  end else
                      result:=false;
              end else begin
                  converttocall:=true;
                  result:=translateDotMethod(obj,'_set_'+memb,assign,styp,res,typ,fromstruct,pos,false);
              end;
              exit;
          end;
       end else begin
           h:=StructList[sid].membershash.ValueOf('_get_'+memb);
           if(h<>-1) then begin
               result:=translateDotMethod(obj,'_get_'+memb,'',styp,res,typ,fromstruct,pos,false);
               exit;
           end;
       end;

       if (StructList[sid].parent<>-1) then begin
          result:=translateDotVar(obj,memb,StructList[sid].parent,res,typ,fromstruct,pos,insetstatement,assign,assigntyp,converttocall);
          //exit;
       end else if (StructList[sid].parentstruct<>-1) then begin
          result:=translateDotVar(obj,memb,StructList[sid].parentstruct,res,typ,fromstruct,pos,insetstatement,assign,assigntyp,converttocall);
          //exit;
       end else begin
           res:=memb+' is not a member of '+StructList[sid].name; result:=false;
       end;
       if(not result) then begin
           //Try delegation:
           for h := 0 to StructList[sid].delegateN-1 do begin
               if(TryDelegate(StructList[sid].delegates[h]) ) then begin
                    result:=true;
                    exit;
               end;
           end;
       end;
       exit;
    end;

    membd:=StructList[sid].members[h];
    if (membd.access=ACCESS_PRIVATE) and (fromstruct<>sid) then begin
        res:=StructList[sid].name+'.'+memb+' is private';result:=false;exit;
    end;
    if (insetstatement and (membd.access=ACCESS_READONLY) and (fromstruct<>sid) ) then begin
        res:=StructList[sid].name+'.'+memb+' is read-only';result:=false;exit;
    end;

    if(membd.ismethod) then begin
        Concatenate6(res,StructList[sid].name,'.getMethod(',memb,',',obj,')');
        typ:=MakeType(METHODTYPE, 'method', res); //method
        tem:=res;
        if (TranslateDotMethodPointer(res,tem,typ,fromstruct,pos)) then
            res:=tem;

        result:=true;
        exit;
    end;
    if(membd.isstatic) then begin
        //ooh a static member.

        concatenate4(res,'s__',StructList[sid].name,'_',memb);
        typ:=MakeType(StructHash.ValueOf(membd.returntype));

        if(typ.id<>-1) and (membd.isstaticarray) then typ:=MakeType(typ.id+ARRAYEDTYPE);

    end else if (not allowinstance) then begin
        res:=memb+' is not an static member of '+StructList[sid].name;
        result:=false;
        exit;
    end else begin
        //ooh an instance member

        if(StructList[sid].requiredspace>=JASS_ARRAY_SIZE) then begin

            if(insetstatement) then begin
                if(obj='') then
                    concatenate7(res,'sg__',StructList[sid].name,'_set_',memb,'(this,',assign,')')
                else
                    concatenate9(res,'sg__',StructList[sid].name,'_set_',memb,'(',obj,',',assign,')');
                typ:=MakeType(-1);

                converttocall:=true;
                result:=true;
                exit;
            end else if(obj='') then
                concatenate5(res,'sg__',StructList[sid].name,'_get_',memb,'(this)')
            else
                concatenate7(res,'sg__',StructList[sid].name,'_get_',memb,'(',obj,')');

        end else begin
            if(obj='') then
                concatenate5(res,'s__',StructList[sid].name,'_',memb,'[this]')
            else
                concatenate7(res,'s__',StructList[sid].name,'_',memb,'[',obj,']');
        end;
        typ:=MakeType(StructHash.ValueOf(membd.returntype));
    
    end;
    Result:=true;
end;

//==============================================================================
function translateDotExecuteMethod(const s:string; const args:string; var res:string; var typ:Tvtype;   fromstruct:integer; const pos:integer):boolean;
var obj,memb:string;
    sid:integer;
begin
    parseMethod(s,obj,sid,memb);
    result:= translateDotMethod(obj,memb,args,MakeType(sid),res,typ,fromstruct,pos,true);
end;

//==============================================================================
function translateDotEvaluateMethod(const s:string; const args:string; var res:string; var typ:Tvtype;   fromstruct:integer; const pos:integer):boolean;
var obj,memb:string;
    sid:integer;
begin
    parseMethod(s,obj,sid,memb);
    result:= translateDotMethod(obj,memb,args,MakeType(sid),res,typ,fromstruct,pos,false, false, true );
end;


//==============================================================================
function translateDotMethod(const obj:string; const memb:string; const args:string; styp:Tvtype; var res:string; var typ:Tvtype;   fromstruct:integer; const pos:integer; const execute:boolean=false; const fromSuper:boolean=false; const evaluate:boolean=false):boolean;
var
   allowinstance,usedestroy:boolean;
   membd, membd2:Tmember;
   h:integer;
   prefix:string;

    function TryDelegate(const dmemb:Tmember):boolean;
     var typ2,typ3:Tvtype;
     var res2,res3:string;
     var dummyvar:boolean;
    begin
        if not translateDotVar(obj,dmemb.name,styp.id, styp,res2,typ2,{fromstruct}styp.id,pos,false,'',MakeType(0),dummyvar) then begin
            raise Exception.Create('Bad internal error #2 : '+res2);
        end;
        result:=false;
        if(translateDotMethod(res2,memb,args,typ2,res3,typ3,fromstruct,pos,execute)) then begin
            result:=true;
            res:=res3;
            typ:=typ3;
        end;

    end;


begin
    if (obj='super') and (fromstruct<>-1) then begin//default

        result:=translateDotMethod('this',memb,args,MakeType(fromstruct),res,typ,fromstruct,pos,execute,true);
        exit;
    end;


    if (obj='') then begin//default

        if (fromstruct=-1) then begin
            res:='Unable to find default object ''.'' used without a preffix outside struct declaration';
            result:=false;exit;
        end;
        result:=translateDotMethod('this',memb,args,makeType(fromstruct),res,typ,fromstruct,pos);
        exit;
    end;

    h:=FunctionHash.ValueOf(obj);
    if(h<>-1) then begin
        result:=translateMethodOfFunction(obj,h,memb,args, res, typ);
        if(result) then exit;

    end;

    allowinstance:=true;

    if(styp.id>=ARRAYEDTYPE) then begin
        res:=obj+' requires an index';
        result:=false;
        exit;
    end;

    if(styp.id=METHODTYPE) then begin
        if(memb='evaluate') then begin
             result:=translateDotEvaluateMethod(styp.tag ,args,res,typ,fromstruct,pos);
             exit;
        end;
        if(memb<>'execute') then raise JasserLineException(RecCurrentLine, 'expected .evaluate, .execute or an struct variable instead of method.');

        result:=translateDotExecuteMethod(styp.tag,args,res,typ,fromstruct,pos);
        exit;
    end;

    if (styp.id=-1) then begin
        //could be an attempt to use a static member
        styp:=MakeType(structhash.ValueOf(obj));
        if (styp.id=-1) then begin
            res:=obj+' is not of a type that allows . syntax';
            result:=false;exit;
        end;
        allowinstance:=false;
    end;

    if( StructList[styp.id].FunctionInterfacePrototype<>-1) then begin
        result:=translateMethodOfFunctionPointer(obj,StructList[styp.id],memb,args, res, typ);
        exit;
    end;

    h:=StructList[styp.id].membershash.ValueOf(memb);
    if( h=-1) then begin

       if (memb='getType') {and StructList[sid].isinterface} then begin
          if(not allowinstance) then begin
              res:='getType is an instance method';
              result:=false;
              exit;
          end;
          if(args<>'') then begin
               res:='Unexpected arguments given to getType';
               result:=false;
               exit;
          end;
          if(StructList[styp.id].isInterface or StructList[styp.id].gotstructchildren) then begin
            if(StructList[styp.id].requiredspace>=JASS_ARRAY_SIZE) then begin
              Concatenate5(res,'si__',StructList[styp.id].GetSuperParentName(StructList),'_getType(',obj,')');
            end else begin
              Concatenate5(res,'si__',StructList[styp.id].GetSuperParentName(StructList),'_type[',obj,']');
            end;

          end else begin
             res:=IntToStr(styp.id);
          end;
          typ:=MakeType(-1);
          result:=true;
          exit;
       end;

       if (StructList[styp.id].parent<>-1) then begin
          result:=translateDotMethod(obj,memb,args,MakeType(StructList[styp.id].parent),res,typ,fromstruct,pos);
       end else if (StructList[styp.id].parentstruct<>-1) then begin
          result:=translateDotMethod(obj,memb,args,MakeType(StructList[styp.id].parentstruct),res,typ,fromstruct,pos,execute);
       end else begin
          if (memb='_lessthan') then begin
              res:=StructList[styp.id].name+' does not overload a < operator'; result:=false;
          end else begin
              res:=memb+' is not a member of '+StructList[styp.id].name; result:=false;
          end;
       end;
       if(not result) then begin
           for h := 0 to StructList[styp.id].delegateN-1 do begin
               if(TryDelegate(StructList[styp.id].delegates[h]) ) then begin
                    result:=true;
                    exit;
               end;
           end;
       end;
       exit;
    end;

    if(StructList[styp.id].forInternalUse) then begin
        Result:=false;
        Res:=StructList[styp.id].name+' does not allow methods';
        Exit;
    end;

    membd:=StructList[styp.id].members[h];
    if (membd.access=ACCESS_PRIVATE) and (fromstruct<>styp.id) then begin
        res:=StructList[styp.id].name+'.'+memb+' is private';result:=false;exit;
    end;

    if(not membd.ismethod) then begin res:=memb+' is not a method.'; result:=false; exit; end;

    if(membd.fromparent and StructList[styp.id].gotstructchildren) and not fromSuper then begin
         if(structList[styp.id].parent<>-1) then begin
             result:=TranslateDotMethod(obj,memb,args,MakeType(structlist[styp.id].parent),res,typ,fromstruct,pos,execute);
         end else if (structlist[styp.id].parentstruct<>-1) then begin
             result:=TranslateDotMethod(obj,memb,args,MakeType(structlist[styp.id].parentstruct),res,typ,fromstruct,pos,execute);
         end else begin
             res:='[internal error] member comes from parent but struct has no parent???!!';
             result:=false;
         end;
         exit;
    end;


    if ((membd.argnumber>0) and (args='')) then begin res:=memb+' requires more arguments'; result:=false; exit; end;

    if (execute) then  begin
        membd.abuseexec:=true;
        prefix:='sx__'
    end else if (membd.decl>pos) then begin
        if(     (not evaluate)
             and not jasshelperConfigFile.AUTO_METHOD_EVALUATE
             and not( (Length(membd.name)>=1) and (membd.name[1]='_') )
             and not ( membd.construct )
             and not ( membd.destruct )
          )

        then
            raise JasserLineDoubleException(pos,'Since you added [forcemethodevaluate] to jasshelper.conf, calling methods "from above their declaration" requires you to add .evaluate, correct this mistake.', pos, 'You may alternatively edit jasshelper.conf and remove that option.');
        membd.abuse:=true;
        prefix:='sc__';
    end else if(StructList[styp.id].isinterface) or (membd.stub) then prefix:='sc__'
    else prefix:='s__';

    if(fromSuper) then begin
        if(execute) then
             raise JasserLineException(pos,'Sorry, can''t use super and execute right now');
        if StructList[styp.id].parentStruct=-1 then
             raise JasserLineException(pos,'The struct has no parent, cannot use super');
        if not StructList[StructList[styp.id].parentStruct].getmember(membd.name,membd2) then
             raise JasserLineException(pos,'The parent struct has no member called '+membd.name);
        if (membd2.decl>pos) then
             raise JasserLineDoubleException(pos, 'Sorry, but for now super cannot call methods declared bellow the line.',membd.decl,'---- Parent''s method declared here.');

        if( args<>'') then
            Concatenate9(res,'s__',StructList[StructList[styp.id].parentStruct].name,'_',memb,'(',obj,',',args,')')
        else
            Concatenate7(res,'s__',StructList[StructList[styp.id].parentStruct].name,'_',memb,'(',obj,')');
        Result:=true;
        exit;
    end;



    if (membd.destruct) then begin
        usedestroy:=false;
        if( (styp.id<>fromstruct) and (membd.access=ACCESS_PRIVATE) ) then begin
            raise JasserLineException(RecCurrentLine,StructList[styp.id].name+'.'+membd.name+' is private.')
        end;

        if(StructList[styp.id].isarraystruct) then begin
            raise JasserLineException(RecCurrentLine,'Array structs cannot be destroyed.')
        end else if (execute) then
            raise JasserLineException(RecCurrentLine,'No reason to use .execute on destructor...')
        else if(StructList[styp.id].parent<>-1) then begin
            prefix:='sc__';
            styp:=MakeType( StructList[styp.id].parent );
        end else if(StructList[styp.id].parentstruct<>-1) then begin
            prefix:='sc__';
            while (StructList[styp.id].parentstruct<>-1) do begin
                styp:=MakeType( StructList[styp.id].parentstruct );
                if( StructList[styp.id].getmember('destroy',membd2) and not membd2.destruct ) then begin
                    if(membd2.decl > pos) then begin
                         membd2.abuse:=true;
                         prefix:='sc__';
                    end else begin
                         prefix:='s__';
                    end;
                    usedestroy:=true;
                    break;
                end;
            end;
            if (StructList[styp.id].parent<>-1) then styp:=MakeType(StructList[styp.id].parent);
        end else if(StructList[styp.id].gotstructchildren) then begin
            prefix:='sc__';
        end else begin
            {if StructList[sid].getmember('onDestroy',membd2) then begin
                if(membd2.decl > pos) then
                    prefix:='sc__'
                else
                    prefix:='s__';
            end else
                prefix:='s__';}
        end;
        if(usedestroy) then begin
            if( args<>'') then
                Concatenate7(res,prefix,StructList[styp.id].name,'_',{memb}'destroy','(',args,')')
            else
                Concatenate7(res,prefix,StructList[styp.id].name,'_',{memb}'destroy','(',obj,')');

        end else begin
            if( args<>'') then
                Concatenate7(res,prefix,StructList[styp.id].name,'_',{memb}'deallocate','(',args,')')
            else
                Concatenate7(res,prefix,StructList[styp.id].name,'_',{memb}'deallocate','(',obj,')');

        end;
        typ:=MakeType(-1);
    


    end else if(membd.isstatic) then begin

        if(membd.construct) then begin
           if (StructList[styp.id].isArrayStruct) then begin
                res:='Array structs cannot be allocated';
                result:=false;
                exit;
           end else if (StructList[styp.id].isinterface) then begin

               if(args='') then begin
                  res:='Interface constructor requires a type id argument';result:=false;exit;
               end;
               typ:=MakeType(styp.id);
           
               Result:=true;
               concatenate5(res,prefix,StructList[styp.id].name,'__factory(',args,')');
               StructList[styp.id].dofactory:=true;
               exit;
           end
           else if(StructList[styp.id].parentstruct<>-1) then begin
               typ:=MakeType(styp.id);
           
               Result:=true;
               concatenate5(res,prefix,StructList[styp.id].name,'__allocate(',args,')');
           end
           else if(args<>'') then begin
               res:='constructor requires no arguments'; result:=false; exit;
               Result:=false;
           end else begin
               typ:=MakeType(styp.id);
           
               Result:=true;
               concatenate3(res,prefix,StructList[styp.id].name,'__allocate()');

           end;


           exit;
       end;

        //ooh a static member.
        concatenate7(res,prefix,StructList[styp.id].name,'_',memb,'(',args,')');
        typ:=MakeType(StructHash.ValueOf(membd.returntype));
    

    end else if (not allowinstance) then begin
        res:=memb+' is not an static member of '+StructList[styp.id].name;
        result:=false;
        exit;
    end else begin


        //ooh an instance member
        if(args='') then begin
          if(obj='') then
            concatenate5(res,prefix,StructList[styp.id].name,'_',memb,'(this)')
          else
            concatenate7(res,prefix,StructList[styp.id].name,'_',memb,'(',obj,')');
        end else begin
          if(obj='') then
            concatenate7(res,prefix,StructList[styp.id].name,'_',memb,'(this,',args,')')
          else
            concatenate9(res,prefix,StructList[styp.id].name,'_',memb,'(',obj,',',args,')');
        end;
        typ:=MakeType(StructHash.ValueOf(membd.returntype) );

    end;


    Result:=true;
end;

//==============================================================================
function TranslateFunctionName(const f:string; var res:string; var typ:Tvtype):boolean;
var
   h:integer;
begin
    h:=FunctionHash.ValueOf(f);
    Result:=false;
    if(h<>-1) then begin
        Result:=true;
        TranslateMemberOfFunction(f,h,'_pointer',res,typ);
    end else begin
        res:='Cannot find function: '+f;
    end;
end;

//==============================================================================
function translateIndexSet( const obj:string; const sid:integer; const ind:string; const val:string;
          var res:string; var typ:Tvtype;  fromstruct:integer; const pos:integer):boolean;

var esp,h:integer;

    function TryDelegate(const dmemb:Tmember):boolean;
     var typ2,typ3:Tvtype;
     var res2,res3:string;
     var dummyvar:boolean;
    begin
        if not translateDotVar(obj,dmemb.name,sid,res2,typ2,{fromstruct}sid,pos,false,'',MakeType(0),dummyvar) then begin
            raise Exception.Create('Bad internal error #4: '+res2);
        end;
        result:=false;
        if(translateIndexSet(res2,typ2.id,ind,val,res3,typ3,fromstruct,pos)) then begin
            result:=true;
            res:=res3;
            typ:=typ3;
        
        end;

    end;
begin

    h:=BigArrayHash.ValueOf(obj);
    if(h<>-1) then begin
        if(bigArrayWidths[h]<>-1) then begin
            res:='Illegal assignment of 2D array';
            result:=false;
            exit;
        end;
        result:=true;
        typ:=MakeType(StructHash.ValueOf(BigArrayTypes[h]));
    
        if(BigArraySizes[h]>JASS_ARRAY_SIZE) then begin
            Concatenate7(res,'call sg__',BigArrayNames[h],'_set(',ind,',',val,')');
        end else begin
            Concatenate6(res,'set s__',BigArrayNames[h],'[',ind,']= ',val);
        end;
        exit;
    end;


    esp:=StructHash.ValueOf(obj);

    if(esp<>-1 ) then begin
        if (StructList[esp].membershash.ValueOf('_staticsetindex')<>-1) then begin
            result:=translateDotMethod(obj, '_staticsetindex', ind+', '+val, MakeType(esp), res, typ, fromstruct, pos);

            res:='call '+res;
        end else begin
            result:=false;
        end;
        exit;
    end;


    if ((sid>=ARRAYEDTYPE)or(sid=-1) or (sid=METHODTYPE) ) then begin result:=false; exit; end;

    h:=StructList[sid].bigArrayId;
    if(h<>-1) then begin
        result:=true;
        typ:=MakeType( StructHash.ValueOf(BigArrayTypes[h]) );
    
        if(BigArraySizes[h]>JASS_ARRAY_SIZE) then begin
            Concatenate9(res,'call sg__',BigArrayNames[h],'_set(',obj,'+',ind,',',val,')');
        end else begin
            Concatenate8(res,'set s__',BigArrayNames[h],'[',obj,'+',ind,']= ',val);
        end;
        exit;
    end;

    if(StructList[sid].customarray>0) then begin
        if(TryStrToIntX(ind,h)) then begin
            if ((h>=StructList[sid].customarray) or (h<0)) then begin
                 res:='Array index out of bounds: '+ind; result:=false; exit;
            end;
        end;
        if(StructList[sid].requiredspace>=JASS_ARRAY_SIZE) then begin
            if(h=0) then
                Concatenate7(res,'call sg__',StructList[sid].name,'_set(',obj,',',val,')')
            else
                Concatenate9(res,'call sg__',StructList[sid].name,'_set(',obj,'+',ind,',',val,')');

        end else begin
            if(h=0) then
                Concatenate6(res,'set s__',StructList[sid].name,'[',obj,']=',val)
            else
                Concatenate8(res,'set s__',StructList[sid].name,'[',obj,'+',ind,']=',val);
        end;
        typ:=MakeType(-1);
    
        Result:=true;


    end else begin
      h:=StructList[sid].membershash.ValueOf('_setindex');
      if (h<>-1) then begin
        result:=translateDotMethod(obj, '_setindex', ind+', '+val, MakeType(sid), res, typ, fromstruct, pos);
        if(StructList[sid].members[h].returntype=StructList[sid].name) then
            res:='set '+obj+'='+res
        else
            res:='call '+res;
      end else begin
        result:=false;
      end;
      if(not result) then begin
          for h := 0 to StructList[sid].delegateN-1 do begin
              if TryDelegate(StructList[sid].delegates[h]) then begin
                  result:=true;
                  exit;
              end;

          end;
      end;
    end;




end;

//==============================================================================
function translateArray(const arr:string; const ind:string; sid:integer; var res:string; var typ:Tvtype;   fromstruct:integer; const pos:integer; const insetstatement:boolean; const allowgetstructindex:boolean=false):boolean;
var
   h:integer;


    function TryDelegate(const dmemb:Tmember):boolean;
     var typ2,typ3:Tvtype;
     var res2,res3:string;
     var dummyvar:boolean;
    begin
        if not translateDotVar(arr,dmemb.name,sid,res2,typ2,{fromstruct}sid,pos,false,'',MakeType(0),dummyvar) then begin
            raise Exception.Create('Bad internal error #3: '+res2);
        end;
        result:=false;
        if(translateArray(res2,ind,typ2.id,res3,typ3,fromstruct,pos,insetstatement,allowgetstructindex)) then begin
            result:=true;
            res:=res3;
            typ:=typ3;
        
        end;

    end;


begin

    h:=BigArrayHash.ValueOf(arr);
    if(h<>-1) then begin
        result:=true;
        if(bigArrayWidths[h]<>-1) then begin
            //2D !
            typ:=MakeType(BigArrayStructs[h] );
        
            Concatenate5(res,'(',ind,')*(',IntToStr(BigArrayWidths[h]),')');
            exit;
        end;

        typ:=MakeType( StructHash.ValueOf(BigArrayTypes[h]) );
    
        if(BigArraySizes[h]>JASS_ARRAY_SIZE) then begin
            Concatenate5(res,'sg__',BigArrayNames[h],'_get(',ind,')');
        end else begin
            Concatenate5(res,'s__',BigArrayNames[h],'[',ind,']');
        end;
        exit;
    end;


    h:=StructHash.ValueOf(arr);
    if (h<>-1) and (StructList[h].customarray<=0) then begin
        if(allowgetstructindex) then h:= StructList[h].membershash.valueOf('_staticgetindex')
        else h:=-1;
        if (h<>-1) then begin
            result:=translateDotMethod(arr,'_staticgetindex',ind,MakeType(sid),res,typ,fromstruct,pos);
            exit;
        end else begin
            h:=StructHash.ValueOf(arr);
            if(StructList[h].isarraystruct) then begin
                if(insetstatement) then begin
                    res:='Cannot assign a struct array''s index';
                    result:=false;
                    exit;
                end;
                typ:=MakeType(h);
            
                Res:='('+ind+')';
                result:=true;
                exit;
            end;
            res:=arr+' is not an array.'; Result:=false; exit;
        end;


    end;




    if ((sid=-1) or (sid>=ARRAYEDTYPE)) then begin
        //just print out
        //res:='['+ind+']';
        //Result:=true;
        concatenate4(res,arr,'[',ind,']');
        result:=true;
    
        if(sid>=ARRAYEDTYPE) then typ:=MakeType(sid-ARRAYEDTYPE)
        else typ:=MakeType(-1);

        exit;
    end;



    if(sid=METHODTYPE) then begin
        res:='Unexpected: [';
        result:=false;
        exit;
    end;


    if(sid<=0) then begin
        res:='[Internal error] wrong "sid" (contact Vexorian about this)';
        result:=false;
        exit;
    end;


    if (StructList[sid].customarray<=0) then begin
        h:=StructList[sid].bigArrayId;
        if(h<>-1) then begin
            result:=true;
            typ:=MakeType(StructHash.ValueOf(BigArrayTypes[h]) );
        
            if(BigArraySizes[h]>JASS_ARRAY_SIZE) then begin
                Concatenate7(res,'sg__',BigArrayNames[h],'_get(',arr,'+',ind,')');
            end else begin
                Concatenate7(res,'s__',BigArrayNames[h],'[',arr,'+',ind,']');
            end;
            exit;
        end;

        if(allowgetstructindex) then begin
            if(translateDotMethod(arr,'_getindex',ind,MakeType(sid),res,typ,fromstruct,pos)) then begin
                result:=true;
                exit;
            end;
            for h := 0 to StructList[sid].delegateN - 1 do begin
                if(TryDelegate(StructList[sid].delegates[h]) ) then begin
                    result:=true;
                    exit;
                end;
            end;

        end;
        res:=arr+' is not an array.'; result:=false; exit;
    end;
    h:=-1;
    if(TryStrToIntX(ind,h)) then begin
        if ((h>=StructList[sid].customarray) or (h<0)) then begin
             res:='Array index out of bounds: '+ind; result:=false; exit;
        end;
    end else h:=-1;


    if(StructList[sid].requiredspace>=JASS_ARRAY_SIZE) then begin
        if(h=0) then
            Concatenate5(res,'sg__',StructList[sid].name,'_get(',arr,')')
        else
            Concatenate7(res,'sg__',StructList[sid].name,'_get(',arr,'+',ind,')');

    end else begin
        if(h=0) then
            Concatenate5(res,'s__',StructList[sid].name,'[',arr,']')
        else
            Concatenate7(res,'s__',StructList[sid].name,'[',arr,'+',ind,']');
    end;
    typ:=MakeType( StructHash.ValueOf(StructList[sid].customarraytype) );

    Result:=true;
end;


//==============================================================================
function translateMethodCode( const clas:string; const meth:string; var res:string; fromstruct:integer; const pos:integer):boolean;
var
  h,k:integer;
  memb:Tmember;
  prefix:string;
begin
    h:=Structhash.ValueOf(clas);
    if(h=-1) then begin res:=clas+' is not an struct name'; result:=false; exit; end;

    k:= StructList[h].membershash.ValueOf(meth);
    if(k=-1) then begin res:=meth+' is not a member of '+clas; result:=false; exit; end;
    memb:=StructList[h].members[k];
    if (memb.ismethod and memb.isstatic and (memb.argnumber=0)) then begin
        if( (memb.access=ACCESS_PRIVATE) and (fromstruct<>h)) then begin
            result:=false; res:=StructList[h].name+'.'+meth+' is private.'; Exit;
        end;
        if (memb.decl>pos) then begin
            memb.abuse:=true;
            prefix:='sc__';
        end
        else prefix:='s__';

        Concatenate5(res,'function ',prefix,StructList[h].name,'_',meth);
        result:=true;

    end else begin
        result:=false;
        res:=meth+' is not an static method of '+clas+' that takes nothing.';
    end;

end;


//==============================================================================
function translateLessThan( const m1:string; i1:integer; const m2:string; i2:integer; var res:string; fromstruct:integer; const pos:integer):boolean;
var
  h:Tvtype;

begin

    if ( (i1=-1) or (i2=-1) ) then begin
        res:='Relational comparission between special type and native type.';
        result:=false; exit;
    end;


    if (i1<>i2) then begin
        res:='Relational comparission between different struct types.';
        result:=false; exit;
    end;


    result:=translateDotMethod( m1,'_lessthan',m2, MakeType(i1), res, h,  fromstruct, pos);

end;

//==============================================================================
function translateEqualTo( const m1:string; i1:integer; const m2:string; i2:integer; var res:string; fromstruct:integer; const pos:integer; const negate:boolean):boolean;
var
  h:Tvtype;
  memb:Tmember;
  label normal;
begin



    if ( (i1=-1) or (i2=-1) ) then begin
        goto normal;
        {res:='== comparission between special type and native type?';
        result:=false; exit;}
    end;

    if (i1<>i2) then begin
        goto normal;
        {res:='== comparission between different struct types.';
        result:=false; exit;}
    end;
    if( (i1<1) or (StructN<i1) ) then goto normal;

    if( StructList[i1].getmember('_equalto', memb) ) then begin







        result:=translateDotMethod( m1,'_equalto',m2, MakeType(i1), res, h,  fromstruct, pos);
        if(negate) then res:='not ('+res+')';
    end else begin
     normal:
        if(negate) then res:=m1+' != '+m2
        else            res:=m1+' == '+m2;
        result:=true;
        //result:=translateDotMethod( m1,'_equal',m2, i1, res, h,  fromstruct, pos);
    end;




end;


//==============================================================================
//  ParseFunctionSignature - used by returnfixer and shadowhelper
procedure ParseFunctionSignature( const s:string; out constant: boolean; out name:string; out argn:integer; out returntype:string);
var
    x,y:integer;
    word:string;
begin
    GetLineWord(s, word, x);
    constant:=(word = 'constant');
    if constant then begin
        CompareLineWord('function',s, x,x);
    end;

    GetLineWord(s, name, x,x);
    CompareLineWord('takes',s,x,x);
    argn:=0;
    if not CompareLineWord('nothing',s,y,x) then begin
        //we got arguments!!111
        while(true) do begin
            GetLineToken(s,word,y,x);
            if(word='returns') then break;
            x:=y;
            if(Length(ParseFunctionSignature_ArgNamesBuf) <= argn) then begin
                SetLength(ParseFunctionSignature_ArgNamesBuf, argn+5);
                SetLength(ParseFunctionSignature_ArgTypesBuf, argn+5);
            end;
            ParseFunctionSignature_ArgTypesBuf[argn] := word;
            GetLineToken(s,ParseFunctionSignature_ArgNamesBuf[argn],x,x);
            argn:=argn+1;
        end;

    end else x:=y;
    CompareLineWord('returns',s,x,x);
    GetLineWord(s, returntype,x,x);



end;

//==============================================================================
//==============================================================================
//==============================================================================
//==============================================================================

end.
