/*
 This project is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 Multiprotocol is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with Multiprotocol.  If not, see <http://www.gnu.org/licenses/>.
 */

#if defined(FRSKYX_CC2500_INO)

#include "iface_cc2500.h"

static void __attribute__((unused)) FrSkyX_build_bind_packet()
{
	//Header
	packet[0] = packet_length;				// Number of bytes in the packet (after this one)
	packet[1] = 0x03;						// Bind packet
	packet[2] = 0x01;						// Bind packet

	//ID
	packet[3] = rx_tx_addr[3];				// ID
	packet[4] = rx_tx_addr[2];				// ID

	if(protocol==PROTO_FRSKYX)
	{
		int idx = ((state -FRSKY_BIND) % 10) * 5;
		packet[5] = idx;
		packet[6] = hopping_frequency[idx++];
		packet[7] = hopping_frequency[idx++];
		packet[8] = hopping_frequency[idx++];
		packet[9] = hopping_frequency[idx++];
		packet[10] = hopping_frequency[idx++];
		packet[11] = rx_tx_addr[1];			// ID
		packet[12] = RX_num;
		//
		memset(&packet[13], 0, packet_length - 14);
		if(binding_idx&0x01)
			memcpy(&packet[13],(void *)"\x55\xAA\x5A\xA5",4);	// Telem off
		if(binding_idx&0x02)
			memcpy(&packet[17],(void *)"\x55\xAA\x5A\xA5",4);	// CH9-16
	}
	else
	{
		//packet 1D 03 01 0E 1C 02 00 00 32 0B 00 00 A8 26 28 01 A1 00 00 00 3E F6 87 C7 00 00 00 00 C9 C9
		//Unknown bytes
		if(state & 0x01)
			memcpy(&packet[7],"\x00\xCC\x00\x00\x00\x70\x14\x15\x00\xD3\x08\x00\x00\xCE\xE2\x85\xC7\x00\x00\x00\x00",21);
		else
			memcpy(&packet[7],"\x27\xFB\x00\x00\x00\xBC\xEF\x19\x00\x26\x07\x00\x00\xB7\xED\x85\xC7\xA7\xA7\xA7\xA7",21);
		//ID
		packet[5] = rx_tx_addr[1];			// ID
		packet[6] = RX_num;
		//Bind flags
		if(binding_idx&0x01)
			packet[7] |= 0x40;				// Telem off
		if(binding_idx&0x02)
			packet[7] |= 0x80;				// CH9-16
		//Replace the ID
		packet[20] ^= rx_tx_addr[3]; 		// Update the ID
		packet[21] ^= rx_tx_addr[2]; 		// Update the ID
		packet[22] ^= rx_tx_addr[1] & 0x3F;	// Update the ID
		//Xor
		for(uint8_t i=3; i<packet_length-1; i++)
			packet[i] ^= 0xA7;
	}
	//CRC
	uint16_t lcrc = FrSkyX_crc(&packet[3], packet_length-4);
	packet[packet_length-1] = lcrc >> 8;
	packet[packet_length]   = lcrc;

	/*//Debug
	debug("Bind:");
	for(uint8_t i=0;i<=packet_length;i++)
		debug(" %02X",packet[i]);
	debugln("");*/
}

static void __attribute__((unused)) FrSkyX_build_packet()
{
	//0x1D 0xB3 0xFD 0x02 0x56 0x07 0x15 0x00 0x00 0x00 0x04 0x40 0x00 0x04 0x40 0x00 0x04 0x40 0x00 0x04 0x40 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x96 0x12
    // data frames sent every 9ms; failsafe every 9 seconds
	//
	//Header
	packet[0] = packet_length;			// Number of bytes in the packet (after this one)
	packet[1] = rx_tx_addr[3];			// ID
	packet[2] = rx_tx_addr[2];			// ID
	packet[3] = rx_tx_addr[1];			// Unknown but constant ID?
	//  
	packet[4] = (FrSkyX_chanskip<<6)|hopping_frequency_no; 
	packet[5] = FrSkyX_chanskip>>2;
	packet[6] = RX_num;

	//Channels
	FrSkyX_channels(7);					// Set packet[7]=failsafe, packet[8]=0?? and packet[9..20]=channels data
	
	//Sequence and send SPort
	FrSkyX_seq_sport(21,packet_length-((protocol==PROTO_FRSKYX && (FrSkyFormat & 2 )) ? 4 : 2)); //21=RX|TXseq, 22=bytes count, 23..packet_length-2=data

	//CRC
	uint16_t lcrc = FrSkyX_crc(&packet[3], packet_length-4);
	packet[packet_length-1] = lcrc >> 8;
	packet[packet_length]   = lcrc;

	/*//Debug
	debug("Norm:");
	for(uint8_t i=0;i<=packet_length;i++)
		debug(" %02X",packet[i]);
	debugln("");*/
}

uint16_t FRSKYX_callback()
{
	#if defined MULTI_EU
		if(sub_protocol == CH_16 || sub_protocol == CH_8)
			return 9000;
	#endif
	switch(state)
	{	
		default: 
			FrSkyX_set_start(47);		
			CC2500_SetPower();
			CC2500_Strobe(CC2500_SFRX);
			//		
			FrSkyX_build_bind_packet();
			CC2500_Strobe(CC2500_SIDLE);
			CC2500_WriteData(packet, packet[0]+1);
			if(IS_BIND_DONE)
				state = FRSKY_BIND_DONE;
			else
				state++;
			break;
		case FRSKY_BIND_DONE:
			FrSkyX_initialize_data(0);
			hopping_frequency_no=0;
			BIND_DONE;
			state++;														//FRSKY_DATA1
			break;

		case FRSKY_DATA1:
			CC2500_Strobe(CC2500_SIDLE);
			CC2500_SetFreqOffset();
			FrSkyX_set_start(hopping_frequency_no);
			FrSkyX_build_packet();
			if(FrSkyFormat & 2)
			{// LBT
				CC2500_Strobe(CC2500_SRX);									//Acquire RSSI
				state++;
				return 400;		// LBT
			}
		case FRSKY_DATA2:
			if(FrSkyFormat & 2)
			{
				uint16_t rssi=0;
				for(uint8_t i=0;i<4;i++)
					rssi += CC2500_ReadReg(CC2500_34_RSSI | CC2500_READ_BURST);	// 0.5 db/count, RSSI value read from the RSSI status register is a 2's complement number
				rssi>>=2;
				#if 0
					uint8_t rssi_level=convert_channel_8b(CH16)>>1;			//CH16 0..127
					if ( rssi > rssi_level && rssi < 128)					//test rssi level dynamically
				#else
					if ( rssi > 14 && rssi < 128)							//if RSSI above -65dBm (12=-70) => ETSI requirement
				#endif
				{
					LBT_POWER_on;											//Reduce to low power before transmitting
					debugln("Busy %d %d",hopping_frequency_no,rssi);
				}
			}
			CC2500_Strobe(CC2500_SIDLE);
			CC2500_Strobe(CC2500_SFTX);										//Flush the TXFIFO
			CC2500_SetTxRxMode(TX_EN);
			CC2500_SetPower();
			hopping_frequency_no = (hopping_frequency_no+FrSkyX_chanskip)%47;
			CC2500_WriteData(packet, packet[0]+1);
			state=FRSKY_DATA3;
			if(FrSkyFormat & 2)
				return 4000;	// LBT
			else
				return 5200;	// FCC
		case FRSKY_DATA3:
			CC2500_Strobe(CC2500_SIDLE);
			CC2500_Strobe(CC2500_SFRX);										//Flush the RXFIFO
			CC2500_SetTxRxMode(RX_EN);
			CC2500_Strobe(CC2500_SRX);
			state++;
			if(FrSkyFormat & 2)
				return 4200;	// LBT
			else
				return 3400;	// FCC
		case FRSKY_DATA4:
			#ifdef MULTI_SYNC
				telemetry_set_input_sync(9000);
			#endif
			#if defined TELEMETRY
				len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;	
				if (len && len <= 17)										//Telemetry frame is 17 bytes
				{
					//debug("Telem:");
					CC2500_ReadData(packet_in, len);						//Read what has been received so far
					if(len<17)
					{//not all bytes were received
						uint8_t last_len=CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
						if(last_len==17)									//All bytes received
						{
							CC2500_ReadData(packet_in+len, last_len-len);	//Finish to read
							len=17;
						}
					}
					if(len==17 && (protocol==PROTO_FRSKYX || (protocol==PROTO_FRSKYX2 && (packet_in[len-1] & 0x80))) )
					{//Telemetry received with valid crc for FRSKYX2
						//Debug
						//for(uint8_t i=0;i<len;i++)
						//	debug(" %02X",packet_in[i]);
						if(frsky_process_telemetry(packet_in,len))			//Check and process telemetry packet
						{//good packet received
							pps_counter++;
							if(TX_LQI==0)
								TX_LQI++;									//Recover telemetry right away
						}
					}
					//debugln("");
				}
				if (millis() - pps_timer >= 900)
				{//1 packet every 9ms
					pps_timer = millis();
					debugln("%d pps", pps_counter);
					TX_LQI = pps_counter;									//Max=100%
					pps_counter = 0;
				}
				if(TX_LQI==0)
					FrSkyX_telem_init();									//Reset telemetry
				else
					telemetry_link=1;										//Send telemetry out anyway
			#endif
			state = FRSKY_DATA1;
			return 400;	// FCC & LBT
	}
	return 9000;
}

void FRSKYX_init()
{
	set_rx_tx_addr(MProtocol_id_master);
	FrSkyFormat = sub_protocol;
	
	if (sub_protocol==XCLONE_16||sub_protocol==XCLONE_8)
		Frsky_init_clone();
	else
	{
		if(protocol==PROTO_FRSKYX)
			Frsky_init_hop();
		else
		{
			#ifdef FRSKYX2_FORCE_ID
				rx_tx_addr[3]=0x0E;
				rx_tx_addr[2]=0x1C;
				FrSkyX_chanskip=18;
			#endif
			FrSkyX2_init_hop();
		}
		rx_tx_addr[1]=0x02;		// ID related, hw version?
	}
	
	if(protocol==PROTO_FRSKYX && (FrSkyFormat & 2 ))
		packet_length = 0x20;	// FrSkyX V1 LBT
	else
		packet_length = 0x1D;

	packet_count=0;
	while(!FrSkyX_chanskip)
		FrSkyX_chanskip=random(0xfefefefe)%47;

	FrSkyX_RF_init();

	if(IS_BIND_IN_PROGRESS)
	{	   
		memset(packet, 0, packet_length);
		state = FRSKY_BIND;
		FrSkyX_initialize_data(1);
	}
	else
		state = FRSKY_BIND_DONE;
	FrSkyX_telem_init();
}
#endif
