/*
(c) Copyright 1988-1994 Microsoft Corporation.

Microsoft Bars&Pipes Professional Source Code License

This License governs use of the accompanying Software.  
Microsoft hopes you find this Software useful.

You are licensed to do anything you want with the Software. 

In return, we simply require that you agree:

1.      Not to remove any copyright notices from the Software.

2.      That the Software comes "as is", with no warranties.  
        None whatsoever. This means no implied warranty of merchantability or 
        fitness for a particular purpose or any warranty of non-infringement.  
        Also, you must pass this disclaimer on whenever you distribute the Software.

3.      That we will not be liable for any of those types of damages known as indirect, 
        special, consequential, or incidental related to the Software or this License, 
        to the maximum extent the law permits. Also, you must pass this limitation of 
        liability on whenever you distribute the Software.

4.      That if you sue anyone over patents that you think may apply to the Software 
        for that person’s use of the Software, your license to the Software ends automatically.

5.      That the patent rights Microsoft is licensing only apply to the Software, 
        not to any derivatives you make.

6.      That your rights under the License end automatically if you breach this in any way.
*/

#include "/CompilerSpecific.h"
#include "rope.h"
#include <proto/console.h>
#include <proto/intuition.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/diskfont.h>
#include <graphics/gfxmacros.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <exec/memory.h>
#include "/v.h"

#ifdef CURSORUP
#undef CURSORUP
#define CURSORUP 76
#endif

#ifdef CURSORDOWN
#undef CURSORDOWN
#define CURSORDOWN 77
#endif

#define TABKEY 66
extern struct Tempo *tempolist;
enum
{
  TYPE_INTEGER,
  TYPE_SMPTE,
  TYPE_MEASURE
};

struct Library *ConsoleDevice = NULL;
extern struct Library *DiskfontBase;
extern struct Functions functions;

static struct IntuiMessage *GetIntuiMessage(struct Window *w)
{
  struct IntuiMessage *msg;

  msg=(struct IntuiMessage *)GetMsg(w->UserPort);
  if(msg==NULL)
  {
    WaitPort(w->UserPort);
    msg=(struct IntuiMessage *)GetMsg(w->UserPort);
  }
  return msg;
}

void RefreshRopeGadget(struct Window *win,struct Gadget *gad)
{
  RopeGadget *rope=(RopeGadget *)gad->UserData;
  struct TextFont *oldfont;
  char *p;
  int len,gx;
  RefreshGList(gad,win,0,1);
  if((rope->flags&ROPE_ACTIVE)==0 && rope->font!=NULL)
  {
    oldfont=win->RPort->Font;
    SetFont(win->RPort,rope->font);
  }
  SetAPen(win->RPort,(rope->flags&ROPE_ACTIVE) ? rope->onbackcolor : rope->offbackcolor);
  SetDrMd(win->RPort,JAM2);
  BNDRYOFF(win->RPort);
  RectFill(win->RPort,gad->LeftEdge,gad->TopEdge,gad->LeftEdge+gad->Width-1,
           gad->TopEdge+gad->Height-1);
  SetAPen(win->RPort,(rope->flags&ROPE_ACTIVE) ? rope->ontextcolor : rope->offtextcolor);
  SetBPen(win->RPort,(rope->flags&ROPE_ACTIVE) ? rope->onbackcolor : rope->offbackcolor);
  SetDrMd(win->RPort,JAM2);
  Move(win->RPort,gad->LeftEdge,gad->TopEdge+win->RPort->TxBaseline);
  p=rope->buffer+rope->disppos;
  len=strlen(p);
  for(gx=TextLength(win->RPort,p,len) ; gx > gad->Width ; gx=TextLength(win->RPort,p,len))
      --len;
  Text(win->RPort,p,len);
  /* draw line in grey beneath text */
  SetAPen(win->RPort,2);
  Move(win->RPort,gad->LeftEdge,gad->TopEdge+gad->Height);
  Draw(win->RPort,gad->LeftEdge+gad->Width-1,gad->TopEdge+gad->Height);
  if((rope->flags&ROPE_ACTIVE)==0 && rope->font!=NULL)
      SetFont(win->RPort,oldfont);
}

static void disp_textfrom(struct Window *win,struct Gadget *gad,short from)
{
  RopeGadget *rope=(RopeGadget *)gad->UserData;
  char *p;
  int len,gx,gw;
  gw=TextLength(win->RPort,rope->buffer+rope->disppos,from-rope->disppos);
  SetAPen(win->RPort,rope->onbackcolor);
  SetDrMd(win->RPort,JAM2);
  BNDRYOFF(win->RPort);
  RectFill(win->RPort,gad->LeftEdge+gw,gad->TopEdge,gad->LeftEdge+gad->Width-1,
           gad->TopEdge+gad->Height-1);
  SetAPen(win->RPort,rope->ontextcolor);
  SetBPen(win->RPort,rope->onbackcolor);
  SetDrMd(win->RPort,JAM2);
  Move(win->RPort,gad->LeftEdge+gw,gad->TopEdge+win->RPort->TxBaseline);
  p=rope->buffer+from;
  len=strlen(p);
  gw=gad->Width-gw;
  for(gx=TextLength(win->RPort,p,len) ; gx > gw ; gx=TextLength(win->RPort,p,len))
      --len;
  Text(win->RPort,p,len);
}

static void disp_curs(struct Window *win,struct Gadget *gad)
{
  RopeGadget *rope=(RopeGadget *)gad->UserData;
  Move(win->RPort,gad->LeftEdge+TextLength(win->RPort,rope->buffer+rope->disppos,
       rope->bufferpos-rope->disppos),gad->TopEdge+win->RPort->TxBaseline);
  SetDrMd(win->RPort,JAM2|COMPLEMENT);
  Text(win->RPort,rope->buffer+rope->bufferpos,1);
}

static short next_right(char *buf, short bufferpos)
{
  while (isdigit(buf[bufferpos])) bufferpos++;
  if (buf[bufferpos]=='\0')
  {
    return(short)(bufferpos-1);
  }
  return(short)(bufferpos+1);
}

static short next_left(char *buf, short bufferpos)
{
  bufferpos-=2;
  while (bufferpos>0)
  {
    if (!isdigit(buf[bufferpos]))
    {
      return(short)(bufferpos+1);
    }
    bufferpos--;
  }
  return 0;
}

static void cursor_on(struct Window *win,struct Gadget *gad,short x,short y)
{
  RopeGadget *rope=(RopeGadget *)gad->UserData;
  int gx;
  char *p;

  RefreshRopeGadget(win,gad);
  if(x!=0 || y!=0)
  {
    p=rope->buffer+rope->disppos;
    for(gx=gad->LeftEdge; *p!='\0' && x>=gx+TextLength(win->RPort,p,1) ;
        ++p,gx+=TextLength(win->RPort,p,1));
    rope->bufferpos=(p-rope->buffer);
  }
  else
      rope->bufferpos=0;
  disp_curs(win,gad);
}

static void cursor_move(struct Window *win,struct Gadget *gad,short delta)
{
  RopeGadget *rope=(RopeGadget *)gad->UserData;
  short i;

  disp_curs(win,gad);     /* turn cursor off */

  rope->bufferpos+=delta;
  if (!isdigit(rope->buffer[rope->bufferpos]))
    (delta>0) ? rope->bufferpos++ : rope->bufferpos--;
  if(rope->bufferpos<0)
      rope->bufferpos=0;
  else if(rope->bufferpos>=strlen(rope->buffer))
      rope->bufferpos=strlen(rope->buffer)-1;
  if(rope->bufferpos<rope->disppos)
  {
    rope->disppos=rope->bufferpos;
    RefreshRopeGadget(win,gad);
  }
  else
  {
    i=0;
    while(TextLength(win->RPort,rope->buffer+rope->disppos,rope->bufferpos-rope->disppos+1)>gad->Width)
    {
      i=1;
      ++rope->disppos;
    }
    if(i)
    {
        RefreshRopeGadget(win,gad);
    }
  }

  disp_curs(win,gad);     /* turn cursor back on */
}

static void add_text(struct Window *win,struct Gadget *gad,char *text)
{
  RopeGadget *rope=gad->UserData;
  int gx;

  while(*text)
  {
    if(strlen(rope->buffer)>=rope->maxchars-1)
    {
      DisplayBeep(win->WScreen);
      break;
    }
    memmove(rope->buffer+rope->bufferpos+1,rope->buffer+rope->bufferpos,strlen(rope->buffer+rope->bufferpos)+1);
    rope->buffer[rope->bufferpos]=*text;

    gx=0;
    cursor_move(win,gad,1);
    while(TextLength(win->RPort,rope->buffer+rope->disppos,rope->bufferpos-rope->disppos+1)>gad->Width)
    {
      gx=1;
      ++rope->disppos;
    }
    if(gx)
    {
        RefreshRopeGadget(win,gad);
    }
    else
        disp_textfrom(win,gad,rope->bufferpos-1);
    disp_curs(win,gad);

    ++text;
  }
}

static void sub_text(struct Window *win,struct Gadget *gad,char *text)
{
  RopeGadget *rope=gad->UserData;
  int gx;
  short initpos = rope->bufferpos;

  while(*text)
  {
    rope->buffer[rope->bufferpos]=*text;

    gx=0;
    cursor_move(win,gad,1);
    while(TextLength(win->RPort,rope->buffer+rope->disppos,rope->bufferpos-rope->disppos+1)>gad->Width)
    {
      gx=1;
      ++rope->disppos;
    }
    if(gx)
    {
        RefreshRopeGadget(win,gad);
    }
    else
        disp_textfrom(win,gad,initpos);
    disp_curs(win,gad);

    ++text;
  }
}

static void del_text(struct Window *win,struct Gadget *gad)
{
  RopeGadget *rope=gad->UserData;

  memmove(rope->buffer+rope->bufferpos,rope->buffer+rope->bufferpos+1,strlen(rope->buffer+rope->bufferpos+1)+1);
  disp_textfrom(win,gad,rope->bufferpos);
  disp_curs(win,gad);
}

static char typebuffer(char *buffer)
{
  char i=0;
  while (buffer[i]!='\0')
  {
    if (buffer[i]==':') return TYPE_SMPTE;
    if (buffer[i]=='.') return TYPE_MEASURE;
    i++;
  }
  return TYPE_INTEGER;
}

static long markertodata(char *buffer,char length)

{
    long time;
    char type=typebuffer(buffer);
    if (type==TYPE_SMPTE) {
        time = stringtoframe(buffer,0);
        if (length) time += uhirestoframe(functions.startoffset); /*because startoffset is removed by frametotime. Unreal! */
        time = uframetotime(time);
    }
    else if (type==TYPE_MEASURE){
        if ((!length)&&(strlen(buffer)>6))
        {
          if ((buffer[4]=='.') && (buffer[5]=='0') && (buffer[6]=='0'))
            buffer[6]='1';
        }
        if (length) time = stringtolength(&functions.masterclip,0,buffer,0);
        else time = stringtotime(&functions.masterclip,buffer,0);
    }
    else time = atoi(buffer);
    return(time);
}

int HandleRopeGadget(struct Window *win,struct Gadget *gad,short x,short y)
/* returns -1 if an illegal value is entered or if the same value is entered
* as was there previously. Otherwise, converts smpte frames into time or
* measures/beats/clocks into time.
* X and Y are the mouse positions relative to the window.
*/

{
  struct IntuiMessage *msg;
  long class,code,qual;
  long oldflags=win->IDCMPFlags;
  struct InputEvent ievent;
  APTR *iaddr;
  char buf[32];
  RopeGadget *rope=gad->UserData;
  long init_data = markertodata(rope->buffer,rope->flags&ROPE_LENGTH);
  long new_data;
  struct IOStdReq  ioStdReq;
  struct TextFont *oldfont=NULL;
  short done,i;

  OpenDevice("console.device",-1l,(struct IORequest *)&ioStdReq,0l);
  ConsoleDevice=(struct Library *)ioStdReq.io_Device;
  class=oldflags|RAWKEY;
  class&=(~(INTUITICKS|VANILLAKEY|MOUSEMOVE|MOUSEBUTTONS));
  ModifyIDCMP(win,class);
  rope->flags|=ROPE_ACTIVE;
  if(rope->font!=NULL)
  {
    oldfont=win->RPort->Font;
    SetFont(win->RPort,rope->font);
  }
  if(rope->undobuffer)
      strcpy(rope->undobuffer,rope->buffer);
  cursor_on(win,gad,x,y);
  /* make sure not on a '.' or ':' with next two cursor_moves */
  if (rope->bufferpos)
  {
    cursor_move(win,gad,-1);
    cursor_move(win,gad,1);
  }
  for(done=0;done==0;)
  {
    msg=GetIntuiMessage(win);
    class=msg->Class;
    if(class==GADGETDOWN && ((struct Gadget *)msg->IAddress)->GadgetID==gad->GadgetID)
    {
      cursor_on(win,gad,msg->MouseX,msg->MouseY);
      if (rope->bufferpos)
      {
        cursor_move(win,gad,-1);
        cursor_move(win,gad,1);
      }
      ReplyMsg((struct Message *)msg);
      continue;
    }
    else if(class==GADGETUP)
    {
      ReplyMsg((struct Message *)msg);
      continue;
    }
    else if(class!=RAWKEY)
    {
      PutMsg(win->UserPort,(struct Message *)msg);
      break;
    }
    code=msg->Code;
    qual=msg->Qualifier;
    iaddr=(APTR *)msg->IAddress;
    ReplyMsg((struct Message *)msg);
    switch(code)
    {
    case CURSORRIGHT:                      /* right */
      if(qual&(IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
          cursor_move(win,gad,next_right(rope->buffer,rope->bufferpos)-
            rope->bufferpos);
      else
          cursor_move(win,gad,1);
      break;
    case CURSORLEFT:                       /* left  */
      if(qual&(IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
          cursor_move(win,gad,next_left(rope->buffer,rope->bufferpos)-
            rope->bufferpos);
      else
          cursor_move(win,gad,-1);
      break;
    case CURSORUP:
      if (rope->buffer[rope->bufferpos] < '9') rope->buffer[rope->bufferpos]++;
      RefreshRopeGadget(win,gad);
      disp_curs(win,gad);
      break;
    case CURSORDOWN:
      if (rope->buffer[rope->bufferpos] > '0') rope->buffer[rope->bufferpos]--;
      RefreshRopeGadget(win,gad);
      disp_curs(win,gad);
      break;
/* I'd like this to get the next gadget, but how?
    case TABKEY:
      if (qual&(IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
      else
      break;
*/
    default:
      ievent.ie_NextEvent=NULL;
      ievent.ie_Class=IECLASS_RAWKEY;
      ievent.ie_SubClass=0;
      ievent.ie_Code=code;
      ievent.ie_Qualifier=qual;
      ievent.ie_position.ie_addr=*iaddr;
      i=RawKeyConvert(&ievent,buf,sizeof(buf),NULL);
      if(i>0)
      {
        buf[i]='\0';
        switch(tolower(buf[0]))
        {
        case 'q':                  /* amiga-q = undo */
          if(qual&AMIGAKEYS && rope->undobuffer)
          {
            i=rope->bufferpos;
            rope->disppos=rope->bufferpos=0;
            strcpy(rope->buffer,rope->undobuffer);
            cursor_on(win,gad,0,0);
            cursor_move(win,gad,i);
          }
          else
              add_text(win,gad,buf);
          break;
        case 'x':                  /* amiga-x = clear buffer */
          if(qual&AMIGAKEYS)
          {
            rope->disppos=rope->bufferpos=0;
            for (i=0;i<strlen(rope->buffer);i++)
              if (isdigit(rope->buffer[i])) rope->buffer[i]='0';
            cursor_on(win,gad,0,0);
          }
          else
              add_text(win,gad,buf);
          break;
        case 0x7f:                 /* del */
//          del_text(win,gad);
          sub_text(win,gad,"0\0");
//          cursor_move(win,gad,1);
          break;
        case 8:                    /* backspace */
          if(rope->bufferpos>0)
          {
            cursor_move(win,gad,-1);
            sub_text(win,gad,"0\0");
            cursor_move(win,gad,-1);
//            del_text(win,gad);
          }
          break;
        case 0xd:                  /* enter */
          done=1;
          break;
        default:
          if(isdigit(buf[0]))
              sub_text(win,gad,buf);
          break;
        }
      }
    }
  }
  if(oldfont)
      SetFont(win->RPort,oldfont);
  rope->flags&=(~ROPE_ACTIVE);
  RefreshRopeGadget(win,gad);
  ModifyIDCMP(win,oldflags);
  CloseDevice((struct IORequest *)&ioStdReq);
  new_data = markertodata(rope->buffer,rope->flags&ROPE_LENGTH);
  if (new_data >= 0) return new_data;
  else return init_data;
}

#define STR_GAD_LENGTH 15
static char *undobuf = "                ";

void SetRopeInfo(struct Window *win,short gadnum,char *text)
{
  struct Gadget *gad=(struct Gadget *)GetGadget(win, gadnum);
  RopeGadget *rope;
  if (gad)
  {
    if (rope=gad->UserData)
    {
      if (rope->buffer)
      {
        strncpy(rope->buffer,text,STR_GAD_LENGTH);
        RefreshRopeGadget(win,gad);
      }
    }
  }
}

void InitRopeGadget(struct Window *win,struct Gadget *gad,short next_gad,short prev_gad)
{
//  struct TextAttr ta;
  RopeGadget *rope;
  char *tbuf;
  if (tbuf=(char *)AllocMem(STR_GAD_LENGTH,MEMF_PUBLIC|MEMF_CLEAR))
  {
    if (rope=(RopeGadget *)AllocMem(sizeof(RopeGadget),MEMF_PUBLIC | MEMF_CLEAR))
    {
      rope->onbackcolor=1;
      rope->ontextcolor=0;
      rope->offbackcolor=0;
      rope->offtextcolor=1;
      rope->maxchars=12;
      rope->buffer=tbuf;
      rope->undobuffer=undobuf;
      rope->next_gad=next_gad;
      rope->prev_gad=prev_gad;
/* Used if need to change fonts
      if(fontname!=NULL)
      {
        ta.ta_Name=fontname;
        ta.ta_YSize=fontsize;
        ta.ta_Style=FS_NORMAL;
        ta.ta_Flags=FPF_ROMFONT|FPF_DESIGNED;
        rope->font=OpenFont(&ta);
        if(rope->font==NULL)
            rope->font=OpenDiskFont(&ta);
      }
*/
      gad->UserData=(APTR)rope;
      if(rope->font && gad->Height<rope->font->tf_YSize)
        gad->Height=rope->font->tf_YSize;
      RefreshRopeGadget(win,gad);
    }
    else FreeMem((char *)tbuf,STR_GAD_LENGTH);
  }
}

void InitRopeLengthGadget(struct Window *win,struct Gadget *gad,short next_gad,short prev_gad)
{
//  struct TextAttr ta;
  RopeGadget *rope;
  char *tbuf;
  if (tbuf=(char *)AllocMem(STR_GAD_LENGTH,MEMF_PUBLIC|MEMF_CLEAR))
  {
    if (rope=(RopeGadget *)AllocMem(sizeof(RopeGadget),MEMF_PUBLIC | MEMF_CLEAR))
    {
      rope->onbackcolor=1;
      rope->ontextcolor=0;
      rope->offbackcolor=0;
      rope->offtextcolor=1;
      rope->maxchars=12;
      rope->buffer=tbuf;
      rope->undobuffer=undobuf;
      rope->next_gad=next_gad;
      rope->prev_gad=prev_gad;
      rope->flags = ROPE_LENGTH;
/* Used if need to change fonts
      if(fontname!=NULL)
      {
        ta.ta_Name=fontname;
        ta.ta_YSize=fontsize;
        ta.ta_Style=FS_NORMAL;
        ta.ta_Flags=FPF_ROMFONT|FPF_DESIGNED;
        rope->font=OpenFont(&ta);
        if(rope->font==NULL)
            rope->font=OpenDiskFont(&ta);
      }
*/
      gad->UserData=(APTR)rope;
      if(rope->font && gad->Height<rope->font->tf_YSize)
        gad->Height=rope->font->tf_YSize;
      RefreshRopeGadget(win,gad);
    }
    else FreeMem((char *)tbuf,STR_GAD_LENGTH);
  }
}

void FreeRopeGadget(struct Gadget *gad)
{
  RopeGadget *rope = (RopeGadget *)gad->UserData;
  if (rope)
  {
    if (rope->buffer) FreeMem((char *)rope->buffer,STR_GAD_LENGTH);
    FreeMem(rope,sizeof(RopeGadget));
  }
}
