/* Copyright (C) 1996,1997 Robert H”hne, see COPYING.RH for details */
/* This file is part of RHIDE. */
#define Uses_TFileViewer
#define Uses_TScrollBar
#define Uses_TWindow
#define Uses_TSCollection
#define Uses_TProgram
#define Uses_TDeskTop
#define Uses_TWindowList
#define Uses_TScreen
#define Uses_TPalette
#define Uses_TIndicator
#define Uses_TFileDialog
#define Uses_MsgBox
#define Uses_TFileInputLine
#define Uses_TRangeValidator
#define Uses_TKeys

#define Uses_TDirList
#define Uses_tvutilFunctions
#include <libtvuti.h>

#include <rhgdb.h>

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

#include <sys/stat.h>


/* This function is from tvedit2.cc but I don't want to link
   this program with the editor object files, because it is
   called from SelectFunctionToJump() */

ushort execDialog( TDialog *d, void *data )
{
    TView *p = TProgram::application->validView( d );
    if( p == 0 )
        return cmCancel;
    else
        {
        if( data != 0 )
            p->setData( data );
        ushort result = TProgram::deskTop->execView( p );
        if( result != cmCancel && data != 0 )
            p->getData( data );
        TObject::destroy( p );
        return result;
        }
}


#define cGDBFileViewer "\x06\x07\x09\x0A"

class GDBFileViewer : public TFileViewer
{
public:
  char *fname;
  char *bname;
  GDBFileViewer(const TRect &r,TScrollBar *ah,TScrollBar *av,const char *n,
                TIndicator *ind);
  ~GDBFileViewer();
  virtual void scrollDraw();
  virtual void draw();
  void CenterCursor();
  virtual TPalette & getPalette() const;
  virtual void handleEvent(TEvent &);
  int WhereX() { return delta.x + cursor.x; }
  int WhereY() { return delta.y + cursor.y; }
  virtual void setState(ushort, Boolean);
protected:
  TIndicator *indicator;
};

void GDBFileViewer::setState( ushort aState, Boolean enable )
{
  TFileViewer::setState(aState, enable);
  switch( aState )
  {
    case sfActive:
      if (hScrollBar != 0)
        hScrollBar->setState(sfVisible, enable);
      if (vScrollBar != 0)
        vScrollBar->setState(sfVisible, enable);
      if (indicator != 0)
        indicator->setState(sfVisible, enable);
      break;
    default:
      break;
  }
}

GDBFileViewer *current_viewer = NULL;
int current_line;

TPalette & GDBFileViewer::getPalette() const
{
  static TPalette palette(cGDBFileViewer, sizeof( cGDBFileViewer)-1);
  return palette;
}

GDBFileViewer::GDBFileViewer(const TRect &r,TScrollBar *ah,TScrollBar *av,
                             const char *n,TIndicator *ind)
    : TFileViewer(r,ah,av,n)
{
  string_dup(fname,n);
  BaseName(fname,bname);
  indicator = ind;
  showCursor();
}

GDBFileViewer::~GDBFileViewer()
{
  if (fname) delete fname;
  if (bname) delete bname;
}

void GDBFileViewer::draw()
{
  const char *p;
    
  ushort c;
  for( int i = 0; i < size.y; i++ )
  {
    TDrawBuffer b;
    if (debugger_started && this == current_viewer && (i+delta.y) == current_line)
      c = getColor(3);
    else
      if (IsBreakPointLine(bname,delta.y+i+1) >= 0)
        c = getColor(4);
      else
        c = getColor(1);
    b.moveChar( 0, ' ', c, size.x );

    if( delta.y + i < count )
    {
      p = operator [](delta.y+i);
      int offset = 0;
      while (offset < delta.x && *p)
      {
        offset++;
        if (*p != '\t' || (offset % tabsize) == 0) p++;
      }
      if (offset == delta.x)
      {
        int indent = 0;
        while (indent < size.x && *p)
        {
          char ch;
          if (*p == '\t') ch = ' ';
          else ch = *p;
          b.moveChar(indent,ch,c,1);
          offset++;
          indent++;
          if (*p != '\t' || (offset % tabsize) == 0) p++;
        }
      }
    }
    writeBuf( 0, i, size.x, 1, b );
  }
}

void GDBFileViewer::scrollDraw()
{
 TPoint  d;

 if( hScrollBar != 0 )
     d.x = hScrollBar->value;
 else
     d.x = 0;

 if( vScrollBar != 0 )
     d.y = vScrollBar->value;
 else
     d.y = 0;

 if ( (d.x - delta.x) != cursor.x || (d.y - delta.y) != cursor.y)
 {
   if (d.x - delta.x >= size.x)
   {
     delta.x = d.x - size.x + 1;
   }
   else if (d.x - delta.x < 0)
   {
     delta.x = d.x;
   }
   if (d.y - delta.y >= size.y)
   {
     delta.y = d.y - size.y + 1;
   }
   else if (d.y - delta.y < 0)
   {
     delta.y = d.y;
   }
   setCursor(d.x-delta.x,d.y-delta.y);
   if (indicator) indicator->setValue(d,False);
   if( drawLock != 0 )
       drawFlag = True;
   else
       drawView();
 }
}

void GDBFileViewer::CenterCursor()
{
#if 0
  int dy = current_line - size.y/2;
#else
  int dy = (delta.y + cursor.y) - size.y/2;
#endif
  if (dy < 0 ) dy = 0;
  delta.y = dy;
  scrollDraw();
}

int SelectFunctionToJump(char *b, unsigned l);

void GDBFileViewer::handleEvent(TEvent &event)
{
  int line;
  char temp[10];
  TFileViewer::handleEvent(event);
  switch (event.what)
  {
    case evKeyDown:
      switch (event.keyDown.keyCode)
      {
//        case RawkbCtrlJ:
        case 0x240A:
          goto label_goto_line;
        case kbEnd:
        {
          const char *p = operator[](WhereY());
          int len = 0;
          int last_nws = 0;
          while (*p)
          {
            if (*p != ' ' && *p != '\t')
            {
              len++;
              last_nws = len;
            }
            else
            {
              len++;
              if (*p == '\t')
              {
                while (len % tabsize) len++;
              }
            }
            p++;
          }
          scrollTo(last_nws,WhereY());
          clearEvent(event);
          break;
        }
        default:
          break;
      }
      break;
    case evCommand:
      switch (event.message.command)
      {
        case cmJumpFunction:
        {
          if (owner->getState(sfActive))
          {
            int bs = getBufsize();
            char *buf = (char *)malloc(bs);
            memcpy(buf,getBuffer(),bs);
            int i=0;
            /* change any \0 to \n except the last one */
            while (i<bs-1)
            {
              if (buf[i] == 0) buf[i] = '\n';
              i++;
            }
            int line = SelectFunctionToJump(buf,bs);
            free(buf);
            if (line != -1)
            {
              scrollTo(0,line-1);
              CenterCursor();
            }
          }
          clearEvent(event);
          break;
        }
        case cmGotoLine:
label_goto_line:
          line=WhereY()+1;
          sprintf(temp,"%d",line);
          if (ValidInputBox(_("Goto the line"),_("line ~n~umber"),
                         temp,10
                         ,new TRangeValidator(1,count+1)) == cmOK)
          {
            sscanf(temp,"%d",&line);
            scrollTo(0,line-1);
            CenterCursor();
          }
          clearEvent(event);
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}

#define cGDBFileWindow "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x40\x41"

class GDBFileWindow : public TWindow
{
public:
  GDBFileViewer *viewer;
  GDBFileWindow(const TRect &,const char *fname);
  virtual TPalette & getPalette() const;
  virtual void handleEvent(TEvent &);
};

void GDBFileWindow::handleEvent(TEvent &event)
{
  TWindow::handleEvent(event);
  switch (event.what)
  {
    case evBroadcast:
      switch (event.message.command)
      {
        case cmViewerAnswer:
          clearEvent(event);
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}

TPalette & GDBFileWindow::getPalette() const
{
  static TPalette palette(cGDBFileWindow, sizeof( cGDBFileWindow)-1);
  return palette;
}

GDBFileWindow::GDBFileWindow(const TRect & r,const char *fname) :
    TWindow( r, fname, 0 ),
    TWindowInit( &GDBFileWindow::initFrame )
{
    options |= ofTileable;
    TRect r( getExtent() );
    r.grow(-1, -1);
    TIndicator *indicator = 
      new TIndicator( TRect( 2, size.y - 1, 16, size.y ) );
    insert(indicator);
    TScrollBar *hsb = 
      new TScrollBar( TRect(18,size.y-1,size.x-2,size.y));
    hsb->options |= ofPostProcess;
    insert(hsb);
    viewer = new GDBFileViewer( r,
                   hsb,
                   standardScrollBar(sbVertical | sbHandleKeyboard),
                   fname,indicator);
    insert(viewer);
}

static char *find_buffer = NULL;

char *find_file(char *fname,char * & bname,Boolean & found)
{
  int i,count;
  string_free(find_buffer);
  string_dup(find_buffer,fname);
  found = True;
  BaseName(fname,bname);
  if (__file_exists(find_buffer)) return find_buffer;
  count = src_dirs->getCount();
  for (i=0;i<count;i++)
  {
    string_free(find_buffer);
    string_dup(find_buffer,(char *)src_dirs->at(i));
    expand_variables(&find_buffer);
    string_cat(find_buffer,"/");
    string_cat(find_buffer,bname);
    if (__file_exists(find_buffer)) return find_buffer;
  }
  string_free(find_buffer);
  string_dup(find_buffer,fname);
  found = False;
  return find_buffer;
}    
  
void OpenViewer(char *_fname,int line,Boolean from_debugger,
                Boolean only_focus)
{
  int i,count;
  Boolean found;
  char *_bname,*bname;
  char *fname = find_file(_fname,_bname,found);
  char full_name[512];
  GDBFileViewer *viewer;
  bname = (char *)alloca(strlen(_bname)+1);
  strcpy(bname,_bname);
  string_free(_bname);
  TWindow *window = NULL;
  TView *v = TProgram::deskTop->current;
  if (windows)
  {
    count = windows->getCount();
    for (i=0;i<count;i++)
    {
      DeskTopWindow *w = (DeskTopWindow *)windows->at(i);
      if (strcmp(w->full_name,fname) == 0 ||
          strcmp(w->base_name,bname) == 0)
      {
        window = DESKTOPWINDOW(i);
        found = True;
        break;
      }
    }
  }
  if (found == False)
  {
    ushort result;
    if (only_focus == True) return;
    messageBox(mfError | mfOKButton,_("Could not find the source "
                                      "file %s."),bname);
    TFileDialog *dialog;
    dialog = new TFileDialog(bname,_("Open a file"),
                           _("~N~ame"),fdOpenButton,0);
    result = TProgram::deskTop->execView(dialog);
    if (result != cmCancel)
    {
      dialog->getData(full_name);
      fname = full_name;
      FExpand(fname,False);
      char *dir,*name,*ext;
      split_fname(fname,dir,name,ext);
      string_free(name);
      string_free(ext);
      if (*dir) dir[strlen(dir)-1] = 0;
      if (messageBox(mfConfirmation | mfYesNoCancel,
                     _("Add '%s' to the search path for source files"),
                     dir) == cmYes)
      {
        src_dirs->insert(strdup(dir));
      }
      string_free(dir);
    }
    else
    {
      return;
    }
  }
  if (!window)
  {
    TRect r = TProgram::deskTop->getExtent();
    r.b.y -= 7;
    window = new GDBFileWindow(r,fname);
    AddWindow(window);
  }
  viewer = ((GDBFileWindow *)window)->viewer;
  if (from_debugger == True)
  {
    current_viewer = viewer;
    current_line = line-1;
  }
  TProgram::deskTop->lock();
  viewer->scrollTo(0,line-1);
  if (viewer->cursor.y == 0 ||
      viewer->cursor.y == viewer->size.y-1)
    viewer->CenterCursor();
  viewer->drawView();
  window->select();
  if (only_focus == True)
    if (v) v->select();
  TProgram::deskTop->unlock();
}

void CenterCursor()
{
  if (!current_viewer) return;
  current_viewer->CenterCursor();
}

static GDBFileViewer *viewer;

char *WhereIsCursor(int &line,int &column,char *&bname)
{
  if (!windows || windows->getCount() == 0) return NULL;
  int count = windows->getCount(),i,found=0;
  TWindow *current = (TWindow *)TProgram::deskTop->current;
  if (!current) return NULL;
  TEvent event;
  event.what = evBroadcast;
  event.message.command = cmViewerAnswer;
  current->handleEvent(event);
  if (event.what != evNothing) return NULL;
  for (i=0;i<count;i++)
  {
    if (DESKTOPWINDOW(i) == current)
    {
      found = 1;
      break;
    }
  }
  if (!found) return NULL;
  viewer = ((GDBFileWindow *)current)->viewer;
  line = viewer->WhereY() + 1;
  column = viewer->WhereX() + 1;
  if (bname) bname = viewer->bname;
  return viewer->fname;
}

static char cursor_word[256];

static inline int
iswordchar(char c)
{
  return (c == '_' || isalnum(c));
}

char *RHGDBWordUnderCursor(void)
{
  cursor_word[0] = 0;
  const char *start,*end,*line_start;
  int line,column;
  char *bname = NULL;
  if (!WhereIsCursor(line,column,bname)) return cursor_word;
  line--;
  column--;
  line_start = (*viewer)[line];
  start = end = line_start + column;
  if (!isalnum(*start)) return cursor_word;
  while (start >= line_start && iswordchar(*start)) start--;
  start++;
  if (!isalpha(*start) && *start != '_') return cursor_word;
  while (iswordchar(*end)) end++;
  end--;
  strncpy(cursor_word,start,(int)(end-start)+1);
  cursor_word[(int)(end-start)+1] = 0;
  return cursor_word;
}


