/* Produces module version of SmartOpenDir */
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"
#include "cmos.h"
#include "pattern.h"
#include "OSLib/filer.h"
#include "OSLib/filter.h"
#include "module.h"
#include "ftask.h"

static bool alias_set = FALSE;

static os_error rmload_syntax_error = {0,
	"SmartOpenDir load parameter syntax: [-a] -f <filename>"};

static os_error loadpatterns_error = {0,
	"SmartOpenDirLoad: "
	"You must specify a filename"};

static os_error no_filer_error = {0,
	"SmartOpenDir: "
	"Task Filer not found"};

static const char Alias_Filer_OpenDir[] = "Alias$Filer_OpenDir";
static const char SmartOpenDirAlias[] = "SmartOpenDir %*0";
static const char SmartOpenDir[] = "SmartOpenDir";

static bool post_filter_set = FALSE;
static wimp_t filer_task = 0;
#define FILTER_MASK (~((1<<3) + (1<<6) + (1<<17) + (1<<18)))

os_error const *finalise(int fatal, int podule, void *pw)
{
  fatal=fatal; podule=podule;

  if (cmos_changed)
    cmos_restore();

  free_patterns();

  if (alias_set)
    xos_set_var_val(Alias_Filer_OpenDir, 0, -1 /* Destroy */,
    	0, 4 /* Literal string */, 0, 0);

  if (post_filter_set)
  {
    xfilter_de_register_post_filter(SmartOpenDir,
    	(void const *) (int) post_filter_veneer,	/* Yeuch */
    	pw, filer_task, FILTER_MASK);
    post_filter_set = FALSE;
  }

  return 0;
}

typedef enum {
  command_SmartOpenDir,
  command_SmartOpenDirLoad
} command_numbers;

os_error const *command(char *args, int argc, int cmd_no, void *pw)
{
  os_error const *e;
  char *newcom;

  pw=pw;

  switch (cmd_no)
  {
    case command_SmartOpenDir:
      if (cmos_changed)
        cmos_restore();
      e = convert_command(args, &newcom);
      if (newcom)
        e = xos_cli(newcom);
      return e;
    case command_SmartOpenDirLoad:
      if (argc != 1)
        return &loadpatterns_error;
      return load_patterns(args);
  }

  return 0;
}

static os_error const *set_filter(void *pw)
{
  os_error const *e = find_named_task("Filer", &filer_task);
  if (e)
    return e;
  if (!filer_task)
    return &no_filer_error;
  e = xfilter_register_post_filter(SmartOpenDir,
  	(void const *) (int) post_filter_veneer,	/* Yeuch */
  	pw, filer_task, FILTER_MASK);
  if (e)
    return e;
  post_filter_set = TRUE;
  return 0;
}

static char *CRstrterm(char *s)
{
  int n;
  if (!s) return s;
  for (n=0; s[n]>31; ++n);
  s[n] = 0;
  return s;
}

extern int post_filter_handler(_kernel_swi_regs *r, void *pw)
{
  wimp_pointer ptrinfo;
  wimp_block *e = (wimp_block *) r->r[1];
#ifdef RISCOS4
  /* Have to back-up wimp block on RO4 because Filer_Layout command uses
     it as workspace */
  wimp_block backup_wb;

  if (riscos4())
    backup_wb = *e;
#endif

  pw = pw;

  if (cmos_changed)
    cmos_restore();
  switch (r->r[0])
  {
    case 3:
      xwimp_get_pointer_info(&ptrinfo);
      if (ptrinfo.buttons == 1)
      {
          /* Read title bar to open parent */
        wimp_window_info winfo;

        winfo.w = e->close.w;
        if (!xwimp_get_window_info_header_only(&winfo))
        {
          char *leaf =
          	strrchr(CRstrterm(winfo.title_data.indirected_text.text), '.');
          if (leaf)
          {
            *leaf = 0;
            match_and_set(winfo.title_data.indirected_text.text);
            *leaf = '.';
          }
        }
      }
      break;
    case 17:
    case 18:
      switch (e->message.action)
      {
        case 5:	/* Message_DataOpen */
          if (e->message.data.data_xfer.file_type == 0x1000)	/* Directory */
            match_and_set(e->message.data.data_xfer.file_name);
          break;
        case 0x400:	/* Message_FilerOpenDir */
          match_and_set(
          	((filer_message_open_dir *) &e->message.data)->dir_name);
          break;
        case 0x402:	/* Message_FilerOpenDirAt */
          match_and_set(
          	((filer_message_open_dir_at *) &e->message.data)->dir_name);
          break;
      }
      break;
  }
#ifdef RISCOS4
  if (riscos4())
    *e = backup_wb;
#endif
  return 1;	/* Behave as if we haven't claimed an interrupt
  		   to make veneer return MOVS pc, lr */
}

#define MAXINITARGS 4

const os_error *initialise(const char *cmd_tail, int len, void *pw)
{
  char *args;
  int argc;
  char *argv[MAXINITARGS];
  int i;
  const os_error *e;

  /* Make writable version of cmd_tail */
  len = strlen(cmd_tail);
  args = malloc(len + 1);
  if (!args)
    return &nomem_error;
  memcpy(args, cmd_tail, len + 1);

  /* Split up args into argv */
  argc = 0;
  for (i = 0; i < len; ++i)
  {
    while (isspace(args[i])) ++i;
    if (!iscntrl(args[i]) && argc == MAXINITARGS)
      return &rmload_syntax_error;
    argv[argc++] = args + i;
    while (i < len && !isspace(args[i])) ++i;
    args[i] = 0;
  }

  /* Process argv */
  for (i = 0; i < argc; ++i)
  {
    if (argv[i][0] == '-')
    {
      switch (argv[i][1])
      {
        case 'a':
          if (!xos_set_var_val(Alias_Filer_OpenDir,
          	(byte const *) SmartOpenDirAlias,
          	sizeof(SmartOpenDirAlias) - 1,
          	0, 4 /* Literal string */, 0, 0))
            alias_set = TRUE;
          break;
        case 'f':
          if (++i == argc)
            return &rmload_syntax_error;
          e = load_patterns(argv[i]);
          if (e)
            return e;
          break;
        default:
          return &rmload_syntax_error;
      }
    }
    else
      return &rmload_syntax_error;
  }
  free(args);
  return set_filter(pw);
}

void service_handler(int sn, _kernel_swi_regs *r, void *pw)
{
  r=r;

  switch (sn)
  {
    case 0x87:	/* FilterManagerInstalled */
      if (!post_filter_set)
        set_filter(pw);
      break;
    case 0x88:	/* FilterManagerDying */
      post_filter_set = FALSE;
      break;
  }
}
