/*
  MidiMan - driver management for midi support

  modules.c
  ---------
  3/4/23
*/

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

module_t module[MAX_MODULES];
int modules; // number of modules in <MIDISupport$Dir>.Drivers directory

/*
 * list_drivers
 * ------------
 * Lists modules in the directory branch at 'path'.
 */
static void list_drivers(char *path)
{
  _kernel_swi_regs regs;
  int i, blk[20];

  regs.r[0] = 10; // read entries and info from given directory
  regs.r[1] = (int)path;
  regs.r[2] = (int)blk;
  regs.r[4] = 0;
  regs.r[5] = 80;
  regs.r[6] = 0;
  do
  {
    regs.r[3] = 1; // read 1 object
    _kernel_swi(OS_GBPB, &regs, &regs);
    if(regs.r[3] != 0)
    {
      char s[128];
      sprintf(s, "%s.%s", path, (char *)(&blk[5]));
      if(blk[4] == 1) // file
      {
        if((blk[0] >> 8) == (0xfffff000 | MODULE))
        {
          // read help string
          FILE *f;
          if((f = fopen(s, "rb")) != NULL)
          {
            char *name = strchr(s, '.');
            name = strchr(name + 1, '.') + 1;
            strncpy(module[modules].filename, name, 64); // filename
            int addr;
            char str[64];
            fseek(f, 16, SEEK_SET);
            if(fread(&addr, 1, 4, f) == 4)
            {
              fseek(f, addr, SEEK_SET);
              fgets(module[modules].title, 32, f); // title
            }
            fseek(f, 20, SEEK_SET);
            if(fread(&addr, 1, 4, f) == 4)
            {
              fseek(f, addr, SEEK_SET);
              fgets(str, 64, f); // help string
              i = 0;
              char *v = module[modules].version;
              while((str[i] < '0') || (str[i] > '9')) i++;
              do  *v++ = str[i]; while(str[i++] != ')'); *v = 0;
            }
            fclose(f);
            // check if the module is loaded and installed
            _kernel_swi_regs r;
            r.r[0] = 18; // Lookup name
            r.r[1] = (int)module[modules].title;
            if(_kernel_swi(OS_Module, &r, &r))
              module[modules].flags &= (1<<MOD_SAVE); // reset all except MOD_SAVE
            else
            { // module is loaded
              module[modules].flags |= (1<<MOD_LOADED);
              // only set MOD_OPTIONS if the module has an options window
              if((strncmp(module[modules].title, "SerialMidi", 10) != 0) &&
                 (strncmp(module[modules].title, "MIDISynth", 9) != 0))
                module[modules].flags |= (1<<MOD_OPTIONS);

              for(i=0; i<drivers; i++)
              {
                driver_t *d = &driver[i];
                if(d->flags)
                { // Modules are assumed installed if their version and date reported by midi support matches
                  // the help string in the file header. The name may not be the same as the file's title.
                  char vers[64];
                  sprintf(vers, "%d.%02d (%s)", d->version / 100, d->version % 100, d->date);
                  if(strcmp(module[modules].version, vers) == 0)
                  { // module is installed
                    d->module = modules; // so we know which module each driver belongs to.
                    module[modules].flags |= (1<<MOD_INSTALLED);
                  }
                }
              }
            }
            modules++;
          }
          else
            report_error("can't open file", 0);
        }
      }
      else if(blk[4] == 2) // directory
        list_drivers(s);
      if(modules >= MAX_MODULES)
        return;
    }
  }
  while(regs.r[4] != -1);
}


/*
 * set_modules_window_extent
 * -------------------------
 * n - number of modules to display, 1 to 32
 */
static void set_modules_window_extent(int n)
{
  static char titl[DRIVER_LIMIT-1][24], vers[DRIVER_LIMIT-1][32]; // additional indirected text
  _kernel_swi_regs regs;
  int blk[790];
  blk[0] = ro.handle[WIN_MODULES];
  regs.r[1] = (int)blk;
  _kernel_swi(Wimp_GetWindowInfo, &regs, &regs);
  int icons = blk[22];

  if(n < 1) n = 1;
  else if(n > 32) n = 32;

  int rows = icons / 3;
  int shift = 64 * rows;
  int k = 0;
  while(n > rows)
  {
    int *icon = &blk[23]; // address of 1st row
    // create a driver icon row
    int i,j;
    for(i=0; i<3; i++)
    {
      regs.r[0] = icons;
      regs.r[1] = (int)blk;
      blk[0] = ro.handle[WIN_MODULES];
      for(j=0; j<8; j++)
        blk[j+1] = icon[j];
      blk[2] -= shift;
      blk[4] -= shift;
      if(i == ICON_MOD_TITLE)
        blk[6] = (int)&titl[k];
      else if(i == ICON_MOD_VERSION)
        blk[6] = (int)&vers[k];
      _kernel_swi(Wimp_CreateIcon, &regs, &regs);
      icons++;
      icon += 8;
    }
    shift += 64;
    rows++;
    k++;
  }
  // resize window
  regs.r[0] = ro.handle[WIN_MODULES];
  regs.r[1] = (int)&blk[11];
  blk[12] = blk[14] - 92 - (n * 64); // y min
  _kernel_swi(Wimp_SetExtent, &regs, &regs);
  // if it's open, re-open it
  regs.r[1] = (int)blk;
  blk[0] = ro.handle[WIN_MODULES];
  _kernel_swi(Wimp_GetWindowState, &regs, &regs);
  if(blk[8] & (1<<16))
  {
    blk[7] = ro.handle[WIN_MOD_PANE];
    _kernel_swi(Wimp_OpenWindow, &regs, &regs);
  }
}


/*
 * init_module_info
 * ----------------
 */
void init_module_info(void)
{
  int i;

  modules = 0;
  list_drivers("<MIDISupport$Dir>.Drivers");
  set_modules_window_extent(modules);

  int win = ro.handle[WIN_MODULES];
  for(i=0; i<modules; i++)
  {
    module_t *m = &module[i];
    icon_sprite_change(win, ICON_MOD_TITLE+(3*i), (m->flags & (1<<MOD_OPTIONS)) ? "R5,2" : "R2"); // action button : slab in
    icon_text_change(m->title, win, ICON_MOD_TITLE+(3*i));
    icon_state_change((m->flags >> MOD_LOADED) & 1, win, ICON_MOD_LOADED+(3*i));
    icon_text_change(m->version, win, ICON_MOD_VERSION+(3*i));
  }
}


/*
 * modules_mouse_click
 * -------------------
 * Load and Kill modules. This will trigger the support module to
 * issue a wimp message that will in turn cause us to reinitialise
 * the module and driver data, so nothing more to do here.
 * Returns non-zero if handled here.
 */
int modules_mouse_click(int *blk, int state)
{
  _kernel_swi_regs regs;
  _kernel_oserror *err;

  if(blk[3] == ro.handle[WIN_MODULES])
  {
    if((blk[2] == MOUSE_SELECT) || (blk[2] == MOUSE_ADJUST))
    {
      int m = blk[4] / 3;
      switch (blk[4] % 3)
      {
        case ICON_MOD_LOADED:
          if(state)
          {
            if(!(module[m].flags & (1<<MOD_LOADED)))
            {
              // load the module, it should install itself
              char s[256];
              sprintf(s, "<MIDISupport$Dir>.Drivers.%s", module[m].filename);
              regs.r[0] = 1; // RMLoad
              regs.r[1] = (int)s;
              if((err = _kernel_swi(OS_Module, &regs, &regs)) == 0)
              {
                module[m].flags |= (1<<MOD_LOADED);
                if(!(module[m].flags & (1<<MOD_INSTALLED)))
                  init_module_info();
              }
              else
                report_error(err->errmess, 1);
            }
          }

          else // remove the module. Before terminating, the module will
          {    // tell midisupport to remove it as a driver.
            regs.r[0] = 4; // RMKill
            regs.r[1] = (int)module[m].title;
            _kernel_swi(OS_Module, &regs, &regs);
            module[m].flags &= (1<<MOD_SAVE); // reset all except MOD_SAVE
            if(!(module[m].flags & (1<<MOD_INSTALLED)))
              init_module_info();
          }
          break;

        case ICON_MOD_TITLE:
          if(module[m].flags & (1<<MOD_OPTIONS))
          {
            if(!strncmp(module[m].title, "MIDIPlay", 8))
            {
              if(blk[2] == MOUSE_SELECT)
              {
                update_player_opt_win();
                open_window(ro.handle[WIN_PLAYER_OPT]);
              }
              else
              {
                blk[0] = ro.handle[WIN_PLAYER_OPT];
                regs.r[1] = (int)blk;
                _kernel_swi(Wimp_CloseWindow, &regs, &regs);
              }
            }
            else if(!strncmp(module[m].title, "USBMidi", 8))
            {
              if(blk[2] == MOUSE_SELECT)
              {
                update_usb_opt_win();
                open_window(ro.handle[WIN_USB_OPT]);
              }
              else
              {
                blk[0] = ro.handle[WIN_USB_OPT];
                regs.r[1] = (int)blk;
                _kernel_swi(Wimp_CloseWindow, &regs, &regs);
              }
            }
          }
          break;
      }
    }
  }

  else
    return 0;

  return 1;
}





