#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"
#include "swis.h"

#include "flex.h"
#include "wimplib.h"

#include "pinboard.h"
#include "msgtrans.h"
#include "carousel.h"
#include "indirected.h"
#include "debug.h"
#include "mystring.h"

static int carousel_default_interval=30000;
static int carousel_interval=-1;
static int carousel_current=-1;
unsigned int carousel_last_change=0;

static int *carousel_index=NULL;
static int carousel_index_length=0;
char *carousel_path=NULL;

void carousel_update_offsets(int offset,int delta)
{
  int i;
  if (carousel_index==NULL) return;

  for (i=0;i<carousel_index_length;i++) {
    if (carousel_index[i]>offset) carousel_index[i]+=delta;
  }
}

static int carousel_index_push(char *pathname,char *leafname)
{
  // allocate store

  if (carousel_index) {
    if (!flex_extend((flex_ptr)&carousel_index,(carousel_index_length+1)*sizeof(int *))) return 0;
  } else {
    if (!flex_alloc((flex_ptr)&carousel_index,sizeof(int *))) return 0;
  }

  carousel_index[carousel_index_length]=indirected_add(NULL,2+strlen(pathname)+strlen(leafname));

  if (carousel_index[carousel_index_length]!=-1) {
    debugf("Add %s.%s\n",pathname,leafname);
    sprintf(indirected(carousel_index[carousel_index_length],char *),"%s.%s",pathname,leafname);
  }

  carousel_index_length++;
  return 1;
}

static void carousel_index_remove(int entry)
{
  if (carousel_index[entry]!=-1) {
    indirected_remove(carousel_index[entry]);
    carousel_index[entry]=-1;
  }
}

static void carousel_index_clear(void)
{
  int i;

  if (carousel_index==NULL) return;

  for (i=0;i<carousel_index_length;i++) {
    debugf("remove index %d\n",i);
    carousel_index_remove(i);
  }

  debugf("free index block\n");

  flex_free((flex_ptr)&carousel_index);
  carousel_index=NULL;
  carousel_index_length=0;
}

static int _carousel_next(_backdrop_state *state)
{
  // move to the next available index that is valid
  int test_index;
  char *pathname=NULL;

  if (carousel_index_length==0) return 0;

  carousel_current=(carousel_current+1) % carousel_index_length;
  test_index=carousel_current;

  do {
    // does this one work?
    if (carousel_index[test_index]!=-1) {
      if (pathname) free(pathname);
      pathname=(char *)malloc(1+strlen(indirected(carousel_index[test_index],char *)));
      if (!pathname) {
        msgtrans_errorlookup_1("ECARMF",indirected(carousel_index[test_index],char *));
        carousel_index_remove(test_index);
      } else {
 // can't use flex for the filename
        strcpy(pathname,indirected(carousel_index[test_index],char *));
        if (backdrop_load_image(state,state->bd_flags & BD_PAINT_MASK,pathname,0,(state->bd_flags & BD_CAROUSEL_CACHE)!=0)) {
          carousel_current=test_index;
          free(pathname);
          return 1;
        } else {
          // no - bin it
          carousel_index_remove(test_index);
        }
      }
    }

    test_index=(test_index + 1) % carousel_index_length;
  } while (test_index!=carousel_current);

  if (pathname) free(pathname);

  // been around and nothing worked :(
  return 0;
}

static _kernel_oserror *carousel_import_path(char *path)
{
  int namebuffer[64+5];
  _kernel_swi_regs r;
  _kernel_oserror *e;

  // clear any existing index
  carousel_index_clear();

  if (!path && !carousel_path) return NULL;

  r.r[0]=10;
  r.r[1]=(int)(path?path:carousel_path);
  r.r[4]=0;
  do {
    r.r[2]=(int)namebuffer;
    r.r[3]=1;
    r.r[5]=276;
    r.r[6]=0;
    e=_kernel_swi(OS_GBPB,&r,&r);
    if (e) {
      carousel_index_clear();
      return e;
    }

    if (r.r[3]) {
      // we have one
      if (namebuffer[4]==1 && (namebuffer[3] & ((1<<0) | (1<<4)))!=0) {
        // files that have read access only
        if (!carousel_index_push(path,(char *)&namebuffer[5])) {
          carousel_index_clear();
          return msgtrans_errorlookup_1("ECARMEM",path?path:carousel_path);
        }
      }
    }
  } while (r.r[4]!=-1);

  return NULL;
}

_kernel_oserror *carousel_start(_backdrop_state *state,char *path)
{
  _kernel_oserror *e;

  if (!flex_alloc((flex_ptr)&carousel_path,1+strlen(path))) {
    return msgtrans_errorlookup_1("ECARMEM",path);
  }

  strcpy(carousel_path,path);

  // import the directory if we can
  e=carousel_import_path(path);
  if (e) {
    carousel_stop(state);
    return e;
  }

  carousel_current=-1;

  if (!_carousel_next(state)) {
    carousel_stop(state);
    return msgtrans_errorlookup_1("ECSEL",path);
  }

  carousel_interval=carousel_default_interval;

  _swix(OS_ReadMonotonicTime,_OUT(0),&carousel_last_change);

  return NULL;
}

void carousel_stop(_backdrop_state *state)
{
  debugf("carousel_stop\n");
  carousel_interval=-1;
  carousel_index_clear();

  if (carousel_path) {
    flex_free((flex_ptr)&carousel_path);
    carousel_path=0;
  }

  // clear backdrop
  backdrop_load_image(state,0,NULL,1,0);
  state->bd_flags &=~ BD_IMAGE_CACHE;
  backdrop_redraw_all(state);
}

int carousel_next(_backdrop_state *state)
{
  // iterate the given directory if possible
  if (!_carousel_next(state)) {
    carousel_stop(state);
    return 0;
  }
  _swix(OS_ReadMonotonicTime,_OUT(0),&carousel_last_change);
  return 1;
}

int carousel_get_interval(void)
{
  return carousel_interval;
}

void carousel_set_interval(int new_time)
{
  carousel_default_interval=new_time*100;

  if (carousel_interval!=-1) {
    // update this as well
    carousel_interval=carousel_default_interval;
  }
}

void carousel_check_update(_backdrop_state *state, char *fs, char *path)
{
  int offset;
  int fs_offset;

  if (carousel_path==NULL) return;

  // test FS
  if (strncasecmp(carousel_path,fs,fs_offset=strlen(fs))!=0) {
//    printf("different FS\n");
    return; // no match
  }
  if (*(carousel_path+fs_offset)!=':') {
//    printf("no :\n");
    return; // no match
  }

  // rescan our directory if path is the directory itself, or if it is a file that could be directly inside our directory
  if (strncasecmp(path,carousel_path+fs_offset+1,offset=strlen(carousel_path)-1-fs_offset)!=0) {
//    printf("%s = %s\n",path,carousel_path+fs_offset+1);
    return; // no match
  }

  // for us to rescan, there should be either a terminator here or a dot followed by no more dots...

  if (*(path+offset)==0) {
//    printf("terminator ok\n");
    // rescan
    if (carousel_import_path(NULL)) {
      // error!
      carousel_stop(state);
    }
    return;
  }

  if (*(path+offset)!='.') {
//    printf("%c not a dot\n",*(path+offset));
    return; // not a prefix
  }

  if (strchr(path+offset+1,'.')!=NULL) {
//    printf("more dots\n");
    return; // other subdirs involved, so skip
  }

  // rescan
  if (carousel_import_path(NULL)) {
    // error!
    carousel_stop(state);
  }
}
