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

#include "flex.h"

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

typedef struct {
  int filename_offset;
  int flags; // paint and cache flags
} carousel;

static char *carousel_data=NULL;
static carousel *carousel_list=NULL;
static int carousel_files=0;
static int carousel_interval=-1;
static int carousel_current=-1;
unsigned int carousel_last_change=0;

void carousel_free(void)
{
  if (carousel_data) {
    flex_free((flex_ptr)&carousel_data);
    carousel_data=NULL;
  }

  if (carousel_list) {
    flex_free((flex_ptr)&carousel_list);
    carousel_list=NULL;
  }

  carousel_files=0;
  carousel_interval=-1;
  carousel_current=-1;
}

_kernel_oserror *carousel_load(char *filename)
{
  int type,length,file_version,offset,flags;
  char *data;
  _kernel_oserror *e;
  int i;

  debugf("carousel_load\n");

  carousel_free();

  e=_swix(OS_File,_INR(0,1)|_OUT(0)|_OUT(4),5,filename,&type,&length);
  if (e) return e;
  if (type!=1) {
    return msgtrans_errorlookup("ECARINV");
  }

  debugf("allocate data\n");

  if (!flex_alloc((flex_ptr)&carousel_data,length+1)) {
    return msgtrans_errorlookup("ENoMem");
  }

  memset(carousel_data,0,length+1);

  debugf("load data\n");

  e=_swix(OS_File,_INR(0,3),255,filename,carousel_data,0);
  if (e) {
    carousel_free();
    return e;
  }
  
  // test version number
  if ((i=sscanf(carousel_data,"%d %d",&file_version,&carousel_interval))!=2) {
    carousel_free();
    return msgtrans_errorlookup("ECARVER");
  }

  if (file_version!=100) {
    carousel_free();
    return msgtrans_errorlookup("ECARVER");
  }

  debugf("tested version\n");

  // work through the files

  // replace LFs                                      
  for (offset=0;offset<length;offset++) if (*(carousel_data+offset)<32) *(carousel_data+offset)=0;

  // skip header line
  for (offset=0;offset<length && *(offset+carousel_data)>31;offset++);
  offset++;

  while (offset<length) {
    while (*(carousel_data+offset)<32 && offset<length) offset++;

    if (offset+2<length) {
      // is this line valid?
      switch (*(carousel_data+offset)) {
        case 'S': // scaled
          flags=BD_SCALED;
          break;

        case 'C': // centred
          flags=BD_CENTRED;
          break;

        case 'T': // tiled
          flags=BD_TILED;
          break;

        case 'F': // fullscreen
          flags=BD_FULLSCREEN;
          break;

        default:
          carousel_free();
          return msgtrans_errorlookup("ECARVER");
      }

      offset++;

      // test for cached image
      switch (*(carousel_data+offset)) {
        case 'C':
          flags|=BD_IMAGE_CACHE;
          offset++;
          break;
          // no other suboptions available yet...
      }

      if (*(carousel_data+offset)!=' ') {
        carousel_free();
        return msgtrans_errorlookup("ECARVER");
      }

      // filename follows
      while (*(carousel_data+offset)==' ') offset++;

      carousel_files++;
      if (carousel_list==NULL) {
        if (!flex_alloc((flex_ptr)&carousel_list,sizeof(carousel))) {
          carousel_free();
          return msgtrans_errorlookup("ENoMem");
        }
      } else {
        if (!flex_extend((flex_ptr)&carousel_list,carousel_files*sizeof(carousel))) {
          carousel_free();
          return msgtrans_errorlookup("ENoMem");
        }
      }
      carousel_list[carousel_files-1].filename_offset=offset;
      carousel_list[carousel_files-1].flags=flags;
    }

    // skip to end of line
    while (*(carousel_data+offset)>31 && offset<length) offset++;
  }

  carousel_current=-1;

  return NULL;
}

void carousel_next(_backdrop_state *state)
{
  if (carousel_files==0) return;
  carousel_current=(carousel_current+1) % carousel_files;

  // preserve filename in case flex moves it
  char filename[1+strlen(carousel_data+carousel_list[carousel_current].filename_offset)];
  strcpy(filename,carousel_data+carousel_list[carousel_current].filename_offset);

  backdrop_load_image(state,carousel_list[carousel_current].flags & BD_PAINT_MASK,filename,1,(carousel_list[carousel_current].flags & BD_IMAGE_CACHE)!=0);
  _swix(OS_ReadMonotonicTime,_OUT(0),&carousel_last_change);
}

int carousel_get_interval(void)
{
  return carousel_interval;
}
