/* Copyright (c) 2014, Nordic Semiconductor ASA
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice, this
 *     list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *
 *   * Neither the name of Nordic Semiconductor ASA nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
 

 
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include "nrf_soc.h"
#include "nrf_sdm.h"
#include "ble.h"
#include "ble_hci.h"
#include "nrf_drv_config.h"  //board specific data, based on examples with additional Defines for LEDs

//Added:
#include "app_timer.h"
#include "ble_advdata.h"
#include "app_error.h"
#include "softdevice_handler.h"
#include "nrf_gpio.h"


#define PERIPHERAL_DEVICE_NAME      	"DSA"
#define TAG_ID							15
#define CENTRAL_LINK_COUNT          0                                  /**< Number of central links used by the application. When changing this number remember to adjust the RAM settings*/
#define PERIPHERAL_LINK_COUNT       0                                  /**< Number of peripheral links used by the application. When changing this number remember to adjust the RAM settings*/

#define ADV_INTERVAL				500 // Advertisement interval in milliseconds
#define SCAN_WINDOW_MS					96.5  //scan window in 0.625 mus
#define SCAN_INTERVAL_MS				100  // scan interval in 0.625 mus
#define NON_CONNECTABLE_ADV_INTERVAL    MSEC_TO_UNITS(ADV_INTERVAL, UNIT_0_625_MS)
#define SCAN_WINDOW					MSEC_TO_UNITS(SCAN_WINDOW_MS, UNIT_0_625_MS)  //scan window in 0.625 mus
#define SCAN_INTERVAL				MSEC_TO_UNITS(SCAN_INTERVAL_MS, UNIT_0_625_MS)  // scan interval in 0.625 mus

#define STATUS_I					1
#define STATUS_S 					0
#define SHOW_STATUS_LED				1
#define MAIN_SAMPLE_RATE			1  // sample rate for main loop in seconds

#define TIMEOUT_KONTAKT 			60 // Timeout for received adv. packet in seconds.
#define LIMIT_INFECT 				600  // Time needed with established contact for infection in seconds
#define LIMIT_RSSI		 			-60 //(FIXME)
#define TX_POWER					-12  //FIXME
#define TAG_INFO_LENGTH 			19 //FIXME
#define TIME_ZERO					0x0103    // Time initialization
#define NO_SOURCE					0x00, 0x00, 0x00, 0x00, \
										0x00, 0x00, 0x00, 0x00, \
										0x00, 0x00, 0x00, 0x00, \
										0x00, 0x00, 0x01
#define POS_ID						7
#define POS_STATUS					POS_ID+1
#define POS_TIME					POS_ID+2
#define POS_NAME_START				POS_ID+21
#define SEARCH_LEN					6

// General application timer settings.
#define APP_TIMER_PRESCALER             0    // Value of the RTC1 PRESCALER register.
#define APP_TIMER_OP_QUEUE_SIZE         5     // Size of timer operation queues.
APP_TIMER_DEF(m_main_timer_id);


static ble_gap_adv_params_t m_adv_params;
static ble_gap_scan_params_t m_scan_param;
static ble_advdata_manuf_data_t manuf_specific_data;
static ble_advdata_t advdata;

static uint8_t search[SEARCH_LEN] = {0x44, 0x53, 0x41, 0x00, 0x48, 0xCC};  //DSA0
static uint8_t kontakt = 0;
static uint8_t status_tag = STATUS_I;
static int8_t limit_rssi = LIMIT_RSSI;
static uint8_t show_status_led = SHOW_STATUS_LED;
static uint16_t limit_timeout_kontakt = TIMEOUT_KONTAKT; // 60 sekunden
static uint16_t limit_time_infect = LIMIT_INFECT; //10 Minuten
static uint16_t time_counter = TIME_ZERO;
static uint8_t infect_source[15] = {NO_SOURCE};


static uint8_t m_beacon_info[TAG_INFO_LENGTH] =
{
    TAG_ID,
	STATUS_S,
	0x00, 0x00,
    NO_SOURCE
};


static void gap_params_init(void)
{
    uint32_t                err_code;
    ble_gap_conn_sec_mode_t sec_mode;
    
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
    
    //err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *)device_name, 4);
    err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *)PERIPHERAL_DEVICE_NAME, strlen(PERIPHERAL_DEVICE_NAME));
    APP_ERROR_CHECK(err_code);
    err_code = sd_ble_gap_tx_power_set(TX_POWER);
}

static void advertising_init(void)
{
    uint32_t err_code; 
    //ble_advdata_t advdata;
    uint8_t flags = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED;

    manuf_specific_data.company_identifier = 0xFFFF;
    m_beacon_info[1] = status_tag;
    manuf_specific_data.data.p_data = (uint8_t *) m_beacon_info;
    manuf_specific_data.data.size   = TAG_INFO_LENGTH;

    memset(&advdata, 0, sizeof(advdata));
    
    advdata.name_type             = BLE_ADVDATA_FULL_NAME;

    advdata.flags				  = flags;
   advdata.p_manuf_specific_data = &manuf_specific_data;


    err_code = ble_advdata_set(&advdata, NULL);
    APP_ERROR_CHECK(err_code);

    // Initialize advertising parameters (used when starting advertising).
    memset(&m_adv_params, 0, sizeof(m_adv_params));

    m_adv_params.type        = BLE_GAP_ADV_TYPE_ADV_NONCONN_IND;
    m_adv_params.interval    = NON_CONNECTABLE_ADV_INTERVAL;

}

static void advertising_start(void)
{
    uint32_t err_code;

    err_code = sd_ble_gap_adv_start(&m_adv_params);
    APP_ERROR_CHECK(err_code);
}

static void on_ble_evt(ble_evt_t * p_ble_evt)
{
    const ble_gap_evt_t   * p_gap_evt = &p_ble_evt->evt.gap_evt;
    static uint8_t sender_id =0;
    
    sender_id = p_gap_evt->params.adv_report.data[POS_ID];

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_ADV_REPORT:
        {
        	if(	p_gap_evt->params.adv_report.data[POS_NAME_START] == search[0] && \
        		p_gap_evt->params.adv_report.data[POS_NAME_START+1] == search[1] && \
				p_gap_evt->params.adv_report.data[POS_NAME_START+2] == search[2] )
        	{
        		//nrf_gpio_pin_toggle(LED_RGB_BLUE);  // debug
       		if( sender_id == search[3] )
        		{
       				// Reset by center user to be implemented
        		}
        		else
        		{
        			if( p_gap_evt->params.adv_report.data[POS_STATUS] == STATUS_S && \
       					status_tag == STATUS_S && \
       					p_gap_evt->params.adv_report.rssi >= limit_rssi )
       				{
       					kontakt = 1;
       					nrf_gpio_pin_toggle(LED_RGB_BLUE); // debug
       					infect_source[sender_id >> 3] |=  (1<< ( (sender_id-1) &7) );
       				}
        		}
        	}

            break;
        }
        default:
            break;
    }
}

static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
    on_ble_evt(p_ble_evt);
}

static void ble_stack_init(void)
{
	nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;
    uint32_t err_code;
    ble_enable_params_t ble_enable_params;

    // Initialize the SoftDevice handler module.
    SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);

    // Enable BLE stack 

    err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
    		PERIPHERAL_LINK_COUNT,
			&ble_enable_params);
    APP_ERROR_CHECK(err_code);

    //Check the ram settings against the used number of links
	CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT,PERIPHERAL_LINK_COUNT);

	// Enable BLE stack.
	err_code = softdevice_enable(&ble_enable_params);
	APP_ERROR_CHECK(err_code);

	// Register with the SoftDevice handler module for BLE events.
	err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
	APP_ERROR_CHECK(err_code);

	// Register with the SoftDevice handler module for System events.
	//err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
	//APP_ERROR_CHECK(err_code);
}

// Create timers
static void timer_main_event_handler(void* p_context)
{
	static uint8_t led_status = LED_RGB_GREEN;
	uint32_t err_code;
	static uint8_t i = 0;

	static uint16_t time_kontakt = 0;
	static uint16_t timer_infect = 0;
	static uint16_t infect_time = 0;
	static uint16_t delta_t = 0;


	// Toggle LED to show active tag
	if( show_status_led == 1)
	{
		switch(status_tag)
		{
		case STATUS_I:
			led_status = LED_RGB_RED;
			break;
		default:
			led_status = LED_RGB_GREEN;
		}
	}
	nrf_gpio_pin_toggle(led_status);

	// Increment time since power on
	time_counter += MAIN_SAMPLE_RATE;

	if( kontakt==1) {
		time_kontakt = limit_timeout_kontakt;
	}else {
		if(time_kontakt == 0 ){}
		else {
				time_kontakt -=MAIN_SAMPLE_RATE;
		}
	}

	if( time_kontakt>0) {
		timer_infect +=MAIN_SAMPLE_RATE;
	}else {
		timer_infect = 0;
		for (i = 0;i<16;i++){
				infect_source[i] = 0x00;
			}
	}

	if( timer_infect	>= limit_time_infect ){
		status_tag = STATUS_I;
		for (i = 0;i<16;i++){
			m_beacon_info[4+i] = infect_source[i];
		}
		m_beacon_info[1] = status_tag;
		infect_time = time_counter;
	}

	//update adv_params
	delta_t = time_counter-infect_time;
	m_beacon_info[3] = delta_t & 0xff;
	m_beacon_info[2] = (delta_t>>8) & 0xff;


    manuf_specific_data.data.p_data = (uint8_t *) m_beacon_info;
    advdata.p_manuf_specific_data = &manuf_specific_data;
    err_code = ble_advdata_set(&advdata, NULL);
    APP_ERROR_CHECK(err_code);

}

static void create_timers()
{
    uint32_t err_code;

    // Create timers
    err_code = app_timer_create(&m_main_timer_id,
                                APP_TIMER_MODE_REPEATED,
                                timer_main_event_handler);
    APP_ERROR_CHECK(err_code);
}

static void power_manage(void)
{
    uint32_t err_code = sd_app_evt_wait();
    APP_ERROR_CHECK(err_code);
}

static void scan_init(void)
{
    m_scan_param.active = 0;
    m_scan_param.selective = 0;
    m_scan_param.p_whitelist = NULL;
    m_scan_param.timeout = 0;
    m_scan_param.interval = SCAN_INTERVAL;
    m_scan_param.window = SCAN_WINDOW;
}

static void scan_start(void)
{  
    uint32_t err_code;
    err_code = sd_ble_gap_scan_start(&m_scan_param);
    APP_ERROR_CHECK(err_code);
}

int main(void)
{
    uint32_t time_ms = MAIN_SAMPLE_RATE*1000; //Time(in milliseconds) for call of "main" loop.

    uint32_t err_code = NRF_SUCCESS;


	ble_stack_init();
    gap_params_init();
    advertising_init();
    scan_init();
    
    // Create application timers.
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false);
    create_timers();


    advertising_start();
    scan_start();
    err_code = app_timer_start(m_main_timer_id, APP_TIMER_TICKS(time_ms, APP_TIMER_PRESCALER), NULL);
            APP_ERROR_CHECK(err_code);

    nrf_gpio_cfg_output(LED_RGB_RED);
    nrf_gpio_cfg_output(LED_RGB_GREEN);
    nrf_gpio_cfg_output(LED_RGB_BLUE);
    nrf_gpio_pin_toggle(LED_RGB_RED);
    nrf_gpio_pin_toggle(LED_RGB_GREEN);
    nrf_gpio_pin_toggle(LED_RGB_BLUE);

    for (;; )
    {
        power_manage();
    }
}
