
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

#include "oslib/mimemap.h"
#include "oslib/osfile.h"
#include "oslib/osfscontrol.h"
#include "oslib/osgbpb.h"


#define error_MEMSHORT 0


#define is_sep(ch) ((ch)=='/' || (ch)=='_' || (ch)=='.')

typedef os_error *(*traverse_handler)(const char *dir, const char *leafname, bits ftype,
	 fileswitch_object_type objtype, bool *recurse);


static bits uk_type = osfile_TYPE_TEXT;

static int indir_len;
static int outdir_len;
static char outdir[256];

static os_error *lookup_error(bits err);
static os_error *mem_alloc(size_t size, void **blk);
static os_error *mem_free(void *blk);
//static os_error *mem_realloc(void *blk, int size_increase, void **blk_out);
static os_error *traverse_dir(const char *path, traverse_handler process);


os_error *lookup_error(bits err)
{
  return (os_error*)"\0\0\0\0Not enough memory available";
}

os_error *mem_alloc(size_t size, void **blk)
{
  int *p = malloc(sizeof(int) + size);
  if (p)
  {
    *p = size;  /* we need to remember the block size to implement realloc() */
    *blk = p + 1;
    return NULL;
  }
  return lookup_error(error_MEMSHORT);
}

os_error *mem_free(void *blk)
{
  int *p = ((int*)blk) - 1;
  free(p);
  return NULL;
}

//os_error *mem_realloc(void *blk, int size_increase, void **blk_out)
//{
//  int *p = ((int*)blk) - 1;
//  int new_size = sizeof(int) + *p + size_increase;
//  int *np;
//  assert(new_size >= sizeof(int));
//  np = realloc(p, new_size);
//  if (np)
//  {
//    *np = new_size;
//    *blk_out = np + 1;
//    return NULL;
//  }
//  return lookup_error(error_MEMSHORT);
//}
/* */

bits ftype_from_ext(const char *ext)
{
  os_error *err;
  bits ftype;

  err = xmimemaptranslate_extension_to_filetype(ext, &ftype);
  if (err) return ~0U;
  return ftype;
}

os_error *traverse_dir(const char *path, traverse_handler process)
{
  osgbpb_INFO_STAMPED(80) info;
  int context = 0;
  os_error *err;

  /* consider each object in directory (and subdirectories if requested) */
  do
  {
    int read;
    err = xosgbpb_dir_entries_info_stamped(path, (osgbpb_info_stamped_list*)&info, 1,
              context, sizeof(info), NULL, &read, &context);
    if (err) return err;
    if (read)
    {
      bool recurse;
      err = process(path, info.name, info.file_type, info.obj_type, &recurse);
      if (err) return err;

      if (recurse && (info.obj_type == osfile_IS_DIR || info.obj_type == osfile_IS_IMAGE))
      {
        char *dir;
        int length = 2 + strlen(path) + strlen((char *) info.name);

        err = mem_alloc(length, (void**)&dir);
        if (err) return err;

        sprintf(dir, "%s.%s", path, info.name);

        err = traverse_dir(dir, process);
        mem_free(dir);
        if (err) return err;
      }
    }
  } while (context != -1);

  return NULL;
}

os_error *handler(const char *dir, const char *leafname, bits ftype, fileswitch_object_type objtype, bool *recurse)
{
  char filename[256];
  os_error *err;

  sprintf(filename, "%s.%s", dir, leafname);

  if (objtype == osfile_IS_FILE)
  {
    const char *sp = filename + indir_len;
    char *dp = outdir + outdir_len;
    bits ftype = osfile_TYPE_TEXT;
    bool set_type = false;
    bool change = false;

    while ((*dp++ = *sp++) != '\0');
    switch (tolower(*(dp-2)))
    {
//      case 't':
//        if (tolower(*(dp-3) == 'x' && tolower(*(dp-4)=='t') && is_sep(*(dp-5))))
//        {
//          fileswitch_object_type obj_type;
//          char sep = *(dp-5);
//          *(dp-5)='\0';
//          if (!xosfile_read_stamped(outdir, &obj_type, NULL, NULL, NULL, NULL, NULL) &&
//             obj_type == osfile_NOT_FOUND)
//            set_type = true;
//          else
//            *(dp-5) = sep;
//        }
//        break;
      case 'c':
        if (tolower(*(dp-3)) == 'c' && is_sep(*(dp-4)))
        {
          dp -= 4;
          change = true;
          set_type = true;
          break;
        }
        /* no break */
      case 'a':
      case 's':
      case 'h':
      case 'l':
      case 'y':
        if (is_sep(*(dp-3)))
        {
          dp -= 3;
          change = true;
          set_type = true;
        }
        break;

      case '+':
      case 'p':
        if (*(dp-3) == *(dp-2) && is_sep(*(dp-4)))
        {
          dp -= 4;
          change = true;
          set_type = true;
        }
        break;
    }

    if (!change)
    {
      /* rewind to the final separator to see whether this is (probably) a filetype extension */
      char *ext = dp;
      while (--ext >= outdir && !is_sep(*ext));
      if (ext >= outdir && (dp-ext) <= 5)
      {
        fileswitch_object_type obj_type;
        char sep = *ext;
        *ext++='\0';
        if (!xosfile_read_stamped(outdir, &obj_type, NULL, NULL, NULL, NULL, NULL) &&
            obj_type == osfile_NOT_FOUND)
        {
          /* check this filetype against the MimeMap */
          ftype = ftype_from_ext(ext);
          if (ftype == ~0U) ftype = uk_type;
          set_type = true;
        }
        else
          *(ext-1) = sep;
      }
    }

    if (change)
    {
      char affix[64];
      int affix_len;
      char *q = dp;

      while (*--q != '.');
      affix_len = (dp - (q + 1));
      memcpy(affix, q + 1, affix_len);

      strcpy(q + 1, dp + 1);

      printf("Creating directory '%s'\n", outdir);
      err = xosfile_create_dir(outdir, 0);
      if (err) return err;

      while (*++q != '\0');
      *q++ = '.';
      memcpy(q, affix, affix_len);
      q[affix_len] = '\0';
    }

    printf("Copying file '%s' as '%s'\n", filename, outdir);

    err = xosfscontrol_copy(filename, outdir, 0, 0,0,0,0, NULL);
    if (!err && set_type)
      err = xosfile_set_type(outdir, ftype);
    return err;
  }
  else
  {
    strcpy(outdir + outdir_len, filename + indir_len);

    printf("Creating directory '%s'\n", outdir);

    err = xosfile_create_dir(outdir, 0);
    if (!err) *recurse = true;
    return NULL;
  }
}


int main(int argc, char *argv[])
{
  os_error *err;

  if (argc < 3)
  {
    printf("Syntax: resourcer <input dir> <output dir>\n");
    return 3;
  }

  indir_len = strlen(argv[1]);
  strcpy(outdir, argv[2]);
  outdir_len = strlen(outdir);

  err = traverse_dir(argv[1], handler);
  if (err)
  {
    printf("Error: %.8X %s\n", err->errnum, err->errmess);
    return 1;
  }
  return 0;
}

