/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
 *
 * Copyright (c) 2018 Vikrant More
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#include "console/console.h"
#include "hal/hal_gpio.h"
#include "mesh/mesh.h"

#include "app_gpio.h"
#include "storage.h"

#include "ble_mesh.h"
#include "device_composition.h"
#include "no_transition_work_handler.h"
#include "publisher.h"
#include "state_binding.h"
#include "transition.h"

static bool reset;

static void light_default_var_init(void)
{
	gen_def_trans_time_srv_user_data.tt = 0x00;

	gen_power_onoff_srv_user_data.onpowerup = STATE_DEFAULT;

	light_lightness_srv_user_data.light_range_min = LIGHTNESS_MIN;
	light_lightness_srv_user_data.light_range_max = LIGHTNESS_MAX;
	light_lightness_srv_user_data.last = LIGHTNESS_MAX;
	light_lightness_srv_user_data.def = LIGHTNESS_MAX;

	/* Following 2 values are as per specification */
	light_ctl_srv_user_data.temp_range_min = TEMP_MIN;
	light_ctl_srv_user_data.temp_range_max = TEMP_MAX;

	light_ctl_srv_user_data.temp_def = TEMP_MIN;

	light_ctl_srv_user_data.lightness_temp_last =
		(uint32_t) ((LIGHTNESS_MAX << 16) | TEMP_MIN);
}

static void light_default_status_init(void)
{
	uint16_t lightness;

	lightness = (uint16_t) (light_ctl_srv_user_data.lightness_temp_last >> 16);

	if (lightness) {
		gen_onoff_srv_root_user_data.onoff = STATE_ON;
	} else {
		gen_onoff_srv_root_user_data.onoff = STATE_OFF;
	}

	/* Retrieve Default Lightness & Temperature Values */

	if (light_ctl_srv_user_data.lightness_temp_def) {
		light_ctl_srv_user_data.lightness_def = (uint16_t)
			(light_ctl_srv_user_data.lightness_temp_def >> 16);

		light_ctl_srv_user_data.temp_def = (uint16_t)
			(light_ctl_srv_user_data.lightness_temp_def);
	}

	light_lightness_srv_user_data.def =
		light_ctl_srv_user_data.lightness_def;

	light_ctl_srv_user_data.temp = light_ctl_srv_user_data.temp_def;

	/* Retrieve Range of Lightness & Temperature */

	if (light_lightness_srv_user_data.lightness_range) {
		light_lightness_srv_user_data.light_range_max = (uint16_t)
			(light_lightness_srv_user_data.lightness_range >> 16);

		light_lightness_srv_user_data.light_range_min = (uint16_t)
			(light_lightness_srv_user_data.lightness_range);
	}

	if (light_ctl_srv_user_data.temperature_range) {
		light_ctl_srv_user_data.temp_range_max = (uint16_t)
			(light_ctl_srv_user_data.temperature_range >> 16);

		light_ctl_srv_user_data.temp_range_min = (uint16_t)
			(light_ctl_srv_user_data.temperature_range);
	}

	switch (gen_power_onoff_srv_user_data.onpowerup) {
		case STATE_OFF:
			gen_onoff_srv_root_user_data.onoff = STATE_OFF;
			state_binding(ONOFF, ONOFF_TEMP);
			break;
		case STATE_DEFAULT:
			gen_onoff_srv_root_user_data.onoff = STATE_ON;
			state_binding(ONOFF, ONOFF_TEMP);
			break;
		case STATE_RESTORE:
			light_lightness_srv_user_data.last = (uint16_t)
				(light_ctl_srv_user_data.lightness_temp_last >> 16);

			light_ctl_srv_user_data.temp =
				(uint16_t) (light_ctl_srv_user_data.lightness_temp_last);

			state_binding(ONPOWERUP, ONOFF_TEMP);
			break;
	}

	default_tt = gen_def_trans_time_srv_user_data.tt;
}

void update_light_state(void)
{
	uint8_t power, color;

	power = 100 * ((float) lightness / 65535);
	color = 100 * ((float) (temperature + 32768) / 65535);

	printk("power-> %d, color-> %d\n", power, color);

	if (lightness) {
		/* LED1 On */
		hal_gpio_write(led_device[0], 0);
	} else {
		/* LED1 Off */
		hal_gpio_write(led_device[0], 1);
	}

	if (power < 50) {
		/* LED3 On */
		hal_gpio_write(led_device[2], 0);
	} else {
		/* LED3 Off */
		hal_gpio_write(led_device[2], 1);
	}

	if (color < 50) {
		/* LED4 On */
		hal_gpio_write(led_device[3], 0);
	} else {
		/* LED4 Off */
		hal_gpio_write(led_device[3], 1);
	}

	if (*ptr_counter == 0 || reset == false) {
		reset = true;
		os_callout_reset(&no_transition_work, 0);
	}
}

static void short_time_multireset_bt_mesh_unprovisioning(void)
{
	if (reset_counter >= 4) {
		reset_counter = 0;
		printk("BT Mesh reset\n");
		bt_mesh_reset();
	} else {
		printk("Reset Counter -> %d\n", reset_counter);
		reset_counter++;
	}

	save_on_flash(RESET_COUNTER);
}

static void reset_counter_timer_handler(struct os_event *dummy)
{
	reset_counter = 0;
	save_on_flash(RESET_COUNTER);
	printk("Reset Counter set to Zero\n");
}

struct os_callout reset_counter_timer;

static void init_timers(void)
{

	os_callout_init(&reset_counter_timer, os_eventq_dflt_get(),
			reset_counter_timer_handler, NULL);
	os_callout_reset(&reset_counter_timer,
			 os_time_ms_to_ticks32(K_MSEC(7000)));

	no_transition_work_init();
}

void bt_initialized(void)
{
	light_default_status_init();

	update_light_state();

	randomize_publishers_TID();

	short_time_multireset_bt_mesh_unprovisioning();
}

int
mynewt_main(int argc, char **argv)
{
	/* Initialize OS */
	sysinit();

	light_default_var_init();

	app_gpio_init();

	init_timers();

	transition_timers_init();

	init_pub();

	ps_settings_init();

	printk("Initializing...\n");

	/* Initialize the NimBLE host configuration. */
	ble_hs_cfg.reset_cb = blemesh_on_reset;
	ble_hs_cfg.sync_cb = blemesh_on_sync;
	ble_hs_cfg.store_status_cb = ble_store_util_status_rr;

	while (1) {
		os_eventq_run(os_eventq_dflt_get());
	}

	return 0;
}
