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

#include "types.h"
#include "input.h"
#include "fceu.h"
#include "driver.h"
#include "state.h"
#include "general.h"
#include "video.h"

static int current = 0;    // > 0 for recording, < 0 for playback
static FILE *slots[10]={0};
static uint8 joop[4];
static uint32 framets;

/* Cache variables used for playback. */
static uint32 nextts;
static int nextd;


static int CurrentMovie = 0;
static int MovieShow = 0;

static int MovieStatus[10];

static void DoEncode(int joy, int button, int);

int FCEUMOV_IsPlaying(void)
{
 if(current < 0) return(1);
 else return(0);
}

void FCEUI_SaveMovie(char *fname)
{
 FILE *fp;
 char *fn;

 if(current < 0)  /* Can't interrupt playback.*/
  return;

 if(current > 0)  /* Stop saving. */
 {
  DoEncode(0,0,1);  /* Write a dummy timestamp value so that the movie will keep
         "playing" after user input has stopped.
      */
  fclose(slots[current-1]);
  MovieStatus[current - 1] = 1;
  current=0;
  FCEU_DispMessage("Movie recording stopped.");
  return;
 }

 current=CurrentMovie;

 if(fname)
  fp = FCEUD_UTF8fopen(fname, "wb");
 else
 {
  fp=FCEUD_UTF8fopen(fn=FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0),"wb");
  free(fn);
 }

 if(!fp) return;

 FCEUSS_SaveFP(fp);
 fseek(fp, 0, SEEK_END);
 slots[current] = fp;
 memset(joop,0,sizeof(joop));
 current++;
 framets=0;
 FCEUI_SelectMovie(CurrentMovie);       /* Quick hack to display status. */
}

static void StopPlayback(void)
{
  fclose(slots[-1 - current]);
  current=0;
  FCEU_DispMessage("Movie playback stopped.");
}

void FCEUMOV_Stop(void)
{
 if(current < 0) StopPlayback();
}

void FCEUI_LoadMovie(char *fname)
{
 FILE *fp;
 char *fn;

 if(current > 0)  /* Can't interrupt recording.*/
  return;

 if(current < 0)  /* Stop playback. */
 {
  StopPlayback();
  return;
 }

 if(fname)
  fp = FCEUD_UTF8fopen(fname, "rb");
 else
 {
  fp=FCEUD_UTF8fopen(fn=FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0),"rb");
  free(fn);
 }
 if(!fp) return;

 if(!FCEUSS_LoadFP(fp)) return;

 current = CurrentMovie;
 slots[current] = fp;

 memset(joop,0,sizeof(joop));
 current = -1 - current;
 framets=0;
 nextts=0;
 nextd = -1;
 MovieStatus[CurrentMovie] = 1;
 FCEUI_SelectMovie(CurrentMovie);       /* Quick hack to display status. */
}


static void DoEncode(int joy, int button, int dummy)
{
 uint8 d;

 d = 0;

 if(framets >= 65536)
  d = 3 << 5;
 else if(framets >= 256)
  d = 2 << 5;
 else if(framets > 0)
  d = 1 << 5;

 if(dummy) d|=0x80;

 d |= joy << 3;
 d |= button;

 fputc(d, slots[current - 1]);
 //printf("Wr: %02x, %d\n",d,slots[current-1]);
 while(framets)
 {
  fputc(framets & 0xff, slots[current - 1]);
  //printf("Wrts: %02x\n",framets & 0xff);
  framets >>= 8;
 }
}

void FCEUMOV_AddJoy(uint8 *js)
{
 int x,y;

 if(!current) return;  /* Not playback nor recording. */

 if(current < 0)  /* Playback */
 {
  while(nextts == framets)
  {
   int tmp,ti;
   uint8 d;

   if(nextd != -1)
   {
    if(nextd&0x80)
    {
  //puts("Egads");
     FCEU_DoSimpleCommand(nextd&0x1F);
    }
    else
     joop[(nextd >> 3)&0x3] ^= 1 << (nextd&0x7);
   }


   tmp = fgetc(slots[-1 - current]);
   d = tmp;

   if(tmp < 0)
   {
    StopPlayback();
    return;
   }

   nextts = 0;
   tmp >>= 5;
   tmp &= 0x3;
   ti=0;

  {
   int tmpfix = tmp;
   while(tmp--) { nextts |= fgetc(slots[-1 - current]) << (ti * 8); ti++; }

   // This fixes a bug in movies recorded before version 0.98.11
   // It's probably not necessary, but it may keep away CRAZY PEOPLE who recorded
   // movies on <= 0.98.10 and don't work on playback.
   if(tmpfix == 1 && !nextts)
   {nextts |= fgetc(slots[-1 - current])<<8; }
   else if(tmpfix == 2 && !nextts) {nextts |= fgetc(slots[-1 - current])<<16; }

   framets = 0;
   nextd = d;
  }

 }
  memcpy(js,joop,4);
 }
 else      /* Recording */
 {
  for(x=0;x<4;x++)
  {
   if(js[x] != joop[x])
   {
    for(y=0;y<8;y++)
     if((js[x] ^ joop[x]) & (1 << y))
      DoEncode(x, y, 0);
    joop[x] = js[x];
   }
   else if(framets == ((1<<24)-1)) DoEncode(0,0,1);  /* Overflow will happen, so do dummy update. */
  }
 }
 framets++;
}

void FCEUMOV_AddCommand(int cmd)
{
 if(current <= 0) return;  /* Return if not recording a movie */
 //printf("%d\n",cmd);
 DoEncode((cmd>>3)&0x3,cmd&0x7,1);
}

void FCEUMOV_CheckMovies(void)
{
  FILE *st=NULL;
  char *fn;
  int ssel;

  for(ssel=0;ssel<10;ssel++)
  {
   st=FCEUD_UTF8fopen(fn=FCEU_MakeFName(FCEUMKF_MOVIE,ssel,0),"rb");
   free(fn);
   if(st)
   {
    MovieStatus[ssel]=1;
    fclose(st);
   }
   else
    MovieStatus[ssel]=0;
  }

}

void FCEUI_SelectMovie(int w)
{
 if(w == -1) { MovieShow = 0; return; }
 FCEUI_SelectState(-1);

 CurrentMovie=w;
 MovieShow=180;

 if(current > 0)
  FCEU_DispMessage("-recording movie %d-",current-1);
 else if(current < 0)
  FCEU_DispMessage("-playing movie %d-",-1 - current);
 else
  FCEU_DispMessage("-select movie-");
}

void FCEU_DrawMovies(uint8 *XBuf)
{
 if(!MovieShow) return;

 FCEU_DrawNumberRow(XBuf,MovieStatus, CurrentMovie);
 MovieShow--;
}

