/****************************************************************************

  Busca Funciones (BuFun), copyright (c) 1996 by Salvador E. Tropea (SET)

  Designed for the TCEditor class to be used by Robert Hhne in your RHIDE.

  If you want to use this code contact me first.

  E-Mail: salvador@inti.gov.ar

  Telephone: (+5411) 4759-0013

  Postal Address:
  Salvador E. Tropea
  Curapalige 2124
  (1678) Caseros - 3 de Febrero
  Prov: Buenos Aires
  Argentina

  These routines are compatible with Borland's TVision 1.03 and the port of
those routines to DJGPP.

  int SelectFunctionToJump(char *b, unsigned l)

  Is the external entry point, this routine calls to one routine that parses
the C source searching for functions, the methode is heuristic and doesn't
detect all the possible cases, in particular the K & R edition I style of
functions (totally obsolet).
  The routine creates a StringCollection with the functions found and then
creates a dialog box to select one. If the user select a function the
routine returns the line where the function starts if there are no functions
or the user choose cancel the routine returns -1.
  The parameters passed to the routine are:
  char *b: The buffer where the routines will search.
  unsigned l: The length of the buffer.

  Defining the label TEST you can test the module as an standalone program.

****************************************************************************/

// That's the first include because is used to configure the editor.
#include "ceditint.h"

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

#define Uses_MsgBox
#define Uses_TLabel
#define Uses_TScrollBar
#define Uses_TRect
#define Uses_TButton
#define Uses_TDialog
#define Uses_TEvent

#define Uses_TCEditor_External // For mode constants
#define Uses_TCEditor_Commands // For the cmcJumpToFunction context
#include <ceditor.h>

#define Uses_TGrowDialog
#define Uses_SOStack
#define Uses_TNoCaseSOSStringCollection
#define Uses_TSOSSortedListBox
#include <settvuti.h>

#define Uses_SETAppConst
#include <setapp.h>

extern ushort execDialog( TDialog *d, void *data );

//#define TEST

inline int IsWordChar(char c)
{
 return c=='_' || ucisalpha(c);
}

static char Buffer[256];
static char NomFun[256];
static char Alone;
static int Used,UsedNom;
static int Line,LineFun;

#ifdef TEST
static FILE *in;
#else
static char *buffer;
static unsigned IndexB;
static unsigned BufLen;
#endif

static int TakeWord(int TakeOneCharToo=0);


#ifdef TEST
static int GetAChar()
{
 char c=getc(in);
 if (c=='\n')
    Line++;
 return c;
}

static void UnGetAChar(int c)
{
 ungetc(c,in);
 if (c=='\n')
    Line--;
}
#else
static int GetAChar()
{
 if (IndexB>=BufLen)
    return EOF;
 int c=(unsigned char)buffer[IndexB++];
 if (c=='\n')
    Line++;
 return c;
}

static void UnGetAChar(int c)
{
 IndexB--;
 if (c=='\n')
    Line--;
}
#endif


static stkHandler StrDup(char *s,int line,int len,SOStack &stk)
{
 stkHandler h=stk.alloc(len+sizeof(int));
 char *d=stk.GetStrOf(h);
 strcpy(d,s);
 int *l=(int *)&d[len];
 *l=line;
 return h;
}

static int TakeWord(int TakeOneCharToo)
{
 int c;
 char last;

 do
  {
   c=GetAChar();
   if (c==EOF)
      return 0;
   if (IsWordChar(c))
     {
      Used=0;
      do
       {
        Buffer[Used++]=c;
        c=GetAChar();
       }
      while (c!=EOF && Used<255 && (ucisalnum(c) || c==':' || c=='_' || c=='~'));
      Buffer[Used]=0;
      if (c!=EOF)
         UnGetAChar(c);
      if (Used==0)
         return 0;
      return 1;
     }
   else
   if (ucisspace(c))
     {
      do
       {
        c=GetAChar();
       }
      while (c!=EOF && ucisspace(c));
      UnGetAChar(c);
     }
   else
     {
      switch (c)
        {
         case '\"':
              do
               {
                c=GetAChar();
                if (c=='\\')
                  {
                   c=GetAChar();
                   c=GetAChar();
                  }
               }
              while (c!=EOF && c!='\"');
              break;

         case '\'':
              do
               {
                c=GetAChar();
                if (c=='\\')
                  {
                   c=GetAChar();
                   c=GetAChar();
                  }
               }
              while (c!=EOF && c!='\'');
              break;

         case '/':
              c=GetAChar();
              if (c=='/')
                {
                 do
                  {
                   do
                    {
                     last=c;
                     c=GetAChar();
                    }
                   while (c!=EOF && c!='\n' && c!='\r');
                   if (c=='\r') c=GetAChar();
                  }
                 while (last=='\\');
                }
              else
                if (c=='*')
                  {
                   do
                    {
                     c=GetAChar();
                     if (c=='*')
                       {
                        c=GetAChar();
                        if (c=='/')
                           break;
                        UnGetAChar(c);
                       }
                    }
                   while (c!=EOF);
                  }
              break;

         case '#':
              do
               {
                do
                 {
                  last=c;
                  c=GetAChar();
                 }
                while (c!=EOF && c!='\n' && c!='\r');
                if (c=='\r') c=GetAChar();
               }
              while (last=='\\');
              break;


         default:
              // Is a single char
              if (TakeOneCharToo)
                {
                 Alone=c;
                 return 2;
                }
        }
     }
  }
 while (c!=EOF);

 return 0;
}


static int SearchBalance(char ref, char ref2)
{
 int r,bal=1;

 do
  {
   r=TakeWord(1);
   if (r==2)
     {
      if (Alone==ref)
         bal--;
      else
         if (Alone==ref2) bal++;
     }
   if (!bal) break;
  }
 while (r);

 return r;
}


static
int SearchBalanceCopy(char ref, char ref2)
{
 int r,bal=1;

 if (Used<255)
    NomFun[Used++]=ref2;
 UsedNom=Used;
 do
  {
   r=TakeWord(1);
   if (r==2)
     {
      if (Alone==ref)
         bal--;
      else
         if (Alone==ref2) bal++;
      if (UsedNom<255)
        {
         if (Alone==')' && NomFun[UsedNom-1]==' ')
            NomFun[UsedNom-1]=Alone;
         else
         if (Alone==',' && NomFun[UsedNom-1]==' ')
           {
            NomFun[UsedNom-1]=Alone;
            NomFun[UsedNom++]=' ';
           }
         else
            NomFun[UsedNom++]=Alone;
        }
     }
   else
     if (r==1)
       {
        if (UsedNom+Used<254)
          {
           strcpy(&NomFun[UsedNom],Buffer);
           UsedNom+=Used;
           NomFun[UsedNom++]=' ';
          }
       }
   if (!bal) break;
  }
 while (r);
 NomFun[UsedNom++]=0;

 return r;
}


static
int SearchFuncs(TNoCaseSOSStringCollection *FunList,SOStack &stk, int mode)
{
 int funs=0;
 int opLen=0;
 char opBuf[8];

 Line=1;
 int r;
 do
  {
   r=TakeWord();
   if (!r) break;
   if (strcmp(Buffer,"operator")==0)
     {
      opLen=1;
      TakeWord(1);
      opBuf[0]=Alone;
      do
        {
         r=TakeWord(1);
         if (r==2 && opLen<4 && Alone!='(')
            opBuf[opLen++]=Alone;
        }
      while (r==2 && Alone!='(');
      opBuf[opLen]=0;
     }
   else
     {
      do
       {
        r=TakeWord(1);
       }
      while(r==1);
      if (!r) break;
     }
   if (Alone=='(')
     {
      strcpy(NomFun,Buffer);
      if (opLen)
        {
         strcat(NomFun," ");
         strcat(NomFun,opBuf);
         strcat(NomFun," ");
         Used+=opLen+2;
         opLen=0;
        }
      LineFun=Line;
      r=SearchBalanceCopy(')','(');
      if (!r) break;
      r=TakeWord(1);
      if (!r) break;

      int SearchOpen=0;
      int Eureka=0,isProto=0;

      if (mode==modeBFPrototypes && r==2 && Alone==';')
        {// Looking for a prototype
         Eureka=isProto=1;
        }

      if (!isProto)
        {// Looking for a function
         if (r==2)
           {
            if (Alone=='{')
               Eureka=1;
            else
              if (Alone==':')
                 SearchOpen=Eureka=1;
           }
         else
           {
            if (strcmp(Buffer,"const")==0)
               SearchOpen=Eureka=1;
           }
        }
      if (Eureka)
        {
#ifdef TEST
         printf("Function: %s, Line %d\n",NomFun,LineFun);
#endif
         if (mode!=modeBFPrototypes || isProto)
           {
            stkHandler s=StrDup(NomFun,LineFun,UsedNom,stk);
            FunList->insert(s);
            funs++;
           }
         if (!isProto)
           {
            if (SearchOpen)
              {
               do
                {
                 r=TakeWord(1);
                 if (r==2 && Alone=='{') break;
                }
               while (r);
               if (!r) break;
              }
            r=SearchBalance('}','{');
            if (!r) break;
           }
        }
     }
  }
 while(r);

 return funs;
}

#ifndef TEST

class TLFuns : public TGrowDialog
{
public:
 TLFuns( TRect r, const char *name, int extraOptions=0 ) :
      TGrowDialog(r,name,extraOptions),
      TWindowInit( &TLFuns::initFrame ) {};
 void handleEvent(TEvent& event);
};

void TLFuns::handleEvent(TEvent& event)
{
 TDialog::handleEvent(event);
 if ( event.what == evCommand || event.what == evBroadcast)
   {
    switch ( event.message.command )
      {
       // a button
       case cmListItemSelected:
            endModal(event.message.command);
            clearEvent(event);
            break;
       case cmeZoom:
            event.message.command=cmZoom;
            TDialog::handleEvent(event);
            break;
       default:
           break;
      }
   }
}

static TDialog *createDialog( )
{
 TLFuns *d = new TLFuns( TRect( 0, 0, 70, 19 ), _("Jump to function") );

 d->options |= ofCentered;
 d->helpCtx =  cmcJumpToFunction;

 TScrollBar *sb = new TScrollBar( TRect(66,2,67,15) );
 d->insert(sb);

 TSOSSortedListBox *ListaH = new TSOSSortedListBox( TRect(3,2,66,15),(ushort)1, sb );
 ListaH->growMode = gfMoveBottomCorner;
 d->insert(ListaH);

 d->insert(new TLabel(TRect(3,1,66,2),_("List of functions"), ListaH));

 TButton *tb=new TButton( TRect( 36, 16, 50, 18 ), _("~O~K"), cmOK, bfDefault );
 tb->growMode = gfMoveAccording;
 d->insert(tb);

 tb=new TButton( TRect( 52, 16, 66, 18 ), _("Cancel"), cmCancel, bfNormal );
 tb->growMode = gfMoveAccording;
 d->insert(tb);

 d->selectNext( False );
 return d;
}

/**[txh]********************************************************************

  Description:
  Searchs functions and prototypes and ask gor jumping.

  Include: ced_exte
  Return: Line to jump or -1

***************************************************************************/

int SelectFunctionToJump(char *b, unsigned l, char *word, int mode)
{
 SOStack stk;
 buffer=b;
 IndexB=0;
 BufLen=l;

 TNoCaseSOSStringCollection *FunList = new TNoCaseSOSStringCollection(20,5,&stk);

 int funcs=SearchFuncs(FunList,stk,mode);

 struct TListBoxRec
 {
  TCollection *items;
  ccIndex selection;
 } br;

 br.items=FunList;
 br.selection=0;

 if (!funcs)
   {
    messageBox(_("Hmmm ... I can't find any function, are you sure?"),mfError | mfOKButton);
    delete FunList;
    return -1;
   }
 else
   {
    if (word)
      {
       stkHandler hStk=stk.addStr(word);
       FunList->search((void *)hStk,br.selection);
       if (br.selection>=FunList->getCount())
          br.selection=FunList->getCount()-1;
      }
    if (execDialog(createDialog(),&br)==cmCancel)
      {
       delete FunList;
       return -1;
      }
   }

 char *s=FunList->atStr(br.selection);
 int length=strlen(s);
 int line=*(int *)(s+length+1);

 delete FunList;

 return line;
}

int CreateFunctionList(char *b, unsigned l, SOStack &stk, TNoCaseSOSStringCollection *FunList)
{
 buffer=b;
 IndexB=0;
 BufLen=l;

 int funcs=SearchFuncs(FunList,stk,modeBFFunctions);
 return funcs==0;
}

#else
static void writeStr(void *p, void *)
{
 char *s=(char *)p;
 int l=strlen(s);
 printf("%s %d\n",s,*(int *)(s+l+1));
}

int main(int argc, char *argv[])
{
 if (argc!=2)
    return 1;
 in=fopen(argv[1],"rb");

 TStringCollection *FunList = new TStringCollection(20,5);

 int funcs=SearchFuncs(FunList);

 printf("\n\nLista ordenada de las funciones: (%d)\n\n",funs);
 FunList->forEach(writeStr,NULL);
 return 0;
}
#endif