/*
 main.c   Midi lyrics display

 craeted 17/2/24
*/

#include "main.h"
#include "wimp.h"
#include "lib.h"


// iconbar menu
menu_t iconbar_menu =
{
  "Lyrics",NULL,6, 7,2,7,0,0,44,0,
  {
    {256,-1,IC_DEF,"Info",     NULL, 4},
    {  0,-1,IC_DEF,"Lyrics...",NULL, 9},
    {  0,-1,IC_DEF,"Help...",  NULL, 7},
    {128,-1,IC_DEF,"Quit",     NULL, 4}
  }
};
#define MENU_ICONBAR_ITEMS 4

// lyrics menu
menu_t lyrics_menu =
{
  "Lyrics",NULL,6, 7,2,7,0,0,44,0,
  {
    {256,-1,IC_DEF,"Stop",    NULL,  4},
    {  0,-1,IC_DEF,"Clear",   NULL,  5},
    {  0,-1,IC_DEF,"Save",    NULL,  4},
    {  0,-1,IC_DEF,"Help...", NULL,  7},
    {128,-1,IC_DEF,"Quit",    NULL,  4}
  }
};

ro_glbs_t ro =
{
  0,   // task handle
  {0}, // window handles
  0,   // current menu
  0,   // flags
  {
    0,   // vdu width
    0    // vdu height
  },
};

// MIDIPlay module definitions
#define MIDIPlay_Info 0x45345 // swi
// lyrics text
typedef struct mp_lyrics_s
{
  int size;      // length of buffer in bytes
  int read;      // read buffer index
  int write;     // write buffer index
  char buff[];   // buffer of "size" bytes
} mp_lyrics_t;

// Lyrics display buffer
#define COLS  80
#define ROWS 100
static int col, row, old_row;
static char text[ROWS][COLS];


/*
 * display_text
 * ------------
 * Reads text from the player buffer and writes it to the
 * row buffers then updates the display.
 * If the lyrics pointer is NULL, clear the screen
 */
void display_text(mp_lyrics_t *t)
{
  int i;

  if(t == NULL) // clear the screen
  {
    for(i=0; i<ROWS; i++)
    {
      text[i][0] = 0;
      icon_text_change(text[i] , ro.handle[WIN_LYRICS], ICON_OUT+i);
    }
    col = row = old_row = 0;
    return;
  }

  // check for buffer empty
  int read = t->read;
  int write = t->write;
  if(read == write)
    return;

  // write text to row buffers
  int size = t->size;
  char *buff = t->buff;

  while(read != write)
  {
    char c = buff[read];

    if(c != '\r') // ignore carriage returns
    {
      if(c == '\n') // new line
      {
        if(++row >= ROWS)
          row = 0;
        col = 0;
      }
      else if(col >= (COLS-1)) // text wrap
      {
        if(++row >= ROWS)
          row = 0;
        text[row][0] = c;
        col = 1;
      }
      else
        text[row][col++] = c;
      text[row][col] = 0; // string terminator
    }
    if(++read == size)
      read = 0;
  }
  t->read = read; // update player's read pointer

  // write the text rows to the window
  if(old_row != row)
  { // update all rows as a scroll has occurred
    old_row = row;
    for(i=0; i<ROWS; i++)
    {
      int r = row + i + 1;
      if(r >= ROWS)
        r -= ROWS;
      icon_text_change(text[r] , ro.handle[WIN_LYRICS], ICON_OUT+i);
    }
  }
  else // only need to update the current row
    icon_text_change(text[row] , ro.handle[WIN_LYRICS], ICON_OUT+ROWS-1);
}


/*
 * get_text
 * --------
 * If the display has not been stopped, gets the player buffer address
 * and passes it to the display function.
 */
void get_text(void)
{
  if(ro.flags & (1<<STOP))
    return;

  _kernel_swi_regs regs;

  if(!_kernel_swi(MIDIPlay_Info, &regs, &regs))
    display_text((mp_lyrics_t *)regs.r[4]);
}


/*
 * read_menus
 * ----------
 * load menu text from Messages file if possible
 */
static void read_menus(void)
{
  static char menu_strings[128]; // holds all menu strings
  char *s = menu_strings;

  s = msg_load_menu("iconbar_m", s, &iconbar_menu);
  s = msg_load_menu("lyrics_m",  s, &lyrics_menu);

  // join menus to make trees
  iconbar_menu.item[0].sub = ro.handle[WIN_INFO];
  lyrics_menu.item[2].sub = ro.handle[WIN_SAVE];
}


/*
 * save_file
 * ---------
 * Saves the displayed text as a file.
 * Uses the filename in the save dialog and the path if given. If not
 * given, uses the default directory.
 */
void save_file(char *path)
{
  _kernel_swi_regs regs;
  int blk[10];

  blk[0] = ro.handle[WIN_SAVE];
  blk[1] = ICON_SAVE_NAME;
  regs.r[1] = (int)blk;
  _kernel_swi(Wimp_GetIconState, &regs, &regs);

  char s[256];
  if(!path)
    sprintf(s, APP_DIR".%s", (char *)(blk[7])); // save to default directory
  else
    sprintf(s, "%s%s", path, (char *)(blk[7])); // save to given directory
  FILE *f;
  if((f = fopen(s, "w")) != NULL)
  {
    int i, started = 0;
    for(i=0; i<ROWS; i++)
    {
      int r = row + i + 1;
      if(r >= ROWS)
        r -= ROWS;
      // skip blank lines from the top
      if(text[r][0] != 0)
        started = 1;
      if(started)
        fprintf(f, "%s\n", text[r]);
    }
    fclose(f);
  }
}


/*
 * mouse_click
 * -----------
 */
static void mouse_click(int blk[])
{
  _kernel_swi_regs regs;

  // Iconbar
  if(blk[3] == ICONBAR)
  {
    switch(blk[2])
    {
      case MOUSE_MENU:
        open_menu(blk[0] - 64, (44*MENU_ICONBAR_ITEMS)+96, MENU_ICONBAR, (int)&iconbar_menu);
        break;

      case MOUSE_SELECT: // open console window
        open_window(ro.handle[WIN_LYRICS]);
        break;

      case MOUSE_ADJUST:
        blk[0] = ro.handle[WIN_LYRICS];
        regs.r[1] = (int)blk;
        _kernel_swi(Wimp_CloseWindow, &regs, &regs);
        break;
    }
  }

  // Text window
  else if(blk[3] == ro.handle[WIN_LYRICS])
  {
    if(blk[2] == MOUSE_MENU)
      open_menu(blk[0] - 64, blk[1], MENU_LYRICS, (int)&lyrics_menu);
  }

  // Save file window
  else if(blk[3] == ro.handle[WIN_SAVE])
  {
    switch(blk[4])
    {
      case ICON_SAVE_SPRITE:
        if(blk[2] == 4*16) // start a drag
          drag_start(blk[3], blk[4]);
        break;

      case ICON_SAVE_OK:
        save_file(0); // save to default directory
        break;
    }
  }
}


/*
 * key_press
 * ---------
 */
void key_press(int *blk)
{
  // file save
  if(blk[0] == ro.handle[WIN_SAVE])
  {
    if((blk[6] == '\r') && (blk[1] == ICON_SAVE_NAME))
      save_file(0); // save to default directory
  }

  else
  {
    _kernel_swi_regs regs;
    regs.r[0] = blk[6];
    _kernel_swi(Wimp_ProcessKey, &regs, &regs);
  }
}


/*
 * menu_selection
 * --------------
 */
static void menu_selection(int blk[])
{
  int b[5];
  _kernel_swi_regs regs;

  regs.r[1] = (int)b;
  _kernel_swi(Wimp_GetPointerInfo, &regs, &regs);

  switch(ro.cur_menu)
  {
    case MENU_ICONBAR:
      switch(blk[0])
      {
        case 1: // open text display window
          open_window(ro.handle[WIN_LYRICS]);
          break;

        case 2: // help
          _kernel_oscli("Filer_Run "RESOURCES_DIR".Help");
          break;

        case 3: // quit
          ro.flags |= (1<<QUIT);
          break;
      }
      if(b[2] == 1) // adjust
        open_menu(0, 0, ro.cur_menu, (int)&iconbar_menu);
      break;

    case MENU_LYRICS:
      switch(blk[0])
      {
        case 0: // stop/start reading
          ro.flags ^= (1<<STOP);
          if(ro.flags & (1<<STOP))
              lyrics_menu.item[0].item_flags |= IT_TICKED;
            else
              lyrics_menu.item[0].item_flags &= ~IT_TICKED;
          break;

        case 1: // clear screen
          display_text(NULL);
          break;

        case 2: // save text
          save_file(0); // save to default directory
          break;

        case 3: // help
          _kernel_oscli("Filer_Run "RESOURCES_DIR".Help");
          break;

        case 4: // quit
          ro.flags |= (1<<QUIT);
          break;
      }
      if(b[2] == 1) // adjust
        open_menu(0, 0, ro.cur_menu, (int)&lyrics_menu);
      break;

  }
}


/*
 * msg_help
 * --------
 * Supplies help text. Requires the HelpRequest message block and current menu tag.
 */
static void msg_help(int *b, int cur_menu)
{
  _kernel_swi_regs regs;
  int blk[10], ok = 0;

  if(b[8] == ro.handle[WIN_INFO])  // Info window
    ok = msg_lookup("info_wh", -1, (char *)&b[5]);

  else if(b[8] == ro.handle[WIN_LYRICS])  // Lyrics window
    ok = msg_lookup("lyrics_wh", -1, (char *)&b[5]);

  else if(b[8] == ro.handle[WIN_SAVE])  // Save file window
  {
    if((ok = msg_lookup("save_wh", b[9], (char *)&b[5])) == 0)
      ok = msg_lookup("save_wh", -1, (char *)&b[5]);
  }

  // unknown window, check for a menu
  else if(b[8] != -2)
  {
    regs.r[0] = 1;
    regs.r[1] = (int)blk;
    regs.r[2] = b[8]; // window handle
    regs.r[3] = b[9]; // icon handle
    _kernel_swi(Wimp_GetMenuState, &regs, &regs);
    switch(cur_menu)
    {
      case MENU_ICONBAR:
        ok = msg_lookup("iconbar_mh", blk[0], (char *)&b[5]);
        break;

      case MENU_LYRICS:
        ok = msg_lookup("lyrics_mh", blk[0], (char *)&b[5]);
        break;
    }
  }

  if(!ok)
    return; // no help available

  // send help text to Help application
  regs.r[0] = 17;     // User message
  regs.r[1] = (int)b;
  regs.r[2] = b[1];   // sender task handle
  b[0] = 256; // buffer length
  b[3] = b[2];
  b[4] = MESSAGE_HELPREPLY;
  _kernel_swi(Wimp_SendMessage, &regs, &regs);
}


/*
 * wimp_msg
 * --------
 * Handles all messages
 */
static void wimp_msg(int blk[], int msg)
{
  switch (blk[4]) // message action
  {
    case MESSAGE_QUIT:
      ro.flags |= (1<<QUIT);
      break;

    case MESSAGE_DATASAVEACK: // end of drag, save song
      save_file((char *)&blk[11]); // use new path
      break;

    case MESSAGE_HELPREQUEST:
      msg_help(blk, ro.cur_menu);
      break;
  }
}


/*
 * loadtemplates
 * -------------
 * returns 0 if ok or error number
 */
static int loadtemplates(char * name)
{
  const char tmpl[NUM_WINDOWS][8] = {"Info","Console","Save"};
  _kernel_swi_regs regs;
  int i, err = 0;

  regs.r[0] = READ_CATINFO;
  regs.r[1] = (int)name;
  _kernel_swi(OS_FILE,&regs,&regs);
  if(regs.r[0] != IS_FILE)
    err = 10; // Cannot open templates file
  else
  {
    regs.r[1] = (int)name;
    _kernel_swi(Wimp_OpenTemplate, &regs, &regs);

      for(i=0; i<NUM_WINDOWS; i++)
        if((ro.handle[i] = loadtemplate(tmpl[i], 0)) == 0)
        {
          err = 12 + i; // Cannot find template
          break;
        }
    _kernel_swi(Wimp_CloseTemplate, &regs, &regs);
  }

  return err;
}


/*
 * terminate_app
 * -------------
 */
static void terminate_app(void)
{
  _kernel_swi_regs regs;

  if(ro.flags & (1<<MESSAGES_OPEN))
  {
    if(ro.flags & (1<<TEMPLATES_OK))
    {
    }
    msg_close();
  }

  regs.r[0] = ro.task_handle;
  regs.r[1] = *(int *)"TASK";
  _kernel_swi(Wimp_CloseDown, &regs, &regs);
}


/*
 * main
 * ----
 * Main Wimp Poll loop.
 */
int main(int argc, char *argv[])
{
  _kernel_swi_regs regs;
  int blk[64];
  int msglist[] = {0};
  int handle = 0;
  int err = 0;

  // find out if we are already running
  regs.r[0] = 0;
  while((regs.r[0] >= 0) && !handle)
  {
    regs.r[1] = (int)blk;
    regs.r[2] = 4*sizeof(int);
    _kernel_swi(TaskManager_EnumerateTasks, &regs, &regs);
    if(strcmp((char *)blk[1], APP_NAME) == 0)
      return 0;
  }

  // read command line options
  int i = 0;
  while(++i < argc)
    if(argv[i][0] == '-') // option
      switch (argv[i][1])
      {
        case 'c': // centralise windows on startup
          ro.flags |= (1<<CENTRALISE);
          break;
      }

  regs.r[0] = 350;
  regs.r[1] = *(int *)"TASK";
  regs.r[2] = (int)APP_NAME;
  regs.r[3] = (int)msglist;
  _kernel_swi(Wimp_Initialise, &regs, &regs);
  ro.task_handle = regs.r[1];

  /* create iconbar icon */
  blk[0] = -1; // r/h icon
  blk[1] = 0;
  blk[2] = 0;
  blk[3] = 68;
  blk[4] = 68;
  blk[5] = 0x0000301a;
  strcpy((char *)&blk[6], "!"APP_SPRITE);
  regs.r[0] = 0;
  regs.r[1] = (int)blk;
  _kernel_swi(Wimp_CreateIcon, &regs, &regs);

  atexit(terminate_app);

  if(!msg_open(RESOURCES_DIR".Messages"))
    report_error("Cannot open messages file", 1);
  else
  {
    ro.flags |= (1<<MESSAGES_OPEN);
    read_vdu_vars();

    if((err = loadtemplates(RESOURCES_DIR".Templates")) != 0)
      report_error_number(err, 1);
    else
    {
      ro.flags |= (1<<TEMPLATES_OK);
      read_menus();

      while (!(ro.flags & (1<<QUIT)))
      {
        _kernel_swi(OS_ReadMonotonicTime, &regs, &regs);
        regs.r[2] = regs.r[0] + 5; // 20Hz poll rate
        regs.r[0] = 0x3830; // poll mask
        regs.r[1] = (int)blk;
        _kernel_swi(Wimp_PollIdle, &regs, &regs);
        switch (regs.r[0]) // result
        {
          case 0: get_text(); break; // null reason
          case 2: _kernel_swi(Wimp_OpenWindow, &regs, &regs); break;
          case 3: _kernel_swi(Wimp_CloseWindow, &regs, &regs); break;
          case 6: mouse_click(blk);    break;
          case 7: drag_return(); break;
          case 8: key_press(blk); break;
          case 9: menu_selection(blk); break;

          case 17:
          case 18:
          case 19: wimp_msg(blk, regs.r[0]); break;
        }
      }
    }
  }

  return 0;
}

