/*-------------------------------------------------------*/
/* ProjectOmega                 									*/
/* Written by T.Miles												*/
/* ID: 289175															*/
/* Module:	Input Class					  						   */
/* Derived from Plugin & InputGUI classes 					*/
/* Handles communication between plugin, coordinator and */
/* output modules.  Assigns value given by derived class */
/* to configured tracks as necessary						 	*/
/*-------------------------------------------------------*/


#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <exec/ports.h>
#include <string.h> 
#include <stdio.h>
#include <iostream.h>
#include <libraries/dos.h>
#include <libraries/mui.h>
#include <mui/nlist_mcc.h>
#include <mui/nlistview_mcc.h>

#include "input/input.h"
#include "base/plugin.h"

void OmegaInput::PutInTrackBuffer(UINT numValues, UBYTE *valArray){
	OmegaTrack *track;
	
	// Look through our list of tracks and see if the values pass
	// any of the conditions necessary for each track
	for (UINT n=0;n<op_Tracks.GetSize();n++){
		track=op_Tracks.GetItem(n);
		
		// If we're using the track then check it otherwise there's no point
		if(track->IsUsed() && track->ot_SatConditions(numValues, valArray)){
			SignalOutput(track);
		}  
	}
} 


OmegaInput::OmegaInput(){
	oa_Tempo=140;
	i_useTempo=FALSE;
	op_Type=0;
	oa_SetName("ProjectOmega Input");
	i_NeedMoreConfig=FALSE;
}	 

OmegaInput::~OmegaInput(){
}		

// Move this to plugin class?????
BOOL OmegaInput::StartUp(){
	UINT val;
	OmegaTrack *pTrack;
	
	op_LoadTracks(TRUE);
	// Scale the track values!
	val=op_Tracks.GetSize();
	for (UINT n=0;n<val;n++){
		pTrack=op_Tracks.GetItem(n);
		pTrack->SetScaleValue(&types,typeMap);
	}
	if (!i_NeedMoreConfig)
		gui_DisableMore();
	
	// If the plugin class can startup  & we have a gui
	if(OmegaPlugin::StartUp() && gui_app){		// This initialise sets up the msg ports
		 // Signal main task that we are ready
		dataMessage->om_Type=OMEGAINPUT;
		dataMessage->om_Class=OM_INIT;
		dataMessage->om_ID=op_Type;			
		dataMessage->om_Track=op_Tracks.GetSize();
		dataMessage->om_Effects=op_TrackNames;
		dataMessage->om_Misc=(UBYTE *)&oa_appTitle[0];
		SendToPort(coordPort);
		coordScreen=dataMessage->om_Scr;
		return TRUE;
	}
	else
		return FALSE;
}
 
//
// We now have a buffer of msgs that get sent to the output
// This is in the vain attempt to stop the outputs going mad from 
// the possibilty of the msg changing whilst it's needed 
void OmegaInput::SignalOutput(OmegaTrack *track){
	InputExtension *iExt;
	MsgPort *o_msgPort;
	while(iExt=track->GetNextOutput()){
//	while(o_msgPort=track->GetMsgPort()){
		o_msgPort=iExt->ie_MsgPort;
		dataMessage->om_Type=OMEGAINPUT;
		dataMessage->om_Class=OM_EFFECT_VALUE;
		dataMessage->om_Value=track->GetCurrentValue();
		dataMessage->om_Track=iExt->ie_EffectNo;;
		SendToPort(o_msgPort, FALSE);
	}
}

void OmegaInput::WaitForSignals(){
	UINT signalMask;
	
	signalMask=Wait(INPUTSIGNAL | SIGBREAKF_CTRL_C | TIMERSIGNAL);
	if (signalMask & INPUTSIGNAL)
		DealWithSignals();
	else 
	if (signalMask & SIGBREAKF_CTRL_C){
		OnStop();
		oa_SetStatus(APP_STATUS_QUIT,TRUE);
	}
	else
		DealWithTimer();
}

void OmegaInput::DealWithSignals(){
	UINT trackNo;
	OmegaMessage *i_msg;
	OmegaTrack *pTrack;
	BOOL replied;
	
	while(i_msg=(OmegaMessage *)GetMsg(basePort)){
		replied=FALSE;
		switch(i_msg->om_Class){
		case OM_QUIT:
			if(IsRunning())
				OnStop();
			oa_SetStatus(APP_STATUS_QUIT,TRUE);
			OnQuit();
			break;
		case OM_START:
			ReplyMsg((Message *)i_msg);
			replied=TRUE;
			if (!IsRunning()){
				oa_SetStatus(APP_STATUS_RUNNING,TRUE);
				if (op_Configured)
					OnStart();
				else
					Error("Can't Start Input","Not Configured Yet");
			}
			break;
		case OM_STOP:
			if (IsRunning()){
				OnStop();
				oa_SetStatus(APP_STATUS_RUNNING,FALSE);
			}
			break;
		case OM_INIT:
			trackNo=i_msg->om_Track;
			pTrack=op_Tracks.GetItem(trackNo);
			pTrack->SetOutput((MsgPort *)(i_msg->om_Misc),i_msg->om_EffectNo);
//			pTrack->SetMsgPort((MsgPort *)(i_msg->om_Misc));
//			pTrack->SetEffectNo(i_msg->om_EffectNo);
			op_ID=i_msg->om_ID;
			OnInit();
			ReplyMsg((Message *)i_msg);
			replied=TRUE;
			break;
		case OM_TEMPO:		// If we receive a tempo msg then we send back another one
			i_msg->om_Tempo=oa_Tempo;
			ReplyMsg((Message *)i_msg);		// Put the current tempo in the reply
			i_useTempo=TRUE;
			replied=TRUE;
			break;
		case OM_TEMPO_STOP:		// We're not being used for tempo anymore
			i_useTempo=FALSE;
			break;
		case OM_CANCEL:
			trackNo=i_msg->om_Track;
			// Setting the msgport to null means that the input track isn't being used
			pTrack=op_Tracks.GetItem(trackNo);
			pTrack->Cancel(i_msg->om_Port,i_msg->om_Value);
			break;
		case OM_CONFIG:
			op_ID=i_msg->om_ID;
			ReplyMsg((Message *)i_msg);
			replied=TRUE;
			OnConfig();
			break;
		default:
			replied=TRUE;
			OmegaPlugin::DealWithMessage(i_msg);
			break;
		}
		if (!replied)
			ReplyMsg((Message *)i_msg);
	}
}

void OmegaInput::SetTempo(UINT tempo){
	if (tempo!=oa_Tempo){
		oa_Tempo=tempo;
		if (i_useTempo){// && IsRunning()){
			dataMessage->om_Class=OM_TEMPO;
			SendToPort(coordPort);
		}
	}
}

void OmegaInput::OnConfig(){
	BOOL loop=TRUE;
	ULONG id, gui_Sig=0;
	OmegaTrack *pTrack=NULL;
	ULONG val, n;
	
	gui_OpenWin(&op_Tracks);
	
	while(loop){
		id=DoMethod((Object *)gui_app, MUIM_Application_NewInput, &gui_Sig);
		switch (id){
		case ID_USE:
		case ID_CANCEL:
			loop=FALSE;
			break;
		case ID_TYPES_LIST:
			gui_ChangeType();
			break;
		case ID_ADD_TK:
			pTrack=gui_AddTrack();
			if (pTrack)
				op_Tracks.AddItem(pTrack);
			else
				Error("Unable to add track","Too many tracks?");
			break;
		// Need to do multiple select delete!
		case ID_DEL_TK:
			val=gui_GetActiveTrack();
			if (val!=-1)
				DeleteTracks();
			else
				Error("No Track Selected","Select an Item From Track List");
			break;
		case ID_SAVE:
			op_SaveTracks();
			break;
		case ID_LOAD:
			op_LoadTracks();
			// The plugin class hasn't scaled the tracks so we need to now :/
			val=op_Tracks.GetSize();
			for (n=0;n<val;n++){
				pTrack=op_Tracks.GetItem(n);
				pTrack->SetScaleValue(&types,typeMap);
			}
			gui_OpenWin(&op_Tracks);
			break;
		case ID_SET_COND:
			val=gui_GetActiveTrack();
			if (val!=-1){
				ChangeConditions(FALSE);
			}
			else
				Error("No Track Selected","Select an Item From Track List");
			break;
		case ID_DEL_COND:
			val=gui_GetActiveTrack();
			if (val!=-1){
				ChangeConditions(TRUE);
			}
			else
				Error("No Track Selected","Select an Item From Track List");	
			break;
		case ID_INTERNAL:
			i_Configure();	// Calls a routine described by the actual plugin
								// Not by the base class
			break;
		case ID_CHANGE_USE:
			val=gui_GetActiveTrack();
			if (val!=-1)
				ChangeUse(TRUE);
			break;
		case ID_CHANGE_SCALE:
			val=gui_GetActiveTrack();
			if (val!=-1)
				ChangeScale(TRUE);
			break;
		case ID_CHANGE_ALL:
			val=gui_GetActiveTrack();
			if (val!=-1){
				ChangeScale(FALSE);
				ChangeUse(FALSE);
				ChangeNames(FALSE);
			}
			break;
		case ID_COPY:
			CopyTracks();
			break;
		}
		if (loop && gui_Sig)
			Wait(gui_Sig);			
	}
	gui_CloseWin();
	
	// We don't want to change any of the configuration if we cancelled
	// the window
	if (id==ID_USE){
		BuildTrackNames();
		// Tell the coordinator that we've finished our configuration 
		dataMessage->om_Type=OMEGAINPUT;
		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);
	}
}

void OmegaInput::ChangeScale(BOOL refresh){
	ULONG val;
	LONG id=MUIV_List_NextSelected_Start;
	BOOL loop=TRUE;

	GetAttr(MUIA_Cycle_Active,c_scale,&val);
	
	while(loop){
		DoMethod((Object *)(lv_tracks),MUIM_NList_NextSelected,&id);
		if (id==MUIV_List_NextSelected_End)
			loop=FALSE;
		else{	
			op_Tracks[id]->SetScaleType(val);
			op_Tracks[id]->SetScaleValue(&types,typeMap);
			gui_RefreshTracks(id);
		}
	}
}

void OmegaInput::ChangeUse(BOOL refresh){
	ULONG val;
	LONG id=MUIV_List_NextSelected_Start;
	BOOL loop=TRUE;

	GetAttr(MUIA_Cycle_Active,c_value,&val);
	
	while(loop){
		DoMethod((Object *)(lv_tracks),MUIM_NList_NextSelected,&id);
		if (id==MUIV_List_NextSelected_End)
			loop=FALSE;
		else{	
			op_Tracks[id]->SetUse(val);
			op_Tracks[id]->SetScaleValue(&types,typeMap);
			gui_RefreshTracks(id);
		}
	}
}

void OmegaInput::ChangeConditions(BOOL reset){
	LONG id=MUIV_List_NextSelected_Start;
	BOOL loop=TRUE;

	while(loop){
		DoMethod((Object *)(lv_tracks),MUIM_NList_NextSelected,&id);
		if (id==MUIV_List_NextSelected_End)
			loop=FALSE;
		else{
			if (reset)
				gui_ResetCondition(op_Tracks[id]);
			else{
				gui_SetCondition(op_Tracks[id]);
				op_Tracks[id]->SetScaleValue(&types,typeMap);			
				gui_RefreshTracks(id);
			}
		}
	}
}

void OmegaInput::ChangeNames(BOOL refresh){
	LONG id=MUIV_List_NextSelected_Start;
	BOOL loop=TRUE;
	ULONG val;
	
	GetAttr(MUIA_String_Contents,str_name,&val);
	
	while(loop){
		DoMethod((Object *)(lv_tracks),MUIM_NList_NextSelected,&id);
		if (id==MUIV_List_NextSelected_End)
			loop=FALSE;
		else{
			if (strlen((char *)val)!=0){
				op_Tracks[id]->SetName((char *)val);
				gui_RefreshTracks(id)
			}
		}
	}
}

void OmegaInput::DeleteTracks(){
	LONG id=MUIV_List_NextSelected_Start;
	BOOL loop=TRUE;
	LONG item=-1;
		
	// Need to find the first item selected
	// and keep removing that one as the index is shifted down
	while(loop){
		DoMethod((Object *)(lv_tracks),MUIM_NList_NextSelected,&id);
		if (item==-1)
			item=id;
		if (id==MUIV_List_NextSelected_End)
			loop=FALSE;
		else{
			op_Tracks.DeleteItem(item);
		}
	}
	gui_OpenWin(&op_Tracks);
}

void OmegaInput::CopyTracks(){
	LONG id=MUIV_List_NextSelected_Start;
	BOOL loop=TRUE;
	OmegaTrack *newTrack;
	OmegaTrack *oldTrack;

	while(loop){
		DoMethod((Object *)(lv_tracks),MUIM_NList_NextSelected,&id);
		if (id==MUIV_List_NextSelected_End)
			loop=FALSE;
		else{
			oldTrack=op_Tracks[id];
			newTrack=new OmegaTrack(oldTrack->GetName(),oldTrack->GetUse(),oldTrack->GetScaleType());
			if (newTrack){
				for (UINT n=0;n<MAXCONDITIONS;n++)
					newTrack->SetCondition(n,oldTrack->GetCondition(n));
				newTrack->SetScaleValue(&types,typeMap);
				op_Tracks.AddItem(newTrack);
				DoMethod((Object *)lv_tracks,MUIM_NList_InsertSingle,(APTR)newTrack,MUIV_List_Insert_Bottom);
			}
			else
				Error("Couldn't create New Track",NULL);
		}
	}
	gui_OpenWin(&op_Tracks);
}