/*-------------------------------------------------------*/
/* ProjectOmega                 									*/
/* Written by T.Miles												*/
/* ID: 289175															*/
/* Module:	Animation Class									   */
/* Derived from Output class & AnimGUI class					*/
/* Example output class.  Displays animation frames in   */
/* various ways.														*/
/*-------------------------------------------------------*/

#include <exec/types.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <intuition/intuition.h>
#include <clib/cybergraphics_protos.h>
#include <cybergraphx/cybergraphics.h>
#include <pragmas/cybergraphics_pragmas.h>
#include <clib/alib_protos.h>
#include <libraries/mui.h>
#include <mui/nlist_mcc.h>

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

#include "misc/filereq.h"
#include "anim.h"
#include "misc/dirscan.h"
#include "misc/progress.h"

AnimOutput::AnimOutput(){
	totSets=0;
	curSet=0;
	curFrame=0;

	oa_SetName("Anim Output");
	oa_Author="T.Miles";
	oa_Version="1.0";
	op_Type=MAKE_TYPE('A','N');
	
	for (UINT f=0;f<MAXSETS;f++){
		totFrames[f]=0;
		for (UINT n=0;n<MAXFRAMES;n++)
			fStore[f][n].Buffer=NULL;
	}
}

AnimOutput::~AnimOutput(){
	for (UINT f=0;f<MAXSETS;f++){
		for (UINT n=0;n<MAXFRAMES;n++){
			if (fStore[f][n].Buffer)
				delete[] fStore[f][n].Buffer;
		}
	}
}

BOOL AnimOutput::StartUp(){
	oa_SetStatus(APP_STATUS_REALTIME,TRUE);
	AddTrackName("Advance");
	AddTrackName("Value");
	AddTrackName("Change_Set");
	return OmegaOutput::StartUp();
}

BOOL AnimOutput::LoadPPMTex(char *name, UINT set, UINT frame){
	FILE *file=fopen(name,"r");
	
	ULONG x,y, bufSize;
	ULONG result;
		
	if (fStore[set][frame].Buffer)	
		delete[] fStore[set][frame].Buffer;		// Delete anything we may have here!
			
	// PPM files are in format P6\nWidth\nHeight\n255\n
	if (file){
		printf("Loading set %d - frame %d\n",set,frame);
		result=fscanf(file,"P6\n%ld\n%ld\n255\n", &x, &y);
		if (result==2){
			fStore[set][frame].Width=x;
			fStore[set][frame].Height=y;
			bufSize=x*y*3;
			fStore[set][frame].Buffer=new UBYTE[bufSize];
			// read the file into the buffer
			fread(fStore[set][frame].Buffer,bufSize,1,file);
			fclose(file);
			return TRUE;
		}
		else
			cout << "Not a valid ppm file" << endl;
		fclose(file);
	}
	else
		cout << "Unable to open file" << endl;
	return FALSE;
}

void AnimOutput::DrawNextFrame(UINT trk){
	UINT bpr;
	UINT value=o_GetCurrentValue(trk);
	float scale;

	switch (trk){
	case 0:
		if (curFrame>=totFrames[curSet])
			curFrame=0;
		break;
	case 1:
		scale=(float)totFrames[curSet]/255.0f;	// Set this value on config?
		curFrame=(UINT)(scale*(float)value);
		if (curFrame>totFrames[curSet])
			curFrame=totFrames[curSet];
		break;
	case 2:
		curFrame=0;
		curSet++;
		if (curSet>=totSets)
			curSet=0;
		break;
	}
	bpr=fStore[curSet][curFrame].Width*3;
	WritePixelArray((APTR)fStore[curSet][curFrame].Buffer,0,0,bpr,
									&o_RPort,0,0,fStore[curSet][curFrame].Width,
									fStore[curSet][curFrame].Height,RECTFMT_RGB);
	if (trk==0)
		curFrame++;
}

void AnimOutput::SetFramesPerBeat(){
	ULONG val;
	
	GetAttr(MUIA_Slider_Level,sl_fpb,&val);
	if ((LONG)val>=0)				// +ve value = frames per beat
		framesPerBeat=val;
	else						// -ve value = beats per frame
		framesPerBeat=1.0/(FLOAT)abs(val);
	printf("Val: %d   FPB: %f\n",val,framesPerBeat);
}

void AnimOutput::SetSlider(){
	LONG val;
	
	if (framesPerBeat<1 && framesPerBeat>0)
		val=-(LONG)(1/framesPerBeat);
	else
		val=(LONG)framesPerBeat;
		
	SetAttrs(sl_fpb,MUIA_Slider_Level,val);
}

void AnimOutput::OnConfig(){
	char *tempName;
	char *baseFile=NULL;
	BOOL loop=TRUE;
	ULONG val,id,gui_Sig=0;
	int len,n,f;
	FileReq myReq;
	FILE *file;
	
	SetSlider();
	gui_OpenWin();

	while(loop){
		id=DoMethod((Object *)app, MUIM_Application_NewInput, &gui_Sig);
		switch (id){
		case ID_USE:
			SetFramesPerBeat();
		case ID_CANCEL:
			loop=FALSE;
			break;
		case ID_SAVE:
			SetFramesPerBeat();
			if (file=OpenConfigFile(TRUE,TRUE)){
				OnSaveConfig(file);
				fclose(file);
			}
			else
				Error("File Saving Error","Unable to Open FIle");
			break;
		case ID_LOAD:
			// Need to free the frames already loaded
			DoMethod((Object *)lv_framesets,MUIM_List_Clear);
			for (f=0;f<totSets;f++){
				for (n=0;n<MAXFRAMES;n++){
					if (fStore[f][n].Buffer){
						delete[] fStore[f][n].Buffer;
						fStore[f][n].Buffer=NULL;
					}
				}
				totFrames[f]=0;
			}
			if (file=OpenConfigFile(TRUE,FALSE)){
				OnLoadConfig(file);
				fclose(file);
			}
			else
				Error("File Loading Error","Unable to Open File");
			SetSlider();
			break;
		case ID_FILE:
			myReq.CreateRequester("Select Base File",NULL,NULL,"#?.pbm",FALSE,FALSE);	
			// Copy the filename into storage
			tempName=myReq.GetName();
			if (!myReq.Cancelled()){
				if (baseFile)
					delete[]baseFile;
				len=strlen(tempName);
				baseFile=new char[len+1];
				strncpy(baseFile, tempName,len);
				baseFile[len]=NULL;
				DoMethod((Object *)lv_framesets,MUIM_NList_InsertSingle,(APTR)baseFile,MUIV_List_Insert_Bottom);
				if (LoadFrames(baseFile,totSets)){
					totSets++;
					op_Configured=TRUE;
				}
				if (IsRunning())
					ScaleSet(totSets-1,FALSE);			
			}
			break;
		case ID_DELETE:
			GetAttr(MUIA_NList_Active,lv_framesets,&val);
			if (val==-1)
				Error("Select Frameset","To Delete");
			else{
				DoMethod((Object *)lv_framesets,MUIM_NList_Remove,val);
				// Delete the frames in the selected set
				cout << "Deleting set " << val << endl;
				for (n=0;n<MAXFRAMES;n++){
					if (fStore[val][n].Buffer){
						cout << "Deleting frame " << n << endl;
						delete[] fStore[val][n].Buffer;
						fStore[val][n].Buffer=NULL;
					}
				}
				// Shift the remaining frames over
				if (val==totSets-1)
					totFrames[val]=0;
					
				for (n=val;n<totSets-1;n++){
					totFrames[n]=totFrames[n+1];
					totFrames[n+1]=0;
					for (f=0;f<totFrames[n];f++){
						printf("Shifting set %d frame %d\n",n,f);
						fStore[n][f]=fStore[n+1][f];
					}
				}
				totSets--;
				if (totSets==0){
					totFrames[0]=0;
					op_Configured=FALSE;
				}
				curSet=0;
				printf ("Total Sets: %d\n",totSets);
				for (f=0;f<MAXSETS;f++)
					cout << "Totframes set " << f << " = " << totFrames[f] << endl;
			}
			break;
		}
		if (loop && gui_Sig)
			Wait(gui_Sig);		
	}
	
	// Will Need to use a dir scanner to find how many files we have in
	// the anim and set the frameBuffer size appropriately
	gui_CloseWin();
	delete[]baseFile;
}	

UINT AnimOutput::LoadFrames(char *baseFile, UINT set){
	char baseName[50];
	char *filename=new char[50];	// Space for filename
	char *index;
	int n, fullLen, endLen;
	BOOL loop=TRUE;
	totFrames[set]=0;
//	DirScanner scan;
	ProgressIndicator myProg; 
	
	// Get the name of first file and create a basename from it
	index=strchr(baseFile,'_');	// Look for _ in the filename
	fullLen=strlen(baseFile);
	endLen=strlen(index);
	fullLen-=endLen;					// Chop off the bit b4 the _
	strncpy(&baseName[0],baseFile,fullLen);	// Copy it over
	baseName[fullLen]=NULL;
	// Load as many files as we can
	n=1;
	// Load the images
	cout << "Loading set " << set<< endl;
	myProg.Initialise(1,NULL);
	while(loop){
		sprintf(filename,"%s_%d.pbm",baseName,n++);
		myProg.Reset(1,filename);
		loop=LoadPPMTex(filename, set, totFrames[set]);
		if (loop){
			totFrames[set]++;
		}
		myProg.Update();
	}
	delete[] filename;
	myProg.Clearup();
	return totFrames[set];
}

void AnimOutput::OnSaveConfig(FILE *f){
	ULONG entries;
	char *file=NULL;

	OmegaOutput::OnSaveConfig(f);	
	GetAttr(MUIA_NList_Entries,lv_framesets,&entries);
	
	for (UINT n=0;n<entries;n++){
		DoMethod((Object *)lv_framesets,MUIM_NList_GetEntry,n,&file);	
		fprintf(f,"%%\n%s\n",file);
	}
}

void AnimOutput::OnLoadConfig(FILE *f){
	char *file=new char[255];
	totSets=0;
	char tag;
	
	OmegaOutput::OnLoadConfig(f);
	while (!(feof(f))){
		tag=fgetc(f);
		if (tag=='%' && fscanf(f,"%s",file)==1){
			DoMethod((Object *)lv_framesets,MUIM_List_InsertSingle,(APTR)file,MUIV_List_Insert_Bottom);
			if(LoadFrames(file,totSets)){
				totSets++;
				op_Configured=TRUE;
			}
		}
	}
	delete[] file;
}

void AnimOutput::InitialConfig(){
	FILE *file;
	
	if (file=OpenConfigFile(FALSE,FALSE)){
		OnLoadConfig(file);
		fclose(file);
	}
}

void AnimOutput::ScaleSet(UINT set, BOOL prog=TRUE){
	UINT bpr, nbpr=o_Width*3;
	ProgressIndicator scaleProg;
	char *buffer=new char[30];
	
	if (prog){
		scaleProg.Initialise(totFrames[0],NULL);
		sprintf(buffer,"Scaling Frameset %d",totFrames[set]);
		scaleProg.Reset(totFrames[set],buffer);
	}
	
	cout << "Scaling set " << set << ": ";
	cout << "Tot Frames = " << totFrames[set] << endl;
	for (UINT n=0;n<totFrames[set];n++){
		cout << n << " ";
		if (fStore[set][n].Width!=o_Width || fStore[set][n].Height!=o_Height){ 
			bpr=fStore[set][n].Width*3;
			// Draw the scaled image to the bitmap
			cout << fStore[set][n].Width << " x " << fStore[set][n].Height << endl;
			ScalePixelArray((APTR)fStore[set][n].Buffer,fStore[set][n].Width,
									fStore[set][n].Height,bpr,&o_RPort,0,0,
									o_Width,o_Height,RECTFMT_RGB);
			fStore[set][n].Width=o_Width;
			fStore[set][n].Height=o_Height;
			delete[] fStore[set][n].Buffer;
			fStore[set][n].Buffer=new char[nbpr*o_Height]; // Allocate new pixel space
			if (fStore[set][n].Buffer){
			// Read back the new scaled values into our new buffer
 				ReadPixelArray((APTR)fStore[set][n].Buffer,0,0,nbpr,&o_RPort,0,0,
									o_Width,o_Height,RECTFMT_RGB);
				if (prog)
					scaleProg.Update();
			}
			else{
				Error("Couldn't Allocate Buffer","Out of Memory?");
				// Need to shift everything else along and decrease the number of frames
				// As we've lost a frame :/
				for (UINT f=n;f<MAXFRAMES-1;f++){
					fStore[set][f]=fStore[set][f+1];
					fStore[set][f+1].Buffer=NULL;
				}
				totFrames[set]--;
			}
		}
	}
	cout << endl;
	if (prog)
		scaleProg.Clearup();
	delete[] buffer;
}

// Scale all our images to fit the current screen
void AnimOutput::OnInit(){
	for (UINT f=0;f<totSets;f++)
		ScaleSet(f);

}

void AnimOutput::OnBeat(){
	DrawNextFrame(0);
}