/**
 * @file example_scene.c
 * @author www.tuya.com
 * @brief example_scene module is used to 
 * @version 0.1
 * @date 2023-03-09
 *
 * @copyright Copyright (c) tuya.inc 2023
 *
 */

#include "tuya_app_config.h"
#include "ty_sys.h"

#include "tdl_pixel_dev_manage.h"
#include "tdl_pixel_color_manage.h"

#include "tbs_math_tools.h"
#include "tbs_color_calc.h"

#include "tbl_leds_pixel_scene.h"
#include "tbl_leds_pixel_mode.h"
#include "tfm_color_shade.h"

/***********************************************************
************************macro define************************
***********************************************************/
#define SCENE_COLOR_NUM                 3

#define FIREWORKS_HANDLE_NUM           30    //烟花柄部灯珠数据量
#define FIREWORKS_DIVERGENT_NUM        11    //烟花散开灯珠长度

#define FIREWORKS_ALL_NUM              (FIREWORKS_HANDLE_NUM + FIREWORKS_DIVERGENT_NUM)

#define CTRL_HUE_VALUE_MAX             360
#define CTRL_HUE_VALUE_MIN             0


#define FIRWAORK_ONE_PART_LEN          15
#define FIRWAORK_ONE_PART_LIGHT_LEN    5

#define FIRWAORK_TWO_PART_LEN          20
#define FIRWAORK_TWO_PART_LIGHT_LEN    6

#define FIRWAORK_THREE_PART_LEN          20
#define FIRWAORK_THREE_PART_LIGHT_LEN    8
#define FIRWAORK_THREE_PART_WHITE_LEN    1 //需要小于 FIRWAORK_THREE_PART_LIGHT_LEN


#define SCENE_MODE_OBJ_NUM             1

#define SCENE_MODE_DIREWORKS_ONE       0
#define SCENE_MODE_DIREWORKS_TWO       1
#define SCENE_MODE_DIREWORKS_THREE     2


#define SCENE_MODE_DIREWORKS_RUNNING    SCENE_MODE_DIREWORKS_ONE          
/***********************************************************
***********************typedef define***********************
***********************************************************/
//烟花场景模式使用的结构体
typedef struct {
    UCHAR_T  color_idx;
    UCHAR_T  nxt_color_idx;
    UCHAR_T  cnt;
}FIREWORKS_RUNNING_PAR_T;

/***********************************************************
********************function declaration********************
***********************************************************/


/***********************************************************
***********************variable define**********************
***********************************************************/
static USHORT_T scene_color_hue[] = {
    LIGHT_COLOR_HUE_RED,
    LIGHT_COLOR_HUE_YELLOW,
    LIGHT_COLOR_HUE_GREEN,
    LIGHT_COLOR_HUE_CYAN,
    LIGHT_COLOR_HUE_BLUE,
    LIGHT_COLOR_HUE_PURPLE,
};

/***********************************************************
***********************function define**********************
***********************************************************/
STATIC VOID __pixel_get_color_transition(PIXEL_COLOR_T *pre, PIXEL_COLOR_T *nxt, UINT_T count, UINT_T idx,
                                             PIXEL_COLOR_T *target_color)
{
    USHORT_T pre_h = 0, pre_s = 0, pre_v = 0;
    USHORT_T nxt_h = 0, nxt_s = 0, nxt_v = 0;
    SHORT_T tmp_h = 0, tmp_s = 0, tmp_v = 0;

    tbs_rgb_to_hsv(pre->red, pre->green, pre->blue, &pre_h, &pre_s, &pre_v);  //起始颜色
    tbs_rgb_to_hsv(nxt->red, nxt->green, nxt->blue, &nxt_h, &nxt_s, &nxt_v);  //目标颜色
    if (tbs_math_get_int_abs_value(pre_h - nxt_h) < LIGHT_COLOR_HUE_MAX / 2) { // str->end
        if (pre_h < nxt_h) {
            tmp_h = tbs_math_get_int_abs_value(pre_h + idx * (nxt_h - pre_h) / count);
        } else {
            tmp_h = tbs_math_get_int_abs_value(pre_h - idx * (pre_h - nxt_h) / count);
        }
    } else { // str->0->end
        if (pre_h < nxt_h) {
            tmp_h = pre_h - idx * (LIGHT_COLOR_HUE_MAX + pre_h - nxt_h) / count;
            tmp_h = (tmp_h >= LIGHT_COLOR_HUE_MIN) ? tmp_h : (tmp_h + LIGHT_COLOR_HUE_MAX);
        } else {
            tmp_h = pre_h + idx * (LIGHT_COLOR_HUE_MAX - pre_h + nxt_h) / count;
            tmp_h = (tmp_h > LIGHT_COLOR_HUE_MAX) ? (tmp_h - LIGHT_COLOR_HUE_MAX) : tmp_h;
        }
    }
    tmp_s = tbs_math_get_int_abs_value(pre_s + idx * (nxt_s - pre_s) / count);
    tmp_v = tbs_math_get_int_abs_value(pre_v + idx * (nxt_v - pre_v) / count);

    tbs_hsv_to_rgb(tmp_h, tmp_s, tmp_v, &(target_color->red), &(target_color->green), &(target_color->blue));
}


static MODE_UPDATE_RET_E __pixel_scene_mode_firework_one(PIXEL_HANDLE_T pixel_dev, PIXEL_MODE_INFOR_T *infor, PIXEL_MODE_STATE_E state)
{
    PIXEL_COLOR_T rst_color = {0,0,0,0,0};

    if (infor->cfg.color_num > PIXEL_MODE_COLOR_MAX) {
        return MODE_RET_DEFAULT;
    }

    switch (state) {
        case PIXEL_MODE_STATE_DEINT: {
            tdl_pixel_set_single_color(pixel_dev, infor->cfg.start_idx, infor->cfg.stop_idx, &rst_color);
        } break;
        case PIXEL_MODE_STATE_RESET:
        case PIXEL_MODE_STATE_INIT: {
            FIREWORKS_RUNNING_PAR_T *p_fireworks=NULL;

            p_fireworks = (FIREWORKS_RUNNING_PAR_T *)infor->reserve;
            p_fireworks->color_idx = 0;
            p_fireworks->cnt = 0;
        }
        case PIXEL_MODE_STATE_RUNING: {
            FIREWORKS_RUNNING_PAR_T *p_fireworks = (FIREWORKS_RUNNING_PAR_T *)infor->reserve;
            UCHAR_T str_idx = 0;

            tdl_pixel_cycle_shift_color(pixel_dev, infor->cfg.option.ty.dir, infor->cfg.start_idx, infor->cfg.stop_idx, 1);

            str_idx = (0 == infor->cfg.option.ty.dir) ? infor->cfg.start_idx : infor->cfg.stop_idx;

            if(p_fireworks->cnt < FIRWAORK_ONE_PART_LIGHT_LEN) {
                tdl_pixel_set_single_color(pixel_dev, str_idx, 1, &infor->cfg.color_arr[p_fireworks->color_idx]);
            }else {
               tdl_pixel_set_single_color(pixel_dev, str_idx, 1, &rst_color); 
            }

            p_fireworks->cnt++;

            if(p_fireworks->cnt >= FIRWAORK_ONE_PART_LEN) {
                p_fireworks->cnt = 0;
                p_fireworks->color_idx = (p_fireworks->color_idx + 1) % infor->cfg.color_num;
            }

            return MODE_RET_NEED_REFRESH;
        }
        default:
            break;
    }

    return MODE_RET_DEFAULT;
}

static MODE_UPDATE_RET_E __pixel_scene_mode_firework_two(PIXEL_HANDLE_T pixel_dev, PIXEL_MODE_INFOR_T *infor, PIXEL_MODE_STATE_E state)
{
    PIXEL_COLOR_T rst_color = {0,0,0,0,0};

    if (infor->cfg.color_num > PIXEL_MODE_COLOR_MAX) {
        return MODE_RET_DEFAULT;
    }

    switch (state) {
        case PIXEL_MODE_STATE_DEINT: {
            tdl_pixel_set_single_color(pixel_dev, infor->cfg.start_idx, infor->cfg.stop_idx, &rst_color);
        } break;
        case PIXEL_MODE_STATE_RESET:
        case PIXEL_MODE_STATE_INIT: {
            FIREWORKS_RUNNING_PAR_T *p_fireworks=NULL;

            p_fireworks = (FIREWORKS_RUNNING_PAR_T *)infor->reserve;
            p_fireworks->color_idx = 0;
            p_fireworks->nxt_color_idx = (p_fireworks->color_idx + 1) % infor->cfg.color_num;
            p_fireworks->cnt = 0;
        }
        case PIXEL_MODE_STATE_RUNING: {
            FIREWORKS_RUNNING_PAR_T *p_fireworks = (FIREWORKS_RUNNING_PAR_T *)infor->reserve;
            UCHAR_T str_idx = 0;
            PIXEL_COLOR_T tar_color = {0,0,0,0,0};

            tdl_pixel_cycle_shift_color(pixel_dev, infor->cfg.option.ty.dir, infor->cfg.start_idx, infor->cfg.stop_idx, 1);

            str_idx = (0 == infor->cfg.option.ty.dir) ? infor->cfg.start_idx : infor->cfg.stop_idx;

            if(p_fireworks->cnt < FIRWAORK_TWO_PART_LIGHT_LEN) {
                __pixel_get_color_transition(&infor->cfg.color_arr[p_fireworks->color_idx], &infor->cfg.color_arr[p_fireworks->nxt_color_idx],\
                                             FIRWAORK_TWO_PART_LIGHT_LEN, p_fireworks->cnt, &tar_color);

                tdl_pixel_set_single_color(pixel_dev, str_idx, 1, &tar_color);
            }else {
               tdl_pixel_set_single_color(pixel_dev, str_idx, 1, &rst_color); 
            }

            p_fireworks->cnt++;

            if(p_fireworks->cnt >= FIRWAORK_TWO_PART_LEN) {
                p_fireworks->cnt = 0;
                p_fireworks->color_idx = (p_fireworks->color_idx + 1) % infor->cfg.color_num;
                p_fireworks->nxt_color_idx = (p_fireworks->color_idx + 1) % infor->cfg.color_num;
            }

            return MODE_RET_NEED_REFRESH;
        }
        default:
            break;
    }

    return MODE_RET_DEFAULT;
}

static MODE_UPDATE_RET_E __pixel_scene_mode_firework_three(PIXEL_HANDLE_T pixel_dev, PIXEL_MODE_INFOR_T *infor, PIXEL_MODE_STATE_E state)
{
    PIXEL_COLOR_T rst_color = {0,0,0,0,0};

    if (infor->cfg.color_num > PIXEL_MODE_COLOR_MAX) {
        return MODE_RET_DEFAULT;
    }

    switch (state) {
        case PIXEL_MODE_STATE_DEINT: {
            tdl_pixel_set_single_color(pixel_dev, infor->cfg.start_idx, infor->cfg.stop_idx, &rst_color);
        } break;
        case PIXEL_MODE_STATE_RESET:
        case PIXEL_MODE_STATE_INIT: {
            FIREWORKS_RUNNING_PAR_T *p_fireworks=NULL;

            p_fireworks = (FIREWORKS_RUNNING_PAR_T *)infor->reserve;
            p_fireworks->color_idx = 0;
            p_fireworks->nxt_color_idx = (p_fireworks->color_idx + 1) % infor->cfg.color_num;
            p_fireworks->cnt = 0;
        }
        case PIXEL_MODE_STATE_RUNING: {
            FIREWORKS_RUNNING_PAR_T *p_fireworks = (FIREWORKS_RUNNING_PAR_T *)infor->reserve;
            UCHAR_T str_idx = 0;
            PIXEL_COLOR_T white_color = {LIGHT_COLOR_RESOLUTION,LIGHT_COLOR_RESOLUTION,LIGHT_COLOR_RESOLUTION,0,0};

            tdl_pixel_cycle_shift_color(pixel_dev, infor->cfg.option.ty.dir, infor->cfg.start_idx, infor->cfg.stop_idx, 1);

            str_idx = (0 == infor->cfg.option.ty.dir) ? infor->cfg.start_idx : infor->cfg.stop_idx;

            if(p_fireworks->cnt < FIRWAORK_THREE_PART_LIGHT_LEN) {
                if((p_fireworks->cnt < FIRWAORK_THREE_PART_WHITE_LEN) || (p_fireworks->cnt >= (FIRWAORK_THREE_PART_LIGHT_LEN - FIRWAORK_THREE_PART_WHITE_LEN))) {
                    tdl_pixel_set_single_color(pixel_dev, str_idx, 1, &white_color);
                }else {
                   tdl_pixel_set_single_color(pixel_dev, str_idx, 1, &infor->cfg.color_arr[p_fireworks->color_idx]); 
                }
            }else {
               tdl_pixel_set_single_color(pixel_dev, str_idx, 1, &rst_color); 
            }

            p_fireworks->cnt++;

            if(p_fireworks->cnt >= FIRWAORK_THREE_PART_LEN) {
                p_fireworks->cnt = 0;
                p_fireworks->color_idx = (p_fireworks->color_idx + 1) % infor->cfg.color_num;
                p_fireworks->nxt_color_idx = (p_fireworks->color_idx + 1) % infor->cfg.color_num;
            }

            return MODE_RET_NEED_REFRESH;
        }
        default:
            break;
    }

    return MODE_RET_DEFAULT;
}



void example_scene_diy_fireworks(void)
{
    OPERATE_RET rt = OPRT_OK;

    // 初始化渐变场景服务
    TUYA_CALL_ERR_GOTO(tbl_pixel_scene_init(LIGHT_DEV_NAME, SCENE_MODE_OBJ_NUM), __EXIT);

    //添加自己实现的烟花场景
    TUYA_CALL_ERR_GOTO(tbl_pixel_scene_add_mode(SCENE_MODE_DIREWORKS_ONE, __pixel_scene_mode_firework_one), __EXIT);
    TUYA_CALL_ERR_GOTO(tbl_pixel_scene_add_mode(SCENE_MODE_DIREWORKS_TWO, __pixel_scene_mode_firework_two), __EXIT);
    TUYA_CALL_ERR_GOTO(tbl_pixel_scene_add_mode(SCENE_MODE_DIREWORKS_THREE, __pixel_scene_mode_firework_three), __EXIT);

    // 配置场景
    PIXEL_MODE_CFG_T scene_mode_cfg = {
        .start_idx = 0,
        .stop_idx = FIREWORKS_ALL_NUM - 1,
        .option.ty = {
            .is_transit = 1,
            .is_cycle = 1,
            .dir = 0,
            .is_multi_part = 0,
        },
    };

    COLOR_HSV_T color_hsv = {0, LIGHT_COLOR_RESOLUTION, LIGHT_COLOR_RESOLUTION};

    for(int i=0; i < CNTSOF(scene_color_hue); i++) {
        if(i >= PIXEL_MODE_COLOR_MAX) {
            break;
        }

        color_hsv.hue = scene_color_hue[i];
        tbs_hsv_to_rgb(color_hsv.hue, color_hsv.sat, color_hsv.val, \
                       &scene_mode_cfg.color_arr[i].red, &scene_mode_cfg.color_arr[i].green, &scene_mode_cfg.color_arr[i].blue);
        scene_mode_cfg.color_num++;
    } 

    TUYA_CALL_ERR_GOTO(tbl_pixel_scene_mode_cfg(0, SCENE_MODE_DIREWORKS_RUNNING, &scene_mode_cfg, 60), __EXIT);
   
    // 选择一种场景模式启动
    TUYA_CALL_ERR_GOTO(tbl_pixel_scene_start(), __EXIT);

__EXIT:
    return;
}

