/*
  ArtToSpr Artworks/Draw to Sprite convertor
  Copyright (c) 1998 Tony Houghton

  This source is distributed under the GPL. Please see the file
  "COPYING" for details.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#include "hourglass.h"
#include "messagetra.h"
#include "osfile.h"
#include "osgbpb.h"

#include "event.h"
#include "iconbar.h"
#include "menu.h"
#include "proginfo.h"
#include "quit.h"
#include "wimplib.h"

#include "flex.h"

#include "CRstring.h"
#include "err.h"
#include "msgtrans.h"
#include "picwin.h"
#include "reply.h"
#include "rescodes.h"
#include "version.h"

#define MAXFLEX (8192*1024)

static const int OurMsgs[] = {
  Wimp_MDataLoad,
  Wimp_MDataLoadAck,
  Wimp_MDataOpen,
  Wimp_MDataSave,
  Wimp_MDataSaveAck,
  Wimp_MTaskCloseDown,
  Wimp_MPreQuit,
  Wimp_MModeChange,
  Wimp_MQuit
};

static const int OurTbox[] = { 0 };

static IdBlock global_id_block;

static ObjectId quit_dbox;
static int shutdown_sender;
static bool force_quit = false;

static bool quit_quit_handler(int c, ToolboxEvent *e, IdBlock *i, void *h)
{
  c=c; e=e; i=i; h=h;

  if (shutdown_sender)
  {
    WimpKeyPressedEvent ke;

    E(wimp_get_caret_position(&ke.caret));
    ke.key_code = 0x1fc;	/* scF12 */
    E(wimp_send_message(Wimp_EKeyPressed, &ke, shutdown_sender, 0, 0));
    force_quit = true;
  }
  else
    exit(0);

  return true;
}

/* Checks whether we can quit, returning false and opening Quit dbox if not */
static bool ok_to_quit()
{
  if (force_quit)
  {
    force_quit = false;
    return true;
  }
  if (picwin_ok_to_quit())
    return true;
  E(toolbox_show_object(Toolbox_ShowObject_AsMenu, quit_dbox,
  	Toolbox_ShowObject_Default, 0, NULL_ObjectId, NULL_ComponentId));
  return false;
}

static bool prequit_handler(WimpMessage *m, void *h)
{
  m=m;
  h=h;

  if (!ok_to_quit())
    reply(m);
  shutdown_sender = (m->data.shutdown.flags & 1) ? 0 : m->hdr.sender;
  return true;
}

static bool message_quit_handler(WimpMessage *m, void *h)
{
  m=m;
  h=h;

  exit(0);
  return true;
}

static void load_and_reply(WimpMessage *message)
{
  picwin_load(message->data.data_load.leaf_name, 0);
  message->hdr.action_code = Wimp_MDataLoadAck;
  message->hdr.your_ref = message->hdr.my_ref;
  E(wimp_send_message(Wimp_EUserMessage, message,
  	message->hdr.sender, 0, 0));
  if (!strcmp(message->data.data_load.leaf_name, "<Wimp$Scrap>"))
    xosfile_delete("<Wimp$Scrap>", 0, 0, 0, 0, 0);
}

static bool is_dir(const char *filename)
{
  fileswitch_object_type objtype;
  if (E(xosfile_read_stamped(filename, &objtype, 0, 0, 0, 0, 0))
      || objtype != 2)
    return false;
  return true;
}

static bool is_art_or_draw(const char *filename)
{
  fileswitch_object_type objtype;
  bits filetype;
  FILE *fp;
  int indic[2];
  int fr;

  if (E(xosfile_read_stamped(filename, &objtype, 0, 0, 0, 0, &filetype))
      || objtype != 1)
    return false;
  switch (filetype)
  {
    case 0xaff: case 0xd94:
      return true;
    default:
      fp = fopen(filename, "r");
      if (!fp)
        return false;
      fr = fread(indic, 4, 2, fp);
      fclose(fp);
      if (fr != 2 || indic[0] != 0x77617244 || indic[1] != 201)
        return false;
      break;
  }
  return true;
}

static bool load_handler(WimpMessage *message, void *h)
{
  h=h;

  if (message->data.data_load.destination_window != WimpWindow_Iconbar)
    return false;
  switch (message->data.data_load.file_type)
  {
    case 0xaff:
    case 0xd94:
      load_and_reply(message);
      return true;
    default:
      if (is_art_or_draw(message->data.data_load.leaf_name))
      {
        load_and_reply(message);
        return true;
      }
      break;
  }
  return false;
}

static bool save_handler(WimpMessage *message, void *h)
{
  h=h;

  if (message->data.data_load.destination_window != WimpWindow_Iconbar)
    return false;
  switch (message->data.data_load.file_type)
  {
    case 0xaff:
    case 0xd94:
      message->hdr.size = sizeof(WimpDataSaveAckMessage);
      message->hdr.action_code = Wimp_MDataSaveAck;
      message->hdr.your_ref = message->hdr.my_ref;
      message->data.data_save_ack.estimated_size = -1;
      strcpy(message->data.data_save_ack.leaf_name, "<Wimp$Scrap>");
      E(wimp_send_message(Wimp_EUserMessage, message,
      	message->hdr.sender, 0, 0));
      return true;
    default:
      return false;
  }
  return false;
}

static bool menu_quit_handler(int c, ToolboxEvent *e, IdBlock *i, void *h)
{
  c=c;
  e=e;
  i=i;
  h=h;

  if (ok_to_quit())
    exit(0);
  else
    shutdown_sender = 0;
  return true;
}

static bool toolbox_error_handler(int c, ToolboxEvent *event, IdBlock *i,
	void *h)
{
  c=c;
  i=i;
  h=h;

  /* Error block immediately follows event header, but needs casting */
  #ifndef NDEBUG
    fprintf(stderr, "Toolbox error: %s\n",
    	((_kernel_oserror *) &(&event->hdr)[1])->errmess);
  #endif
  err_check((_kernel_oserror *) &(&event->hdr)[1]);
  return true;
}

static bool object_created_handler(int c, ToolboxObjectAutoCreatedEvent *event,
	IdBlock *id, void *h)
{
  c=c; h=h;

  if (!strcmp(event->template_name, "ProgInfo"))
  {
    char verstr[128];

    sprintf(verstr, "%s (%s)", version_string, version_date());
    E(proginfo_set_version(0, id->self_id, verstr));
    return true;
  }

  return false;
}

/*
static bool catch_keys(int c, WimpPollBlock *e, IdBlock *id, void *h)
{
  c=c; id=id; h=h;

  wimp_process_key(e->key_pressed.key_code);
  return true;
}
*/

static bool ibar_click(int c, ToolboxEvent *e, IdBlock *id, void *h)
{
  ObjectId ibm, ow;

  c=c; e=e; h=h;

  E(iconbar_get_menu(0, id->self_id, &ibm));
  E(menu_get_click_show(0, ibm, ibarmenu_SHOWOPTS, &ow, 0));
  E(toolbox_show_object(0, ow, Toolbox_ShowObject_Default, 0,
  	id->self_id, id->self_component));
  return true;
}

static void mt_close()
{
  xmessagetrans_close_file((messagetrans_control_block *) &msgs_descriptor);
}

static void do_whole_dir(const char *indir, const char *outdir)
{
  char infile[256], outfile[256];
  char *inleaf;
  char *outleaf;
  int insize, outsize;
  int index, nread;
  bool done = false;

  insize = strlen(indir);
  strcpy(infile, indir);
  infile[insize] = '.';
  inleaf = infile + insize + 1;
  insize = 254 - insize;

  outsize = strlen(outdir);
  strcpy(outfile, outdir);
  outfile[outsize] = '.';
  outleaf = outfile + outsize + 1;
  outsize = 254 - outsize;

  for (index = 0; index != -1; )
  {
    if (E(xosgbpb_dir_entries(indir, (osgbpb_string_list *) inleaf, 1, index,
                              insize, "*", &nread, &index)))
      break;
    if (!nread)
      continue;
    if (is_art_or_draw(infile))
    {
      done = true;
      strcpy(outleaf, inleaf);
      printf("%s\n", msgs_plookup("Processing", inleaf, 0, 0, 0));
      picwin_load(infile, outfile);
    }
    else
    {
      printf("%s\n", msgs_plookup("Skipping", inleaf, 0, 0, 0));
    }
  }
  if (!done)
    err_report(0, msgs_plookup("NoneDone", indir, 0, 0, 0));
}

int main(int argc, char **argv)
{
  ObjectId ibar;
  ObjectId attached;
  const char *choices = 0;
  const char *infile = 0;
  const char *outfile = 0;
  bool dir = false;

  if (argc == 1)
  {
    EF(toolbox_initialise(0, 310, (int *) OurMsgs, (int *) OurTbox,
    	"<ArtToSpr$Dir>", &msgs_descriptor, &global_id_block,
    	0, 0, 0));
    EF(toolbox_create_object(0, "Iconbar", &ibar));
    EF(iconbar_get_menu(0, ibar, &attached));
    EF(menu_get_click_show(0, attached, ibarmenu_SHOWOPTS,
                           &opts_default_id, 0));
    EF(toolbox_set_client_handle(0, opts_default_id, &default_opts));
    EF(toolbox_create_object(0, "Quit", &quit_dbox));
    EF(event_initialise(&global_id_block));
    EF(event_set_mask(Wimp_Poll_PointerLeavingWindowMask |
    	Wimp_Poll_KeyPressedMask |
    	Wimp_Poll_PointerEnteringWindowMask |
    	Wimp_Poll_LoseCaretMask |
    	Wimp_Poll_GainCaretMask));
    err_set_taskname(msgs_lookup("_TaskName"), true);
    EF(event_register_message_handler(Wimp_MQuit, message_quit_handler, 0));
    EF(event_register_toolbox_handler(-1, event_QUIT, menu_quit_handler, 0));
    EF(event_register_toolbox_handler(-1, Toolbox_Error,
    	toolbox_error_handler, 0));
    EF(event_register_toolbox_handler(-1, Toolbox_ObjectAutoCreated,
    	(ToolboxEventHandler *) object_created_handler, 0));
    EF(event_register_message_handler(Wimp_MDataOpen, load_handler, 0));
    EF(event_register_message_handler(Wimp_MDataLoad, load_handler, 0));
    EF(event_register_message_handler(Wimp_MDataSave, save_handler, 0));
    EF(event_register_toolbox_handler(-1, Quit_Quit, quit_quit_handler, 0));
    EF(event_register_message_handler(Wimp_MPreQuit, prequit_handler, 0));
    /*EF(event_register_wimp_handler(-1, Wimp_EKeyPressed, catch_keys, 0));*/
    EF(event_register_toolbox_handler(ibar, Iconbar_Clicked, ibar_click, 0));
  }
  else
  {
    int size;
    char *buffer;     /* OK for this to be local, we don't need it elsewhere */

    EF(xmessagetrans_file_info("<ArtToSpr$Dir>.Messages", 0, &size));
    if ((buffer = malloc(size)) == 0)
      EF(msgs_nomem());
    EF(xmessagetrans_open_file((messagetrans_control_block *)
                               &msgs_descriptor,
                               "<ArtToSpr$Dir>.Messages", buffer));
    atexit(mt_close);
    err_set_taskname(msgs_lookup("_TaskName"), false);

    if (!strcmp(argv[1], "-c"))
    {
      if (argc != 5)
        err_complain_fatal(0, msgs_lookup("CLISyntax"));
      choices = argv[2];
      infile = argv[3];
      outfile = argv[4];
    }
    else
    {
      if (argc != 3)
        err_complain_fatal(0, msgs_lookup("CLISyntax"));
      infile = argv[1];
      outfile = argv[2];
    }

    if (!is_art_or_draw(infile))
    {
      if (is_dir(infile) || is_dir(outfile))
      {
        if (!is_dir(infile) || !is_dir(outfile))
          err_complain_fatal(0, msgs_plookup("NotBothDirs", infile, 0, 0, 0));
        else
          dir = true;
      }
      else
        err_complain_fatal(0, msgs_plookup("NotRenderable", infile, 0, 0, 0));
    }
  }

/*
  flex_initx((char *) msgs_lookup("FlexName"), (int *) &msgs_descriptor,
  	true, MAXFLEX, false);
*/
  flex_init((char *) msgs_lookup("FlexName"), (int *) &msgs_descriptor,
  	MAXFLEX);
  opts_init(choices);

  if (infile)
  {
    if (dir)
      do_whole_dir(infile, outfile);
    else
      picwin_load(infile, outfile);
  }
  else
  {
    picwin_initialise();
    for(;;)
      E(event_poll(0, 0, 0));
  }
  return 0;
}
