/* Copyright (C) 1996,1997,1998,1999 by Salvador E. Tropea (SET),
   see copyrigh file for details */
#include <ceditint.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

#define Uses_TDialog
#define Uses_TScrollBar
#define Uses_TProgram
#define Uses_TApplication
#define Uses_TDeskTop
#define Uses_TWindowList
#define Uses_TListBox
#define Uses_TSOSCol
#define Uses_TSOSListBox
#define Uses_SOStack
#define Uses_MsgBox
#define Uses_TKeys
#define Uses_TCEditor_Commands
#define Uses_TCEditor_External
#define Uses_TCEditor
#define Uses_FileOpenAid
#include <ceditor.h>
#define Uses_TSOSListBoxMsg
#include <edmsg.h>
#include <dskwin.h>
#include <dskmessa.h>
#define Uses_SETAppConst
#define Uses_SETAppHelper
#define Uses_SETAppVarious
#include <setapp.h>
#include <splinman.h>
#include <codepage.h>

static TEdMsgDialog   *MsgWindow=NULL;
static TSOSListBoxMsg *MsgList  =NULL;
static SOStack        *Stack    =NULL;
static TSOSCol        *MsgCol   =NULL;
static TRect MsgWindowRect(-1,-1,-1,-1);

TEdMsgDialog::TEdMsgDialog(const TRect &r,const char *t) :
    TDialog(r,t),
    TWindowInit(TEdMsgDialog::initFrame)
{
 TScrollBar *scrollbar;
 TRect r;
 flags = wfMove | wfGrow | wfZoom | wfClose;
 growMode = gfGrowLoY | gfGrowHiX | gfGrowHiY;
 r=getExtent();
 r.grow(-1,-1);
 scrollbar=standardScrollBar(sbVertical | sbHandleKeyboard);
 MsgList = new TSOSListBoxMsg(r,1,scrollbar);
 MsgList->growMode = gfGrowHiX | gfGrowHiY;
 scrollbar=standardScrollBar(sbHorizontal | sbHandleKeyboard);
 scrollbar->setParams(0,0,255-(r.b.x-r.a.x),40,1);
 MsgList->hScrollBar = scrollbar;
 insert(MsgList);
}

void TEdMsgDialog::changeBounds(const TRect &r)
{
  TDialog::changeBounds(r);
  MsgWindowRect = r;
}

void TEdMsgDialog::close(void)
{
 hide();
}

void TEdMsgDialog::handleEvent(TEvent& event)
{
 TDialog::handleEvent(event);
 if (event.what==evBroadcast && event.message.command==cmcUpdateCodePage)
   {
    RemapNStringCodePage((uchar *)MsgList->hScrollBar->chars,
                         (uchar *)TScrollBar::ohChars,
                         (ushort *)event.message.infoPtr,5);
    RemapNStringCodePage((uchar *)MsgList->vScrollBar->chars,
                         (uchar *)TScrollBar::ovChars,
                         (ushort *)event.message.infoPtr,5);
   }
}

TEdMsgDialog::~TEdMsgDialog()
{
 if (Stack)
   {
    delete Stack;
    Stack=0;
   }
 if (MsgCol)
   {
    delete MsgCol;
    MsgCol=0;
   }
}

static int avoidFocusAction=0;

void TSOSListBoxMsg::focusItem(ccIndex item)
{
 ccIndex old=focused;
 TSOSListBox::focusItem(item);
 if (!avoidFocusAction && item!=old)
   {
    stkHandler aux=(stkHandler)(list()->at(focused));
    aux=Stack->GetPreviousOf(aux);
    FileInfo *fI=(FileInfo *)(Stack->GetPointerOf(aux));
    if (fI->Line>=0)
      {
       TApplication::deskTop->lock();
       char *fileName=Stack->GetStrOf(Stack->GetPreviousOf(aux));
       ShowFileLine(SpLineGetNewValueOf(fI->Line,fileName),fileName);
       this->owner->select();
       TApplication::deskTop->unlock();
      }
   }
}

void TSOSListBoxMsg::selectItem(ccIndex item)
{
 TSOSListBox::selectItem(item);
 selectOK=0;
 if (!avoidFocusAction)
   {
    stkHandler aux=(stkHandler)(list()->at(focused));
    char *msg=Stack->GetStrOf(aux);
    aux=Stack->GetPreviousOf(aux);
    FileInfo *fI=(FileInfo *)(Stack->GetPointerOf(aux));
    if (fI->Line>=0)
      {
       char *fileName=Stack->GetStrOf(Stack->GetPreviousOf(aux));
       selectOK=GotoFileLine(SpLineGetNewValueOf(fI->Line,fileName),fileName,msg);
      }
   }
}

void TSOSListBoxMsg::setState(uint16 aState, Boolean enable)
{
 TSOSListBox::setState(aState,enable);
 if (aState==sfActive)
    updateCommands(state & sfActive);
}

void TSOSListBoxMsg::updateCommands(int enable)
{
 TCollection *l=list();
 if (enable && l && l->getCount()!=0)
   {
    enableCommand(cmeNextMessage);
    enableCommand(cmePrevMessage);
    enableCommand(cmcCopy);
    enableCommand(cmcSaveAs);
   }
 else
   { // Don't disable it, they must be available even if the window isn't selected
    if (!l->getCount())
      {
       disableCommand(cmeNextMessage);
       disableCommand(cmePrevMessage);
      }
    disableCommand(cmcCopy);
    disableCommand(cmcSaveAs);
   }
}

void TSOSListBoxMsg::saveAs()
{
 char fileName[PATH_MAX];
 strcpy(fileName,"messages.txt");

 // Hey man, just reuse code ;-)
 if (TCEditor::editorDialog(edSaveAs,fileName)!=cmCancel)
    save(fileName);
}

void TSOSListBoxMsg::save(char *name)
{ // Reusing the editor's dialogs this function is very complet
 if (access(name,F_OK)==0 && TCEditor::editorDialog(edFileExists,name,0)==cmNo)
    return;
 FILE *f=fopen(name,"wt");
 if (!f)
   {
    TCEditor::editorDialog(edCreateError,name);
    return;
   }
 int c=MsgCol->getCount();
 for (int i=0; i<c; i++)
    {
     fputs(MsgCol->atStr(i),f);
     fputs("\n",f);
    }
 if (ferror(f))
    TCEditor::editorDialog(edWriteError,name);
 fclose(f);
}

void TSOSListBoxMsg::copyClipboard()
{
 if (!TCEditor::clipboard)
    return;

 int c=MsgCol->getCount();
 int len=1,i,lacu;
 for (i=0; i<c; i++)
     len+=strlen(MsgCol->atStr(i))+LenEOL;

 char *buffer=new char[len];
 if (!buffer)
   {
    TCEditor::editorDialog(edOutOfMemory);
    return;
   }

 for (lacu=0, i=0; i<c; i++)
    {
     char *s=MsgCol->atStr(i);
     strcpy(buffer+lacu,s);
     lacu+=strlen(s);
     strcpy(buffer+lacu,crlf);
     lacu+=LenEOL;
    }

 TCEditor::clipboard->insertText(buffer,lacu,True);

 delete[] buffer;
}


void TSOSListBoxMsg::handleEvent(TEvent& event)
{
 if ((event.what==evKeyDown   && event.keyDown.keyCode==kbEnter) ||
     (event.what==evMouseDown && event.mouse.doubleClick))
   {
    selectItem(focused);
    clearEvent(event);
    return;
   }
 else
 if (event.what==evKeyDown && event.keyDown.keyCode==kbDel)
   {
    TCollection *l=list();
    if (l && focused<l->getCount())
      {
       l->atRemove(focused);
       int c=l->getCount();
       setRange(c);
       if (!c)
          updateCommands(0);
      }
    draw();
    clearEvent(event);
    return;
   }
 else
   if (event.what==evCommand)
     {
      switch (event.message.command)
        {
         case cmeNextMessage:
              selectNext();
              clearEvent(event);
              return;
         case cmePrevMessage:
              selectPrev();
              clearEvent(event);
              return;
         case cmcSaveAs:
              saveAs();
              clearEvent(event);
              break;
         case cmcCopy:
              copyClipboard();
              clearEvent(event);
              break;
        }
     }
 TListBox::handleEvent(event);
}

int TSOSListBoxMsg::getLineOf(int pos)
{
 stkHandler aux=(stkHandler)(list()->at(pos));
 aux=Stack->GetPreviousOf(aux);
 FileInfo *fI=(FileInfo *)(Stack->GetPointerOf(aux));
 return fI->Line;
}

void TSOSListBoxMsg::selectNext(void)
{
 int nFocus=focused+1;

 while (nFocus<range)
   {
    if (getLineOf(nFocus)>=0)
      {
       TSOSListBox::focusItem(nFocus);
       selectItem(focused);
       if (!selectOK)
          owner->select();
       return;
      }
    nFocus++;
   }
}

void TSOSListBoxMsg::selectPrev(void)
{
 int nFocus=focused-1;

 while (nFocus>=0)
   {
    if (getLineOf(nFocus)>=0)
      {
       TSOSListBox::focusItem(nFocus);
       selectItem(focused);
       if (!selectOK)
          owner->select();
       return;
      }
    nFocus--;
   }
}

static void InsertInHelper(void)
{
 if (!SearchInHelper(dktMessage,MsgWindow))
   {
    TDskWinMessage *p=new TDskWinMessage(MsgWindow);
    AddNonEditorToHelper(p);
   }
}

TEdMsgDialog *EdMessageWindowInit(int Insert)
{
 SpLinesDeleteForId(idsplError);
 // If allready initialized clean & return
 if (MsgWindow)
   {
    MsgCol->removeAll();
    MsgList->setRange(0);
    Stack->Clean();
    MsgWindow->hide();
    if (Insert)
       InsertInHelper();
    return MsgWindow;
   }
 // Create an stack for the messages:
 Stack=new SOStack;
 if (!Stack)
    return MsgWindow;
 // Create the list:
 MsgCol=new TSOSCol(8,6,Stack);
 if (!MsgCol)
    return MsgWindow;
 // Create the dialog:
 if (MsgWindowRect.a.x==-1)
   {
    MsgWindowRect = TProgram::deskTop->getExtent();
    MsgWindowRect.a.y = MsgWindowRect.b.y-7;
   }
 MsgWindow=new TEdMsgDialog(MsgWindowRect,_("Message Window"));
 if (!MsgWindow)
   return MsgWindow;
 MsgList=MsgWindow->MsgList; // Global alias
 MsgList->newList(MsgCol);

 if (Insert)
   {
    // Insert it hided because is empty
    TProgram::deskTop->lock();
    TProgram::deskTop->insert(MsgWindow);
    MsgWindow->hide();
    TProgram::deskTop->unlock();
    InsertInHelper();
   }

 return MsgWindow;
}


static
stkHandler InsertFileInfo(FileInfo &p)
{
 stkHandler aux=Stack->alloc(sizeof(FileInfo));
 if (aux!=stkNULL)
    memcpy(Stack->GetPointerOf(aux),&p,sizeof(FileInfo));
 return aux;
}

static
stkHandler addString(char *str)
{
 char *s=str;
 int x;

 for (x=0; *s; s++)
    {
     if (*s=='\t')
        x|=7;
     x++;
    }
 x++;
 stkHandler stk=Stack->alloc(x);
 if (stk==stkNULL)
    return stk;

 char *d=Stack->GetStrOf(stk);
 for (x=0,s=str; *s; s++)
    {
     if (*s=='\t')
       {
        do
          {
           *(d++)=' ';
           x++;
          }
        while (x & 7);
       }
     else
       {
        x++;
        *(d++)=*s;
       }
    }
 *d=0;

 return stk;
}

void EdShowMessage(char *msg, FileInfo &fInfo, char *fileName, Boolean remove_old)
{
 if (!MsgWindow || remove_old)
    EdMessageWindowInit();
 if (!msg)
    return;
 stkHandler aux;
 if (fileName)
   {
    aux=Stack->addStr(fileName);
    if (aux==stkNULL)
       return;
   }
 aux=InsertFileInfo(fInfo);
 if (aux==stkNULL)
    return;
 aux=addString(msg);
 if (aux==stkNULL)
    return;
 MsgCol->insert(aux);
 if (fileName && fInfo.Line>=0)
    SpLinesAdd(fileName,fInfo.Line,idsplError,False);

 TProgram::deskTop->lock();
 MsgList->updateCommands(1);
 if (MsgList->hScrollBar)
    MsgList->hScrollBar->setValue(0);
 MsgWindow->show();
 MsgWindow->select();
 MsgList->setRange(MsgCol->getCount());
 avoidFocusAction++;
 MsgList->focusItem(MsgCol->getCount()-1);
 avoidFocusAction--;
 MsgList->drawView();
 //MsgWindow->drawView(); It doesn't work, the above does
 TProgram::deskTop->unlock();
}

void EdShowMessage(char *msg,Boolean remove_old)
{
 FileInfo dummy;
 dummy.Line=-1;
 EdShowMessage(msg,dummy,0,remove_old);
}


void EdJumpToMessage(ccIndex item)
{
 if (!MsgWindow)
    EdMessageWindowInit();
 if (item<MsgCol->getCount())
   {
    avoidFocusAction=1;
    MsgList->focusItem(item);
    avoidFocusAction=0;
   }
}

int EdMessageCantMessages(void)
{
 return MsgCol->getCount();
}

void EdMessageSelectNext(void)
{
 if (MsgList)
    MsgList->selectNext();
}

void EdMessageSelectPrev(void)
{
 if (MsgList)
    MsgList->selectPrev();
}


