/* 
   Copyright 2001-2003 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222

**********************************************************************

BROWSE.C holds the functionality for browsing to the definition of 
a variable or macro.  It also allows to browse backwards.

**********************************************************************
*/
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <richedit.h>
#include <stdio.h>

#include "header.h"
#include "winconst.h"

extern char szProjectName[256] ;
extern int browseInfo ;

static char **filenames ;
static int filecount ;
static DWINFO **browsebacklist ;
static int browseCount, browseMax ;

static int LoadBrowseInfo(FILE *fil)
{
   char buf[12] ;
   unsigned short count,i ;
   fread(buf,12,1,fil) ;
   if (strncmp(buf,"$BRW",4))
      return 0 ;
   if (filecount)
      return *(int *)(buf + 4) ;
   fseek(fil,32,SEEK_SET) ;
   fread(&count,2,1,fil) ;
   filecount = count ;
   filenames = calloc(count, sizeof(char *)) ;
   if (!filenames)
      return 0 ;
   for (i=0; i < count; i++) {
      char name[256] ;
      int len = fgetc(fil) ;
      fread(name,len,1,fil) ;
      name[len] = 0 ;
      filenames[i] = strdup(name) ;
      if (!filenames[i]) {
         for (--i; i>=0; --i) {
            free(filenames[i]) ;
         }
         free(filenames) ;
         return 0 ;
      }
   }
   
   return *(int *)(buf+4) ;
}
void FreeBrowseInfo(void)
{
   int i ;
   for (i=0; i < filecount; i++)
      free(filenames[i]) ;
   free(filenames) ;
   filenames = 0 ;
   filecount = 0 ;
}
int FindBrowseInfo(FILE*fil, char *name, int root) 
{
   if (*name == 0)
      return 0 ;
   while (1) {
      char buf[128 * 9],*p=buf ;
      int count ;
      fseek(fil,root,SEEK_SET) ;
      fread(buf,128*9,1,fil) ;
      count = *p++ ;
      while (count) {
         if (*p == *name) {
            name++ ;
            if (*name == 0)
               return *(int *)(p+5) ;
            root = *(int *)(p+1) ;
            break ;
         }
         count-- ;
         p += 9 ;
      }
      if (!count)
         return 0 ;
   }
}
static int FindBrowseData(HWND hwnd, FILE *fil, int ofs, char *hint, char *name, int *line, int insertbrowse)
{
   int curline,recsize ;
   unsigned char buf[256] ;
   unsigned char global[256] ;
   char *filname = (char *)SendMessage(hwnd, WM_FILENAME,0,0) ;
   int fileno=-1,i ;
   int found = FALSE ;
   struct {
      long min,max ;
   } charrange ;
   SendDlgItemMessage(hwnd,ID_EDITCHILD,EM_EXGETSEL,(WPARAM) 0, (LPARAM) &charrange) ;
   curline = SendDlgItemMessage(hwnd,ID_EDITCHILD,EM_LINEFROMCHAR,(WPARAM)charrange.min,0) + 1 ;

   for (i=0 ; i < filecount; i++)
      if (!stricmp(filenames[i],filname)) {
         fileno = i ;
         break ;
      }
   fseek(fil,ofs,SEEK_SET) ;
   memset(global,0,sizeof(global)) ;

   while(1) {
      fread(buf,2,1,fil) ;
      if (*(short *)buf == 0)
         break ;
      fread(buf+2,(*(short *)buf)-2,1,fil) ;
      if (*(int *)(buf + 6) == -1) // -1 = global
         memcpy(global,buf,*(short *)buf) ;
      if (*(int *)(buf+10) != fileno)
         continue ;
      if (*(int *)(buf+6) != -1)
         if (*(int *)(buf+2) <= curline)
            if (*(int *)(buf+6) > curline || curline == -2) { // -2 == static
               found = TRUE ;
               break ;
            }
   }
   if (!found && global) {
      memcpy(buf,global,*(short*)global) ;
      found = TRUE ;
   }
   if (!found)
      return 0 ;
// *(short *)(buf+15) == charpos eventually...
   if (hint) {
      memcpy(hint,buf+18,buf[17]) ;
      hint[buf+15] = 0 ;
   }
   if (line && name) {
      if (*(int *)(buf + 10) >= filecount)
         return 0 ;
      strcpy(name,filenames[*(int *)(buf+10)]) ;
      *line = *(int *)(buf + 2) ;
      if (insertbrowse) {
         DWINFO *info ;
         char *p ;
         if (browseCount >= browseMax) {
            if (browseCount >= 20) {
               memmove(browsebacklist,browsebacklist+1,(--browseCount) * sizeof(void *)) ;
            } else {
               browsebacklist = realloc(browsebacklist,(browseMax += 20) * sizeof(void *)) ;
               if (!browsebacklist) { 
                  browseMax =0 ;
                  return 1;
               }
            }
         }
         info = calloc(sizeof(DWINFO),1) ;
         if (!info)
            return 1 ;
         strcpy(info->dwName,filname) ;
         info->dwLineNo = curline ;
         p = strrchr(info->dwName,'\\') ;
         if (p)
            strcpy(info->dwTitle,p+1) ;
         browsebacklist[browseCount++] = info ;
      }
   }
   return 1 ;
}
void BrowseTo(HWND hwnd)
{
   char name[256] ;
   int ofs ;
   if (!browseInfo)
      return ;
   if (SendMessage(hwnd,WM_WORDUNDERCURSOR,0,(LPARAM)name)) {
      char filename[256] ;
      FILE *fil ;
      DWINFO info ;
      memset(&info,0,sizeof(info)) ;
      strcpy(filename,szProjectName) ;
      strcpy(filename +strlen(filename)-4,".BRW") ;
      fil = fopen(filename,"rb") ;
      if (!fil)
         return ;
      if ( !(ofs = LoadBrowseInfo(fil))) {
         fclose(fil) ;
         return ;
      }       
      ofs = FindBrowseInfo(fil,name,ofs) ;
      if (ofs &&FindBrowseData(hwnd,fil,ofs,0,&info.dwName,&info.dwLineNo,TRUE )) {
         char *p = strrchr(info.dwName,'\\') ;
         if (p)
            strcpy(info.dwTitle,p+1) ;
         CreateDrawWindow(&info) ;
      }
      fclose(fil) ;
   }
}
char *BrowseHint(HWND hwnd)
{
   static char hint[256] ;
   char *rv = 0 ;
   char name[256] ;
   int ofs ;
   if (!browseInfo)
      return rv ;
   if (SendMessage(hwnd,WM_WORDUNDERCURSOR,0,(LPARAM)name)) {
      char filename[256] ;
      FILE *fil ;
      strcpy(filename,szProjectName) ;
      strcpy(filename +strlen(filename)-4,".BRW") ;
      fil = fopen(filename,"rb") ;
      if (!fil)
         return rv;
      if ( !(ofs = LoadBrowseInfo(fil))) {
         fclose(fil) ;
         return rv;
      }       
      ofs = FindBrowseInfo(fil,name,ofs) ;
      if (ofs && FindBrowseData(hwnd,fil,ofs,hint,0,0,FALSE ))
         rv = hint ;
      fclose(fil) ;
   }
   return rv ;
}
void BrowseBack(void)
{
   if (!browseCount)
      return ;
   CreateDrawWindow(browsebacklist[--browseCount]) ;
   free(browsebacklist[browseCount]) ;
}