/*!
 * chacon.c
 *
 * Copyright (c) 2015, NADAL Jean-Baptiste. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA
 * 
 * @Author:  NADAL Jean-Baptiste
 * @Date: 20/03/2015
 *
 */

/*-------------------------------- INCLUDES ---------------------------------*/

#include <stdio.h>
#include <sched.h>

#include <wiringPi.h>

#include "chacon.h"

/*-------------------------------- GLOBALES ---------------------------------*/

static int gGpioPin = 0;

bool gBit2[26]={};              // 26 bit Identifiant emetteur
bool gBit2Interruptor[4]={}; 


void sendBit (bool aBit);
unsigned long power2 (int aPower);
void itob (unsigned long anInteger, int aLength);
void itobInterruptor (unsigned long anInteger, int aLength);
void sendPair (bool aBit);
void transmit (bool aState);


/*!  ----------------------------------------------------------------------------
 *  @fn SetupChacon
 *
 *  @brief this method setup the Chacon environment.
 */
int SetupChacon (int aGpioPin)
{
	gGpioPin = aGpioPin;

    if (wiringPiSetup() == -1) {

        fprintf (stderr, "Impossible to setup the WiringPI...");
        return -1;

    }

	pinMode (gGpioPin, OUTPUT);

	return 0;
}


/*!  ----------------------------------------------------------------------------
 *  @fn Chacon_SetInterruptorState
 *
 *  @brief This function Change the State of the Interruptor.
 */
int Chacon_SetInterruptorState (uint32_t aSender, uint32_t anInterruptor, bool aState)
{
	int i;

	fprintf (stdout, "Chacon_SetInterruptorState - aSender:%d, anInterruptor:%d, aState:%d\n", 
					aSender, anInterruptor, aState);

	/* convert sender and interruptor ID */
	itob (aSender, 26);            // convertion du code de l'emetteur (ici 8217034) en code binaire
	itobInterruptor (anInterruptor, 4);

	/* Send the State Five Time. */
	for (i = 0; i < 10; i++) {

		transmit (aState);
		delay (10);                 // attendre 10 ms (sinon le socket nous ignore)
	}

	return 0;
}


/*-------------------------------- LOCALES ---------------------------------*/


/*!  ----------------------------------------------------------------------------
 *  @fn scheduler_realtime
 *
 *  @brief Switch the process to the RealTime Scheduler
 */
void scheduler_realtime (void)
{
	struct sched_param theSchedParam;
	
	theSchedParam.__sched_priority = sched_get_priority_max (SCHED_RR);

	if( sched_setscheduler (0, SCHED_RR, &theSchedParam) == -1 ) {

		perror ("Failed to switch to realtime scheduler.");
	}
}


/*!  ----------------------------------------------------------------------------
 *  @fn scheduler_standard
 *
 *  @brief Switch the process to the regular Scheduler
 */
void scheduler_standard (void)
{
	struct sched_param theSchedParam;
	
	theSchedParam.__sched_priority = 0;
	
	if( sched_setscheduler (0, SCHED_OTHER, &theSchedParam) == -1 ) {
		
		perror ("Failed to switch to normal scheduler.");
	}
}


/*!  ----------------------------------------------------------------------------
 *  @fn sendBit
 *
 *  @brief Envois d'une pulsation (passage de l'etat haut a l'etat bas)
 *         1 = 310µs haut puis 1340µs bas
 *         0 = 310µs haut puis 310µs bas
 */
void sendBit (bool aBit)
{
	if (aBit) {

		digitalWrite (gGpioPin, HIGH);
		delayMicroseconds (310);   //275 orinally, but tweaked.
		digitalWrite (gGpioPin, LOW);
		delayMicroseconds (1340);  //1225 orinally, but tweaked.
 	}
	else {

		digitalWrite (gGpioPin, HIGH);
		delayMicroseconds (310);   //275 orinally, but tweaked.
		digitalWrite (gGpioPin, LOW);
		delayMicroseconds (310);   //275 orinally, but tweaked.
 	}
}


/*!  ----------------------------------------------------------------------------
 *  @fn power2
 *
 *  @brief Calcul le nombre 2^chiffre indiqué, fonction utilisé par itob pour
 *         la conversion decimal/binaire
 */
unsigned long power2 (int aPower)
{
	int i;
	unsigned long theInteger = 1;

	for (i = 0; i < aPower; i++) {

  		theInteger *= 2;
	}

	return theInteger;
} 


/*!  ----------------------------------------------------------------------------
 *  @fn itob
 *
 *  @brief Convertis un nombre en binaire, nécessite le nombre, et le nombre de 
 *         bits souhaité en sortie (ici 26)
 *         Stocke le résultat dans le tableau global "bit2"
 */
void itob (unsigned long anInteger, int aLength)
{
	int i;

	for (i = 0; i < aLength; i++) {

		if ((anInteger / power2 (aLength - 1 - i)) == 1) {

			anInteger -= power2 (aLength - 1 - i);
			gBit2[i] = 1;
		}
		else {

			gBit2[i]=0;
		} 
	}
}


/*!  ----------------------------------------------------------------------------
 *  @fn itobInterruptor
 *
 *  @brief TODO
 */
void itobInterruptor (unsigned long anInteger, int aLength)
{
	int i;

	for (i = 0; i < aLength; i++) {

		if ((anInteger / power2 (aLength - 1 - i)) == 1) {

			anInteger -= power2 (aLength - 1 - i);
			gBit2Interruptor[i] = 1;
		}
		else {

			gBit2Interruptor[i] = 0;	
		} 
	}
}


/*!  ----------------------------------------------------------------------------
 *  @fn sendPair
 *
 *  @brief Envoie d'une paire de pulsation radio qui definissent 1 bit réel :
 *         0 =01 et 1 =10
 *         c'est le codage de manchester qui necessite ce petit bouzin, ceci permet
 *         entre autres de dissocier les données des parasites
 */
void sendPair (bool aBit)
{
	if (aBit) {

		sendBit (true);
		sendBit (false);
	}
	else {

		sendBit (false);
   		sendBit (true);
	}
}


/*!  ----------------------------------------------------------------------------
 *  @fn transmit
 *
 *  @brief Fonction d'envois du signal
 *         recoit en parametre un booleen définissant l'arret ou la marche du matos
 *         (true = on, false = off)
 */
void transmit (bool aState)
{
	int i;

 	// Sequence de verrou anoncant le départ du signal au recepeteur
 	digitalWrite (gGpioPin, HIGH);
 	delayMicroseconds (275);     // un bit de bruit avant de commencer pour remettre les delais du recepteur a 0
 	digitalWrite (gGpioPin, LOW);
 	delayMicroseconds (9900);     // premier verrou de 9900µs
 	digitalWrite (gGpioPin, HIGH);   // high again
 	delayMicroseconds (275);      // attente de 275µs entre les deux verrous
 	digitalWrite (gGpioPin, LOW);    // second verrou de 2675µs
 	delayMicroseconds (2675);
 	digitalWrite (gGpioPin, HIGH);  // On reviens en état haut pour bien couper les verrous des données

 	// Envoie du code emetteur (272946 = 1000010101000110010  en binaire)
 	for (i = 0; i < 26; i++) {

   		sendPair (gBit2[i]);
 	}

 	// Envoie du bit définissant si c'est une commande de groupe ou non (26em bit)
 	sendPair (false);

	// Envoie du bit définissant si c'est allumé ou eteint 27em bit)
 	sendPair (aState);

	// Envoie des 4 derniers bits, qui représentent le code interrupteur, ici 0 (encode sur 4 bit donc 0000)
 	// nb: sur  les télécommandes officielle chacon, les interrupteurs sont logiquement nommés de 0 à x
	// interrupteur 1 = 0 (donc 0000) , interrupteur 2 = 1 (1000) , interrupteur 3 = 2 (0100) etc...
  	for (i = 0; i < 4; i++) {

		if (gBit2Interruptor[i] == 0) {

			sendPair (false);
 		} else {

			sendPair (true);
 		}
	}
 	
	digitalWrite (gGpioPin, HIGH);   // coupure données, verrou
 	delayMicroseconds(275);      // attendre 275µs
 	digitalWrite (gGpioPin, LOW);    // verrou 2 de 2675µs pour signaler la fermeture du signal
}
