#ifndef SPIDRIVER_C
#define SPIDRIVER_C

#include "SPIDriver.h"


int SPIUSEDMASK = 0;	// Use mutex or critical section

/*
xQueueHandle xQueueSPI1TX
xQueueHandle xQueueSPI1RX
xQueueHandle xQueueSPI2TX
xQueueHandle xQueueSPI2RX
*/


SPIX *SPIXConstruct( int moduleID, PIN *pins ){
	SPIX *this;
	int i;
	
	if( NULL == ( this = (SPIX*) /*pvPort*/malloc( sizeof( SPIX ) ) ) )
		return NULL;

	if( moduleID == -1 ){
		for( i = 0 ; i < SPIX_MAX ; i++ ){
			if( (SPIUSEDMASK & (0x01<<i)) == 0 ){
				SPIUSEDMASK |= 0x01<<i;
				moduleID = i;
				break;
			}
		}
		if( i == SPIX_MAX )
			moduleID = i;
				
	}		
	
	this->SCK = pins[0];
	this->MOSI = pins[1];
	this->MISO = pins[2];
	this->SS = pins[3];
		
	switch( moduleID ){
		case 0 :
			this->moduleID = moduleID;
			
			PINSetOutFunc( &this->SCK, OUT_FN_PPS_SCK1OUT );
			PINSetOutFunc( &this->MOSI, OUT_FN_PPS_SDO1 );
			//PINSetInFunc( &this->MISO, IN_FN_PPS_SDI1 );
			PINSetOutFunc( &this->SS, OUT_FN_PPS_SS1OUT ); // NOT supported by proteus
			
			this->pIPC = &IPC2;
			this->IPCoffset = 0x0008;
			this->pIE = &IEC0;
			this->IEMASK = 0x0400;
			this->pIF = &IFS0;
			this->IFMASK = 0x0400;
			
			this->pstat = &SPI1STAT;
			this->pcon1 = &SPI1CON1;
			this->pcon2 = &SPI1CON2;
			this->pbuf = &SPI1BUF;
			break;

		case 1 :
			this->moduleID = moduleID;
			
			PINSetOutFunc( &this->SCK, OUT_FN_PPS_SCK2OUT );
			PINSetOutFunc( &this->MOSI, OUT_FN_PPS_SDO2 );
			//PINSetInFunc( &this->MISO, IN_FN_PPS_SDI2 );
			PINSetOutFunc( &this->SS, OUT_FN_PPS_SS2OUT );
			
			this->pIPC = &IPC8;
			this->IPCoffset = 0x04;
			this->pIE = &IEC2;
			this->IEMASK = 0x0002;
			this->pIF = &IFS2;
			this->IFMASK = 0x0002;
			
			this->pstat = &SPI2STAT;
			this->pcon1 = &SPI2CON1;
			this->pcon2 = &SPI2CON2;
			this->pbuf = &SPI2BUF;
			break;
	
		default:
			// software SPI emulation
		
			this->moduleID = moduleID;
			
			PINSetOutFunc( &this->SCK, OUT_FN_PPS_NULL );
			PINSetOutFunc( &this->MOSI, OUT_FN_PPS_NULL );
			//PINSetInFunc( &this->MISO, OUT_FN_PPS_NULL );
			PINSetOutFunc( &this->SS, OUT_FN_PPS_NULL ); // NOT supported by proteus
			
			PINOpen( &this->SCK, OUTPUT, LOW );
			PINOpen( &this->MOSI, OUTPUT, LOW );
			PINOpen( &this->MISO, INPUT, LOW );
			PINOpen( &this->SS, OUTPUT, HIGH );
			
			if( NULL == ( this->pIPC = (volatile unsigned int*) malloc( sizeof( volatile unsigned int ) ) ) )
				return NULL;
			this->IPCoffset = 0x0008;
			if( NULL == ( this->pIE = (volatile unsigned int*) malloc( sizeof( volatile unsigned int ) ) ) )
				return NULL;
			this->IEMASK = 0x0400;
			if( NULL == ( this->pIF = (volatile unsigned int*) malloc( sizeof( volatile unsigned int ) ) ) )
				return NULL;
			this->IFMASK = 0x0400;			

			if( NULL == ( this->pstat = (volatile unsigned int*) malloc( sizeof( volatile unsigned int ) ) ) )
				return NULL;
			if( NULL == ( this->pcon1 = (volatile unsigned int*) malloc( sizeof( volatile unsigned int ) ) ) )
				return NULL;
			if( NULL == ( this->pcon2 = (volatile unsigned int*) malloc( sizeof( volatile unsigned int ) ) ) )
				return NULL;
			if( NULL == ( this->pbuf = (volatile unsigned int*) malloc( sizeof( volatile unsigned int ) ) ) )
				return NULL;

			break;
	}

	return this;
}


void SPIDestruct( SPIX *this ){
	return;
}


void SPIXOpen( SPIX *this, unsigned int config1, unsigned int config2, unsigned int config3 ){
	*this->pcon1 = config1;      /* Initalizes the spi module */
	*this->pcon2 = config2;
	*this->pstat = config3;     /* Enable/Disable the spi module */
}

void SPIXClose( SPIX *this ){
	*this->pIE &= ~(this->IEMASK);
	*this->pstat &= ~SPIENMASK;
	*this->pIF &= ~(this->IFMASK);
}

unsigned int SPIXRead(  SPIX *this ){
	*this->pstat &= ~SPIROVMASK; 
	if ( *this->pcon1 & MODE16MASK )
		return (*this->pbuf);
    else
        return (*this->pbuf & 0xff);
}

void SPIXWrite( SPIX *this, unsigned int data_out ){
	if( this->moduleID < SPIX_MAX ){
		if ( *this->pcon1 & MODE16MASK )
			*this->pbuf = data_out;
    	else
        	*this->pbuf = data_out & 0xff;
	}
	else{
		char i;
		int data_in;
		for ( i = 0 ; i < 8 ; i++ , data_out <<= 1, data_in <<= 1 ) {
			PINWrite( &this->SCK, LOW );
			
			if ( data_out & 0x80 )		
				PINWrite( &this->MOSI, HIGH );
			else 							
				PINWrite( &this->MOSI, LOW );
			
			if( PINRead( &this->MISO ) ) 
				data_in |= 0x01;
			else						 
				data_in &= ~0x01;
			
			PINWrite( &this->SCK, HIGH );
		}
		
		PINWrite( &this->SCK, LOW );
		*this->pbuf = data_in;
		*this->pIF |= this->IFMASK;
	}
}

unsigned int SPIXGets( SPIX *this, unsigned int length, unsigned int *rdptr, unsigned int spi_data_wait ){
	int wait = 0;
    char *temp_ptr = (char *)rdptr;
    while (length){
	    while(!SPIXDataRdy( this )){
           if(wait < spi_data_wait)
               wait++ ;             
           else
               return(length);      
        }
        wait = 0;
        
        if( *this->pcon1 & MODE16MASK )
            *rdptr++ = SPIXRead( this );  
        else
            *temp_ptr++ = SPIXRead( this );
        length--;    
    }
    return (length); 
}

void SPIXPuts( SPIX *this, unsigned int length, unsigned int *wrptr ){
	char *temp_ptr = (char *) wrptr;
    while (length)                   // write byte/word until length is 0
    {   
	    if( this->moduleID < SPIX_MAX ){
        	if(*this->pcon1 & MODE16MASK)
            	*this->pbuf = *wrptr++;      // initiate SPI bus cycle by word write
        	else
            	*this->pbuf = *temp_ptr++;   // initiate SPI bus cycle by byte write
        	while(*this->pstat & SPITBFMASK);  // wait until 'SPITBF' bit is cleared
     	}
     	else{
     		SPIXWrite( this, *wrptr++ );
     	}   
        length--;                    // decrement length
    }
}

char SPIXDataRdy( SPIX *this ){
	return *this->pstat & ~SPIRBFMASK;
}
   
void SPIXConfigInt( SPIX *this, unsigned int config ){
	*this->pIF &= ~(this->IFMASK);
	*this->pIPC &= ~(0x7<<this->IPCoffset);
	*this->pIPC |= ( config & 0x0007 )<<this->IPCoffset;
	if(config & 0x0008)		*this->pIE |= (this->IEMASK);
	else					*this->pIE &= ~(this->IEMASK);
}

void EnableIntSPIX( SPIX *this ){
	*this->pIE |= this->IEMASK;
}

void DisableIntSPIX( SPIX *this ){
	*this->pIE &= ~(this->IEMASK);
}

int SPIX_Intr_Flag( SPIX *this ){
	return ( (*this->pIF & this->IFMASK) != 0 ); 
}

void SetPriorityIntSPIX( SPIX *this, int priority ){
	*this->pIPC &= ~(0x0007<<this->IPCoffset);
	*this->pIPC |= (priority&0x0007)<<this->IPCoffset;
}

void SPIX_Clear_Intr_Status_Bit( SPIX *this ){
	*this->pIF &= ~(this->IFMASK);
}

void SPIX_Clear_Recv_OV( SPIX *this ){
	*this->pstat &= ~SPIROVMASK;
}

int SPIX_Tx_Buf_Full( SPIX *this ){
	return ((*this->pstat & SPITBFMASK) != 0);
}

int SPIX_Rx_Buf_Full( SPIX *this ){
	return ((*this->pstat & SPIRBFMASK) != 0);
}

/*
void SPIXWrite(data){
	
	xQueueSend( xQueueSPIX1TX, &data, pd_MAX );
	
	if( xQueueMessagesWait( xQueueSPIX1TX ) == 1 )
		SPIX.IF = 1;
}

void SPIXReceive(){
	
	if( xQueueMessagesWait( xQueueSPIX1TX ) == 0 )
		SPIXWrite( 0xFFFF );
		
	xQueueReceiveFromISR( xQueueSPIX1TX, &data, pd_MAX );
}

void ISR_SPI(){
	SPIX.IF = 0;
	
	data = *this->pbuf;
	xQueueSendFromISR( xQueueSPIX1RX, &data, TaskWoken );
	
	if( xQueueReceiveFromISR( xQueueSPIX1TX, &data, 0 ) ){
		if( *this->pcon1 & MODE16MASK )
			*this->pbuf = data_out;
    	else
        	*this->pbuf = data_out & 0xff;
	}
	
	if( TaskWoken )
		yeld();
}

*/
#endif // SPIX_C
