/*---------------------------------------------------------------------------------
 libBAG  Copyright (C) 2010 - 2010		
  BassAceGold - <BassAceGold@gmail.com>

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any
  damages arising from the use of this software.

  Permission is granted to anyone to use this software for any
  purpose, including commercial applications, and to alter it and
  redistribute it freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you
     must not claim that you wrote the original software. If you use
     this software in a product, an acknowledgment in the product
     documentation would be appreciated but is not required.
	 
  2. Altered source versions must be plainly marked as such, and
     must not be misrepresented as being the original software.
	 
  3. Restricting the usage of or portions of code in this library
     from other software developers and or content creators
     is strictly prohibited in applications with software creation capabilities
     and or with the intent to profit from said restrictions.
	 
  4. This notice may not be removed or altered from any source
     distribution.
---------------------------------------------------------------------------------*/

#include "Audio/Audio_Core.h"
#include "core.h"
#include "input/input.h"
#include "BAG_Math.h"


static s16 _BAG_FPS_Lim = 0;
static s8  _BAG_UPDATE_FLAG = 0,
	Num_Asynchro = 0,
	Num_Synchro = 0;

BAG_Core_UpdateFunction BAG_AsynchroFunc[BAG_MAX_FUNCTIONS];
BAG_Core_UpdateFunction BAG_SynchroFunc[BAG_MAX_FUNCTIONS];

/*===========================================================
Asynchronus function updates (not limited to the programs set FPS)
===========================================================*/
void BAG_Core_SetAsynchroFunction (void(*BAG_Core_UpdateFunction)(void)){
	if(Num_Asynchro < BAG_MAX_FUNCTIONS){
		BAG_AsynchroFunc[Num_Asynchro] = (*BAG_Core_UpdateFunction);
		Num_Asynchro++;
	}
}



void BAG_Core_RemoveAsynchroFunction (void(*BAG_Core_UpdateFunction)(void)){
	int i = 0, update = 0;
	//scan for function pointer and set it to null
	for(i = 0; i < Num_Asynchro; i++){
		if(BAG_AsynchroFunc[i] == (*BAG_Core_UpdateFunction)){
			BAG_AsynchroFunc[i] = NULL;
			update = i;
			i = 0;
			break;
		}
	}
	
	//if a function is found and removed, then shift everything in the array down
	if(update){
		for( i = update; i < Num_Asynchro - 1; i++)
			BAG_AsynchroFunc[i] = BAG_AsynchroFunc[i + 1];
			
		BAG_AsynchroFunc[i] = NULL;
		
		if(Num_Asynchro > 0) Num_Asynchro--;
	}
}	
		
/*===========================================================
Synchronus function updates limited to the programs set FPS
===========================================================*/
void BAG_Core_SetSynchroFunction (void(*BAG_Core_UpdateFunction)(void)){
	if(Num_Synchro < BAG_MAX_FUNCTIONS){
		BAG_SynchroFunc[Num_Synchro] = (*BAG_Core_UpdateFunction);
		Num_Synchro++;
	}
}



void BAG_Core_RemoveSynchroFunction (void(*BAG_Core_UpdateFunction)(void)){
	int i = 0, update = 0;
	//scan for function pointer and set it to null
	for(i = 0; i < Num_Synchro; i++){
		if(BAG_SynchroFunc[i] == (*BAG_Core_UpdateFunction)){
			BAG_SynchroFunc[i] = NULL;
			update = i;
			i = 0;
			break;
		}
	}
	
	//if a function is found and removed, then shift everything in the array down
	if(update){
		for( i = update; i < Num_Synchro - 1; i++)
			BAG_SynchroFunc[i] = BAG_SynchroFunc[i + 1];
			
		BAG_SynchroFunc[i] = NULL;
		
		if(Num_Synchro > 0) Num_Synchro--;
	}
}	



void _BAG_Core_Update(unsigned int arg){
	_BAG_UPDATE_FLAG = arg;
}

void BAG_Core_SetFPS(s8 fps){
	/*random values obtained through cross multiplication.
		Setting _BAG_FPS_Lim to 60 resulted in 108 fps
		so using those values and the value of fps we can 
		calculate a cailbrated value for the timer.
	*/
	_BAG_FPS_Lim = ((60 *  fps ) / 108);
	//initiate timer for fps
	stopTimer (0);
	resetTimer (0);
	initTimer (0, 1000000/_BAG_FPS_Lim, _BAG_Core_Update, 1);
	runTimer (0);
}


s8 BAG_Core_InitEx(SCREEN_ID console_screen, s8 console, s8 enable_update){
	//initialize the library with the sdk
	if(ds2io_init(1152))//1152 samples needed for mp3 decoding
		return 0;
		
	BAG_ClearAllScreenMemory();
	
	//initialize the console
	if(console){
		if(ConsoleInit(RGB15(31,31,31), RGB15(0,0,0), console_screen, 10))
			return 0;
	}
	
	//initialize fat
	if(fat_init())
		return 0;
	
    #ifdef  _BAG_AUDIO_
        //initialize audio mixer in libBAG
        if(!BAG_Audio_InitMixer())
            return 0;
            
        //set update function
        BAG_Core_SetAsynchroFunction ((void*)&BAG_Audio_UpdateAudio);
    #endif
    
    #ifdef _BAG_INPUT_
        //set default double click time
        BAG_Input_SetDblClickTime(BAG_DEFAULT_DBLCLICK);
        //set update function
        BAG_Core_SetSynchroFunction ((void*)&BAG_Input_UpdateIN);
    #endif
    
    if(enable_update)//enable the use of timer 0 to handle frame limiting, updates etc
        BAG_Core_SetFPS(BAG_DEFAULT_FPS);
    else//disable the asynchro updates to prevent infinite loop
        _BAG_Core_Update(-1);
	
    //seed random generator
    struct rtc Time;
    ds2_getTime(&Time);
    int CurTime = Time.year + (Time.month*3600*24*30) + (Time.day*3600*24*7) + (Time.weekday*3600*24) + (Time.hours*3600) +  (Time.minutes*60) + Time.seconds;

    BAG_Math_srand(CurTime);
	return 1;
}


s8 BAG_Core_Init(s8 console){
    return BAG_Core_InitEx(UP_SCREEN, console, 1);
}


s8 BAG_Core_UpdateAll(void){
	int i = 0;
	//update the synchronous functions
	do{
		if(BAG_SynchroFunc[i])
			BAG_SynchroFunc[i]();
	}while( ++i < Num_Synchro);
	//limit the number of cycles to execute synchronous functions
	_BAG_UPDATE_FLAG = 0;
	while(_BAG_UPDATE_FLAG == 0){
		i = 0;
		do{
			if(BAG_AsynchroFunc[i])
				BAG_AsynchroFunc[i]();
		}while( ++i < Num_Asynchro);	
	}
	return 1;
}
