/*-------------------------------------------------------*/
/* ProjectOmega                 									*/
/* Written by T.Miles												*/
/* ID: 289175															*/
/* Module:	Output Class			    						   */
/* Derived from Plugin class            						*/
/* Provides storage of output bitmaps.  Handles  			*/
/* communication between plugin, coordinator and renderer*/
/*-------------------------------------------------------*/

#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/cybergraphics_protos.h>
#include <pragmas/cybergraphics_pragmas.h>
#include <libraries/dos.h>
#include <exec/ports.h>

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

#include "misc/systemdefs.h"
#include "output/output.h"
#include "misc/omegamsg.h"
  
OmegaOutput::OmegaOutput(){
	o_Width=GLOBALWIDTH;
	o_Height=GLOBALHEIGHT;
	oa_SetName("ProjectOmega Output"); 
	o_ScrBuffer=NULL;
	rendPort=NULL;
	framesPerBeat=0;
	o_useRenderer=TRUE;
} 

OmegaOutput::~OmegaOutput(){
}

void OmegaOutput::AddTrackName(char *name){
	OmegaTrack *newTrack=new OmegaTrack(name,0,0);
	op_Tracks.AddItem(newTrack);
	o_tracksUsed.AddItem(FALSE);	// We've got a new track, but is it used?
}
	
void OmegaOutput::ClearTracks(){
	for (UINT size=op_Tracks.GetSize();size>0;size--){
		op_Tracks.DeleteItem(0);
		o_tracksUsed.DeleteItem(0);
	}
}
 
BOOL OmegaOutput::O_InitBuffers(){
	if (o_useRenderer){
		InitRastPort(&o_RPort);
		o_Width=o_mainScr->Width;
		o_Height=o_mainScr->Height;
		blitRect.MinX=0;
		blitRect.MinY=0;
		blitRect.MaxX=o_Width;
		blitRect.MaxY=o_Height;
	
		if(o_ScrBuffer=AllocScreenBuffer(o_mainScr,NULL,0)){
			o_Bitmap=o_ScrBuffer->sb_BitMap;
			o_RPort.rp_BitMap=o_Bitmap;		// Get a bitmap in the rastport
			FillPixelArray(&o_RPort,0,0,o_Width,o_Height,TRANSPARENCY);		// Clear the bitmap
		}
		else{
			Error("Unable to create screen buffers","Not enough memory?");
			return FALSE;
		}
	}
	return TRUE;
}							 


BOOL OmegaOutput::StartUp(){
	InitialConfig();
	if (OmegaPlugin::StartUp()){
		SignalMain(TRUE);
		return TRUE;
	}
	else
		return FALSE;
}

void OmegaOutput::SignalMain(BOOL init){
	dataMessage->om_Type=OMEGAOUTPUT;
	dataMessage->om_Effects=op_TrackNames;
	dataMessage->om_Misc=(UBYTE *)&oa_appTitle[0];
	dataMessage->om_ID=op_ID;
	if (init){
		dataMessage->om_EffectNo=o_useRenderer;
		dataMessage->om_Class=OM_INIT;
		dataMessage->om_ID=op_Type;
		dataMessage->om_Track=op_Tracks.GetSize();		// This way we can tell how many effects we have on init
	}
	SendToPort(coordPort);
}

// Signal the renderer that we have a frame ready
// Don't wait for a reply
void OmegaOutput::SignalRenderer(){
	dataMessage->om_Class=OM_RENDER;
	dataMessage->om_Scr=(Screen *)&o_RPort;
	dataMessage->om_ID=op_ID;
	dataMessage->om_Misc=(UBYTE *)&blitRect;
	if (rendPort)
		SendToPort(rendPort,FALSE);
}

// We can't do a normal Wait() here as we need to keep rendering
// so instead we just look at the state of the signal bits and hope for
// the best
void OmegaOutput::WaitForSignals(){
	UINT signals;
	ULONG val;
	
	signals=Wait(TIMERSIGNAL | OUTPUTSIGNAL | SIGBREAKF_CTRL_C);

	if (signals & OUTPUTSIGNAL)
		DealWithSignals();
	if (signals & SIGBREAKF_CTRL_C){
		OnStop();
		oa_SetStatus(APP_STATUS_QUIT,TRUE);
	}
	if ((signals & TIMERSIGNAL && framesPerBeat!=0)){
		OnBeat();
		SignalRenderer();
		val=ULONG(oa_Tempo*framesPerBeat);
		oa_SetTimerAlarm((ULONG)BPM_TO_ALARM(val));
	}
} 

void OmegaOutput::DealWithSignals(){
	OmegaTrack *track;
	BOOL replied=FALSE;
	ULONG val=oa_Tempo;
	FLOAT oldFPB=framesPerBeat;
	
	OmegaMessage *o_msg;
	while(o_msg=(OmegaMessage *)GetMsg(basePort)){
		replied=FALSE;
		switch(o_msg->om_Class){
		case OM_QUIT:
			ReplyMsg((Message *)o_msg);
			replied=TRUE;
			if (IsRunning())
				OnStop();
			oa_SetStatus(APP_STATUS_QUIT,TRUE);
			OnQuit();
			break;
		case OM_STOP:
			OnStop();
			OmegaPlugin::OnStop();
			break;
		case OM_START:
			op_ID=o_msg->om_ID;
			ReplyMsg((Message *)o_msg);
			replied=TRUE;
			if (!IsRunning()){
				if (op_Configured){
					OnStart();
					if (framesPerBeat!=0){		// This is a nasty check!
														// Shouldn't really check equality of a FLOAT
						val=ULONG(oa_Tempo*framesPerBeat);
						oa_SetTimerAlarm((ULONG)BPM_TO_ALARM(val));
					}
					OmegaPlugin::OnStart();
				}
				else
					Error("Can't Start Output","Not Configured Yet");
			}
			break;
		case OM_TEMPO:
			oa_Tempo=o_msg->om_Tempo;
			cout << "Tempo changed to " << oa_Tempo << endl;
			break;
		case OM_EFFECT_START:
			break;
		case OM_EFFECT_STOP:
			break;
		case OM_EFFECT_QUIT:
			break;
		case OM_EFFECT_VALUE:	
			// This msg has come from an input saying we have a new value to deal with
			// Store the new value in our internal buffer
			lastTrack=o_msg->om_Track;
			val=o_msg->om_Value;
			if (o_tracksUsed[lastTrack]==FALSE)
				o_tracksUsed.PutItem(lastTrack,TRUE);
			track=op_Tracks.GetItem(lastTrack);
			track->AddToBuffer(val);
			DrawNextFrame(lastTrack);
			replied=TRUE;
			if (o_useRenderer)
				SignalRenderer();	// Tell the renderer we're got a new frame
			break;
		case OM_INIT:
			cout << "Init msg received" << endl;
			o_mainScr=o_msg->om_Scr;
			oa_Tempo=o_msg->om_Tempo;
			op_ID=o_msg->om_ID;
			rendPort=(MsgPort *)(o_msg->om_Misc);
			replied=TRUE;
			if (O_InitBuffers())
				OnInit();
			ReplyMsg((Message *)o_msg);
			break;
		case OM_CONFIG:
			op_ID=o_msg->om_ID;
			ReplyMsg((Message *)o_msg);
			replied=TRUE;
			
			OnConfig();
			BuildTrackNames();
			// FPB might have changed - another unsafe check, but it seems to be working okay
			if (oldFPB==0 && framesPerBeat!=0){		
				val=ULONG(oa_Tempo*framesPerBeat);
				oa_SetTimerAlarm((ULONG)BPM_TO_ALARM(val));
			}
			
			// Tell the coordinator that we've finished our configuration 
			dataMessage->om_Type=OMEGAOUTPUT;
			dataMessage->om_Class=OM_CONFIG;
			dataMessage->om_ID=op_ID;			
			dataMessage->om_Track=op_Tracks.GetSize();
			dataMessage->om_Effects=op_TrackNames;
			dataMessage->om_Misc=(UBYTE *)&oa_appTitle[0];
	
			SendToPort(coordPort,FALSE);			
			break;
		default:
			replied=TRUE;
			OmegaPlugin::DealWithMessage(o_msg);
			break;
		} 
		if (!replied)
			ReplyMsg((Message *)o_msg);
	}
}

UBYTE OmegaOutput::o_GetCurrentValue(UINT trackNo){
	OmegaTrack *pTrk=op_Tracks.GetItem(trackNo);
	return pTrk->GetCurrentValue();
}

void OmegaOutput::OnSaveConfig(FILE *f){
	fprintf(f,"&\n%f\n",framesPerBeat);
}

void OmegaOutput::OnLoadConfig(FILE *f){
	BOOL loop=TRUE;
	char tag;
	
	while (loop && !feof(f)){
		tag=fgetc(f);
		if (tag=='&'){
			fscanf(f,"%f",&framesPerBeat);
			loop=FALSE;
			cout << "Value found " << framesPerBeat << endl;
		}
	}
}

void OmegaOutput::InitialConfig(){
	FILE *f;
	
	if (f=OpenConfigFile(FALSE,FALSE)){
		OnLoadConfig(f);
		fclose(f);
	}
}
	
void OmegaOutput::OnStop(){
	if(IsRunning()){
		StopConductor();
		if (o_ScrBuffer!=NULL && o_mainScr)
			FreeScreenBuffer(o_mainScr,o_ScrBuffer);
		OmegaPlugin::OnStop();
	}
}