/* 
   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

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

TAGS.C holds code to manage the various tags that can fit at the left
hand side of each line in a source file.  Tags inclue breakpoints,
bookmarks, and tags related to the find in files function.  This file
also defines the bookmark functionality and dialogs, and the routines to
save and restore tags to the project file

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

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

#include "header.h"
#include "winconst.h"
extern HANDLE children[MAX_CHILDREN] ;
extern int numberofdrawwindows ;
extern HWND hwndFrame ;
extern HINSTANCE hInstance ;

static LOGFONT fontdata = {
   14,0,0,0,FW_NORMAL,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_DEFAULT_PRECIS,
	CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH | FF_DONTCARE,
	"Courier New"
} ;

DWINFO currentBM ;
struct tag {
   struct tag *next ;
   int lineno ;
   int charpos ;
   int enabled ;
   void *extra ;
} ;

struct filename {
   char *next ;
   char name[256] ;
   struct _linechange {
      struct _linechange *next ;
      int lineno ;
      int delta ;
   } *linechange ;
   struct tag *tagArray[TAG_MAX] ;
} ;

static struct filename *fileList, *last_module ;

static int realline(struct filename *l, int lineno)
{
   struct _linechange *c = l->linechange ;
   int adj = 0 ;
   while (c && c->lineno < lineno) {
      adj += c->delta ;
      c = c->next ;
   }

   return lineno + adj ;
}
int FindRealLine(char *name, int lineno)
{
   struct filename *l = fileList ;
   while(l) {
      if (!stricmp(l->name,name)) {
         return realline(l,lineno) ;
      }
      l = l->next ;
   }
   return lineno ;

}
int IsTagged(char *module, int line)
{
   struct filename *l =fileList ;
   if (last_module && !stricmp(module, last_module->name)) {
      l = last_module ;
      goto join ;
   }
   while (l) {
      if (!stricmp(l->name,module)) {
         int i;
         last_module = l ;
join:
         for (i= 0; i < TAG_MAX; i++) {
            struct tag *t = l->tagArray[i] ;
            while (t) {
               if (line == realline(l,t->lineno))
                  if (t->enabled)
                     return i ;
                  else
                     break ;
               t = t->next ;
            }
         }
         return -1 ;
      }
      l = l->next ;
   }
   return -1 ;
}
int Tag(int type, char *name, int lineno, int charpos, void *extra, int freeextra, int alwaysadd )
{
   struct filename **l = &fileList ;
   struct tag *t, **oldt ;
   while (*l) {
      if (!stricmp((*l)->name, name))
         break ;
      l = &(*l)->next ;
   }
   if (!*l) {
      *l = calloc(sizeof(struct filename),1) ;
      if (!*l)
         return ;
      strcpy((*l)->name, name) ;
   }
   t = (*l)->tagArray[type] ;
   oldt = &(*l)->tagArray[type] ;
   while (t) {
      if (lineno == t->lineno) {
         if (alwaysadd) {
            if (!t->enabled && type == TAG_BP)
               dbgSetBreakPoint(name, lineno, extra) ;
            InvalidateByName((*l)->name) ;
            t->enabled = TRUE ;
            return TRUE ;
         }
            
         if (t->enabled && type == TAG_BP && (!t->extra || freeextra) || type != TAG_BP)  {
            *oldt = t->next ;
            if (type == TAG_BP)
               dbgClearBreakPoint((*l)->name, t->lineno) ;
            free(t->extra) ;
            free(t) ;
            InvalidateByName((*l)->name) ;
         } else {
            t->enabled = !t->enabled ;
            if (t->enabled)
               dbgSetBreakPoint((*l)->name,t->lineno, t->extra) ;
            else
               dbgClearBreakPoint((*l)->name, t->lineno) ;
            InvalidateByName((*l)->name) ;
            return t->enabled ;
         }
         return FALSE ;

      }
      if (lineno < t->lineno)
         break ;
      oldt = &t->next ;
      t= t->next ;
   }
   t = calloc(sizeof(struct tag),1) ;
   if (!t)
      return ;
   t->lineno = lineno ;
   t->charpos = charpos ;
   t->extra = extra ;
   t->next = *oldt ;
   t->enabled = TRUE ;
   *oldt = t ;
   if (type == TAG_BP)
      dbgSetBreakPoint(name,lineno,extra) ;
   InvalidateByName((*l)->name) ;
   return TRUE ;
}
void TagRemoveAll(int type)
{
   struct filename *l = &fileList ;
   struct tag *t ;
   while (l) {
      t = l->tagArray[type] ;
      l->tagArray[type] = NULL ;
      while (t) {
         struct tag *nt = t->next ;
         if (type == TAG_BP)
            dbgClearBreakPoint(l->name, t->lineno) ;
         free(t->extra) ;
         free(t) ;
         t = nt ;
      }
      InvalidateByName(l->name) ;
      l = l->next ;
   }
}
// used after BP are restored from data file
void TagRegenBreakPoints(void)
{
   struct filename *l = fileList ;
   struct tag *t ;
   while (l) {
      t = l->tagArray[TAG_BP] ;
      while (t) {
         if (t->enabled)
            dbgSetBreakPoint(l->name, t->lineno, t->extra) ;
         t = t->next ;
      }
      l = l->next ;
   }
}
void TagLineChange(char *name, int lineno, int delta)
{
   struct filename **l = &fileList ;
   struct _linechange *n ;
   while (*l) {
      if (!stricmp((*l)->name,name)) {
         int adj = 0 ;
         struct _linechange **c = &(*l)->linechange ;
         while (*c) {
            if ((*c)->lineno == lineno + adj - (*c)->delta) {
               (*c)->delta += delta ;
               return ;
            } else if ((*c)->lineno > lineno + adj)
               break ;
            adj -= (*c)->delta ;
            c = &(*c)->next ;
         }
         n = calloc(sizeof(struct _linechange),1) ;
         if (!n)
            return ;
         n->next = *c ;
         *c = n ;
         n->lineno = lineno+adj ;
         n->delta = delta ;
         if (delta < -1) {
            int i ;
            for (i=0; i < TAG_MAX; i++) {
               struct tag **t = &(*l)->tagArray[i] ;
               while (*t &&(*t)->lineno <= lineno)
                  t = &(*t)->next ;
               while (*t && (*t)->lineno < lineno - delta) {
                  struct tag *t1 = *t ;
                  *t = (*t)->next ;
                  if (i == TAG_BP)
                     dbgClearBreakPoint((*l)->name, t1->lineno) ;
                  InvalidateByName((*l)->name) ;
                  free(t1->extra) ;
                  free(t1) ;
               }
            }
         }
         return ;
      }
      l = &(*l)->next ;
   }
   *l = calloc(sizeof(struct filename),1) ;
   if (!*l)
      return ;
   strcpy((*l)->name, name) ;
   n = calloc(sizeof(struct _linechange),1) ;
   if (!n)
      return ;
   (*l)->linechange = n ;
   n->lineno = lineno ;
   n->delta = delta ;

}
void TagLinesAdjust(char *name, int remove, int debug)
{
   struct filename *l = fileList ;
   while (l) {
      if (!name || !stricmp(l->name,name)) {
         struct tag *tp[TAG_MAX] ;
         struct _linechange *c,*c1 ;
         int lineno = 1 ;
         int adj = 0 ;
         int i ;
         memset(tp,0,sizeof(tp)) ;
         if (!remove)
            if (debug) // hard coding TAG_BP
               tp[0] = l->tagArray[0] ;
            else
               for (i=1; i < TAG_MAX; i++)
                  tp[i] = l->tagArray[i] ;
         c = l->linechange ;
         l->linechange = 0 ;
         while (c) {
            c1 = c ;
            c = c->next ;
            if (!remove)
               for (i=0; i < TAG_MAX; i++) {
                  while (tp[i] && tp[i]->lineno <= c1->lineno) {
                     tp[i]->lineno += adj ;
                     tp[i] = tp[i]->next ;
                  }
               }
            adj += c1->delta ;
            free(c1) ;
         }
         if (!remove)
            for (i=0; i < TAG_MAX; i++) {
               while (tp[i]) {
                  tp[i]->lineno += adj ;
                  tp[i] = tp[i]->next ;
               }
            }
      }
      l = l->next ;
   }
}
void ToggleBookMark(void)
{
      HWND hWnd = GetFocus() ;
      int i ;
      for (i=0; i < numberofdrawwindows; i++) {
         DWINFO *ptr = (DWINFO *)GetWindowLong(children[i],0) ;
         if (ptr->dwHandle == hWnd) {
            char buf[256],*ch ;
            int addr,linenum ;
            SendMessage(ptr->dwHandle,EM_GETSEL,(WPARAM) &linenum,0) ;

            linenum = SendMessage(ptr->dwHandle,EM_LINEFROMCHAR,linenum,0)+1 ;
            memset(buf,0,256) ;
            *(short *)buf = 256 ;

            SendMessage(ptr->dwHandle,EM_GETLINE,(WPARAM) linenum-1,(LPARAM)buf) ;
            if (Tag(TAG_BOOKMARK,ptr->dwName,linenum,0,ch = strdup(buf),0,0)) {
               strcpy(currentBM.dwName,ptr->dwName) ;
               currentBM.dwLineNo = linenum ;
            }  else
               free(ch) ;
            break ;
         }
      }
   
}
int findbmpos(struct filename **l, struct tag **t)
{
   struct filename *il ;
   (*l) = fileList ;
   if (currentBM.dwName[0]) {
      while ((*l)) {
         if (!stricmp(currentBM.dwName,(*l)->name))
            break ;
         (*l) = (*l)->next ;
      }
      if (!(*l))
         (*l) = fileList ;
   }
   if (!(*l))
      return 0;
   il = (*l) ;

   do {
      (*t) = (*l)->tagArray[TAG_BOOKMARK] ; 
      while ((*t)) {
         if ((*t)->lineno >= currentBM.dwLineNo|| (*l) != il)
            return 1 ;
         (*t) = (*t)->next ;
      }
      (*l) = (*l)->next ;
      if (!(*l))
         (*l) = fileList ;
      (*t)= (*l)->tagArray[TAG_BOOKMARK] ;
   }  while ((*l) != il) ;
   return 0 ;
}
void NextBookMark(void)
{
   struct filename *l ;
   struct tag * t,*st ;
   int sln ;
   if (!findbmpos(&l,&t))
      return ;
   st = t ;
   t = t->next ;
   do {
      if (t) {
         strcpy(currentBM.dwName,l->name) ;
         currentBM.dwLineNo = t->lineno ;
         CreateDrawWindow(&currentBM) ;
         return ;
      } else {
         l = l->next ;
         if (!l)
            l = fileList ;
         t = l->tagArray[TAG_BOOKMARK] ;
      }
   }  while (t != st) ;

}
void PreviousBookMark(void)
{
   DWINFO temp ;
   struct filename *l , *fl ;
   struct tag * t, *ft ;
   int sln,gotone = FALSE ;
   if (!findbmpos(&l,&t))
      return ;
   fl = fileList ;
   while (fl) {
      ft = fl->tagArray[TAG_BOOKMARK] ;
      strcpy(temp.dwName,fl->name) ;
      while(ft) {
         if (fl == l) {
            if (ft->lineno == t->lineno && gotone) {
               currentBM= temp ;
               CreateDrawWindow(&currentBM) ;
               return ;
            } else if (ft->lineno >= t->lineno) {
               while (fl) {
                  strcpy(temp.dwName,fl->name) ;
                  while (ft) {
                     temp.dwLineNo = ft->lineno ;
                     ft = ft->next ;
                  }
                  fl = fl->next ;
                  if (fl)
                     ft = fl->tagArray[TAG_BOOKMARK] ;
               }
               currentBM = temp ;
               CreateDrawWindow(&currentBM) ;
               return ;
            }
         }
         temp.dwLineNo = ft->lineno ;
         gotone = TRUE ;
         ft = ft->next ;
      }
      fl = fl->next ;
   }
}
LRESULT  CALLBACK _export BMProc( HWND hwnd, UINT iMessage, WPARAM wParam,
																		LPARAM lParam)
{
   static HFONT font ;
   LPMEASUREITEMSTRUCT mi ;
   LPDRAWITEMSTRUCT di ;
   RECT r ;
   struct filename *l = &fileList ;
   struct tag *t,*t1 ;
   HWND hwndlb ;
   int i ;
	switch(iMessage) {
		case WM_COMMAND:
         if (wParam == IDCANCEL) {
            DeleteObject(font) ;
				EndDialog(hwnd,0) ;
         }else if (wParam == IDC_BMGOTO) {
            int sel = SendDlgItemMessage(hwnd,IDC_BMLISTBOX,LB_GETCURSEL,0,0) ;
            if (sel == LB_ERR) {
               DeleteObject(font) ;
               EndDialog(hwnd,1) ;
               return 0 ;
            }
            t1 = (struct tag *)SendDlgItemMessage(hwnd,IDC_BMLISTBOX,LB_GETITEMDATA,sel,0) ;
            while (l) {
               t = l->tagArray[TAG_BOOKMARK] ;
               while  (t && t != t1)
                  t = t->next ;
               if (t) {
                  strcpy(currentBM.dwName,l->name) ;
                  currentBM.dwLineNo = t->lineno ;
                  CreateDrawWindow(&currentBM) ;
                  break  ;
               }
               l = l->next ;
            }
            DeleteObject(font) ;
            EndDialog(hwnd,1) ;
         }
			break ;
      case WM_DRAWITEM:
         di = (LPDRAWITEMSTRUCT)lParam ;
         t1 = (struct tag *)di->itemData ;
         while (l) {
            t = l->tagArray[TAG_BOOKMARK] ;
            while  (t && t != t1)
               t = t->next ;
            if (t) {
               int bk ;
               char buf[256] ;
               sprintf(buf,"%s(%d): %s",l->name,t->lineno,t->extra) ;
               if (di->itemState & ODS_SELECTED) {
                  bk = SetTextColor(di->hDC,SetBkColor(di->hDC,0)) ;
                  SetBkColor(di->hDC,bk);
               }
               TextOut(di->hDC,di->rcItem.left,di->rcItem.top,buf,strlen(buf)) ;
               if (di->itemState & ODS_SELECTED) {
                  bk = SetTextColor(di->hDC,SetBkColor(di->hDC,0)) ;
                  SetBkColor(di->hDC,bk);
               }
               break  ;
            }
            l = l->next ;
         }
         return TRUE ;         
      case WM_MEASUREITEM:
         mi = (LPMEASUREITEMSTRUCT)lParam ;
         hwndlb = GetDlgItem(hwnd,IDC_BMLISTBOX) ;
         GetClientRect(hwndlb,&r) ;
         mi->itemWidth = r.right ;
         mi->itemHeight = 14 ;
         break ;
		case WM_INITDIALOG:
			CenterWindow(hwnd) ;
         font=CreateFontIndirect(&fontdata) ;
         SendDlgItemMessage(hwnd,IDC_BMLISTBOX, WM_SETFONT, (WPARAM)font, 0) ;
         while (l) {
            t = l->tagArray[TAG_BOOKMARK] ;
            while (t) {
               SendDlgItemMessage(hwnd,IDC_BMLISTBOX, LB_ADDSTRING,0,(LPARAM)t) ;
               t = t->next ;
            }
            l = l->next ;
         }
			break ;
	}
	return 0 ;
}
void ShowBookMarks(void)
{
   DialogBox(hInstance,"BOOKMARKDLG",hwndFrame,(DLGPROC)BMProc) ;
}
void saveonetag(FILE *out, int tag)
{
   struct filename *list = fileList ;
   fprintf(out,"<BLOCK>\n") ;
   while (list) {
      if (list->tagArray[tag]) {
         struct tag *data = list->tagArray[tag] ;
         fprintf(out,"<FILE %s>\n",list->name) ;
         while (data) {
            if (tag == TAG_BOOKMARK)
               fprintf(out,"%d %d %d %s\n",data->lineno, data->charpos, data->enabled, data->extra) ;
            else
               fprintf(out,"%d %d %d\n",data->lineno, data->charpos, data->enabled) ;
            data = data->next ;
         }
         fprintf(out,"</FILE>\n") ;
      }
      list = list->next ;
   }
   fprintf(out,"</BLOCK>\n") ;
}
void SaveTags(FILE *out)
{
   fprintf(out,"<TAGS>\n") ;
   saveonetag(out,TAG_BP) ;
   saveonetag(out,TAG_BOOKMARK) ;
   fprintf(out,"</TAGS>\n") ;
}
int restoreonetag(FILE *in, int tag)
{
   char buf[256] ;
   struct filename *l,**ls = &fileList ;
   struct tag * t,**ts ;
   if (ReadString(buf,in,"<BLOCK"))
      return 1 ;
   while (1) {
      if (ReadString(buf,in,0))
         return 1 ;
      if (!xstricmp(buf,"<FILE")) {
         char *p = strchr(buf,'>') ;
         if (p)
            *p = 0 ;
         l = fileList ;

         while (l) {
            if (!strcmp(l->name,buf+6))
               break ;
            l = l->next ;
         }
         if (!l) {
            l = *ls = calloc(sizeof(struct filename),1) ;
            if (!l)
               return 1 ;
            ls = &l->next ;
            strcpy(l->name,buf+6) ;
         }
         ts = &l->tagArray[tag] ;
         while (1) {
            int index ;
            if (ReadString(buf,in,0))
               return 1 ;
            if (buf[0] == '<')
               break ;
            t = *ts = calloc(sizeof(struct tag),1) ;
            if (!t)
               return 1 ;
            ts = &t->next ;
            sscanf(buf,"%d %d %d %n",&t->lineno,&t->charpos, &t->enabled, &index) ;
            if (tag == TAG_BOOKMARK)
               if (buf[index])
                  t->extra = strdup(buf + index) ;
         }
         if (xstricmp(buf,"</FILE"))
            return 1 ;
      } else if (buf[0] == '<')
         break ;
      else
         return 1 ;
   }
   if (xstricmp(buf,"</BLOCK"))
      return 1 ;
   return 0 ;
}
int RestoreTags(FILE *in)
{
   char buf[256] ;
   int i ;
   struct filename *l = fileList ;
   for (i=0; i < TAG_MAX; i++)
      TagRemoveAll(i) ;
   while (l) {
      struct filename *s = l->next ;
      free(l) ;
      l = s ;
   }
   fileList = 0 ;
   if (ReadString(buf,in,"<TAGS"))
      return 1 ;
   if (restoreonetag(in,TAG_BP))
      return 1 ;
   if (restoreonetag(in,TAG_BOOKMARK))
      return 1 ;
   if (ReadString(buf,in,"</TAGS"))
      return 1 ;
   return 0 ;
}