#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;
static 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;

  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) {
      char pathname[1+strlen(indirected(carousel_index[test_index],char *))]; // 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;
        return 1;
      } else {
        // no - bin it
        carousel_index_remove(test_index);
      }
    }

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

  // 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();

  r.r[0]=10;
  r.r[1]=(int)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);
        }
      }
    }
  } while (r.r[4]!=-1);

  return NULL;
}

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

  // import the directory if we can
  e=carousel_import_path(path);

  carousel_current=-1;

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

  carousel_interval=carousel_default_interval;

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

  strcpy(carousel_path,path);

  _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;
  }
}

static _kernel_oserror *carousel_reimport_path(void)
{
  char path[1+strlen(carousel_path)];

  // don't want the path to wander when flex moves...
  strcpy(path,carousel_path);
  return carousel_import_path(path);
}

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_reimport_path()) {
      // 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_reimport_path()) {
    // error!
    carousel_stop(state);
  }
}
