// Copyright (C) 2018-2020 Alibaba Group Holding Limited
// Adaptations to ESP-IDF Copyright (c) 2020 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed 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.

#include <stdio.h>
#include <stdint.h>
#include <string.h>

#include "genie_mesh.h"
#include "genie_event.h"
#include "genie_util.h"
#include "genie_timer.h"
#include "genie_model_srv.h"
#include "ble_mesh_example_nvs.h"

#define DAY     86400
#define HOUR    3600
#define MINU    60

#define VT_NUM (40)
#define VT_LOCK    util_semaphore_take(&g_genie_timer.lock, -1)
#define VT_UNLOCK  util_semaphore_give(&g_genie_timer.lock)

extern nvs_handle_t NVS_HANDLE;

static const char *TAG = "genie_timer";

static utc_time_t local_time = {0};

typedef enum {
    TIMER_OFF     = 0,
    TIMER_ON      = 1,
    TIMER_INVAILD = 0xf,
} vt_state;

struct genie_timer_t {
    genie_snode_t next;
    uint8_t index;
    uint8_t state: 4;
    uint8_t periodic: 1;
    uint16_t periodic_time;
    uint8_t schedule;
    uint32_t unixtime_match;
    genie_timer_attr_data_t attr_data;
};

struct unixtime_sync_para_t {
    uint16_t period_time; // Synchronization period: Time synchronization request cycle.
    uint8_t retry_delay;  // Retry delay: How long to retry after a failed time synchronization request
    uint8_t retry_times;  // Retry times of time synchronization request
};

struct {
    uint16_t magic;
    int8_t timezone;
    struct unixtime_sync_para_t timing_sync_config;  // Time synchronization parameters
    struct genie_timer_t timer_data[VT_NUM];    // Store Timing operation
} g_timing_data;

struct {
    uint32_t init: 1;
    uint32_t update: 1;
    esp_timer_handle_t timer;
    struct k_work work;
    util_semaphore_t lock;
    genie_slist_t timer_list_active;
    genie_slist_t timer_list_idle;
    uint32_t unix_time;
    uint32_t unix_time_sync_match;
    uint8_t  unix_time_sync_retry_times;
    genie_timer_event_func_t cb;
} g_genie_timer;

static inline uint8_t is_leap_year(uint16_t year);

utc_time_t genie_timer_local_time_get(void)
{
    return local_time;
}

uint32_t genie_timer_local_unixtime_get(void)
{
    return g_genie_timer.unix_time;
}

static inline utc_time_t convert_unix_to_utc(uint32_t unix_time)
{
    uint16_t g_noleap_daysbeforemonth[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};

    utc_time_t utc     = {0};
    uint32_t   epoch   = 0;
    uint32_t   jdn     = 0;
    int        year    = 0;
    int        month   = 0;
    int        day     = 0;
    int        hour    = 0;
    int        minutes = 0;
    int        sec     = 0;
    int        weekday = 0;

    epoch = unix_time;

    jdn    = epoch / DAY;
    epoch -= DAY * jdn;

    weekday = (jdn + 4) % 7; // 1970/1/1 is thursday

    hour   = epoch / HOUR;
    epoch -= HOUR * hour;

    minutes = epoch / MINU;
    epoch  -= MINU * minutes;

    sec    = epoch;

    year   = jdn   / (4 * 365 + 1); /* Number of 4-years periods since the epoch */
    jdn   -= year * (4 * 365 + 1);  /* Remaining days */
    year <<= 2;                  /* Years since the epoch */

    /* Then we will brute force the next 0-3 years */
    bool leapyear;
    int  tmp;

    for (; ;) {
        /* Is this year a leap year (we'll need this later too) */
        leapyear = is_leap_year(year + 1970);

        /* Get the number of days in the year */
        tmp = (leapyear ? 366 : 365);

        /* Do we have that many days? */
        if (jdn >= tmp) {
            /* Yes.. bump up the year */
            year++;
            jdn -= tmp;
        } else {
            /* Nope... then go handle months */
            break;
        }
    }

    /* At this point, value has the year and days has number days into this year */
    year += 1970;

    /* Handle the month (zero based) */
    int min   = 0;
    int max   = 11;
    int value = 0;

    do {
        /* Get the midpoint */
        value = (min + max) >> 1;

        /* Get the number of days that occurred before the beginning of the month
         * following the midpoint.
         */
        tmp = g_noleap_daysbeforemonth[value + 1];
        if (value + 1 >= 2 && is_leap_year(leapyear)) {
            tmp++;
        }

        /* Does the number of days before this month that equal or exceed the
         * number of days we have remaining?
         */
        if (tmp > jdn) {
            /* Yes.. then the month we want is somewhere from 'min' and to the
             * midpoint, 'value'.  Could it be the midpoint?
             */
            tmp = g_noleap_daysbeforemonth[value];
            if (value >= 2 && is_leap_year(leapyear)) {
                tmp++;
            }

            if (tmp > jdn) {
                /* No... The one we want is somewhere between min and value-1 */
                max = value - 1;
            } else {
                /* Yes.. 'value' contains the month that we want */
                break;
            }
        } else {
            /* No... The one we want is somwhere between value+1 and max */
            min = value + 1;
        }

        /* If we break out of the loop because min == max, then we want value
         * to be equal to min == max.
         */
        value = min;
    } while (min < max);

    /* The selected month number is in value. Subtract the number of days in the
     * selected month
     */
    tmp = g_noleap_daysbeforemonth[value];

    if (value >= 2 && is_leap_year(leapyear)) {
        tmp++;
    }

    jdn -= tmp;

    /* At this point, value has the month into this year (zero based) and days has
     * number of days into this month (zero based)
     */
    month = value;   // zero based
    day   = jdn + 1; // one based

    utc.year    = year;
    utc.month   = month;
    utc.day     = day;
    utc.weekday = weekday;
    utc.hour    = hour;
    utc.minutes = minutes;
    utc.seconds = sec;

    return utc;
}

static inline uint32_t convert_utc_to_unix(utc_time_t *utc_time)
{
    uint32_t days;
    uint16_t g_noleap_daysbeforemonth[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};

    days = (utc_time->year - 1970) * 365;
    days += (utc_time->year - 1969) >> 2;

    days += g_noleap_daysbeforemonth[utc_time->month];

    if (utc_time->month >= 2 && is_leap_year(utc_time->year)) {
        days++;
    }

    days += utc_time->day - 1;

    return ((days * 24 + utc_time->hour) * 60 + utc_time->minutes) * 60 + utc_time->seconds;
}

static inline uint8_t is_leap_year(uint16_t year)
{
    if (((year % 4) == 0) && ((year % 100) != 0)) {
        return 1;
    } else if ((year % 400) == 0) {
        return 1;
    } else {
        return 0;
    }
}

static inline void month_update(void)
{
    local_time.month++;

    if (local_time.month >= 12) {
        local_time.month = 0;
        local_time.year++;
    }
}

static inline void days_update(void)
{
    uint8_t month_days_list[12] = {
        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
    };

    local_time.day++;

    if (is_leap_year(local_time.year)) {
        month_days_list[1] = 29;
    }

    uint8_t month_day = month_days_list[local_time.month];

    if (local_time.day > month_day) {
        local_time.day = 0;
        month_update();
    }

    local_time.weekday = (local_time.weekday + 1) % 7;
}

static inline void hours_update(void)
{
    local_time.hour++;

    if (local_time.hour == 24) {
        local_time.hour = 0;
        days_update();
    }
}

static inline void minutes_update(void)
{
    local_time.minutes++;

    if (local_time.minutes == 60) {
        local_time.minutes = 0;
        hours_update();
    }
}

static inline void seconds_update(void)
{
    local_time.seconds++;

    if (local_time.seconds == 60) {
        local_time.seconds = 0;
        minutes_update();
    }
}

static void genie_timer_update(void *args)
{
    if (!g_genie_timer.update) {
        ESP_LOGD(TAG, "g_genie_timer.update %d", g_genie_timer.update);
        return;
    }

    g_genie_timer.unix_time += 1;
    seconds_update();

    k_work_submit(&g_genie_timer.work);

    if (g_genie_timer.unix_time % 60 == 0) {
        ESP_LOGI(TAG, "genie_timer_update %d", g_genie_timer.unix_time);
    }
}

static inline uint8_t next_weekday_diff_get(uint8_t weekday_now, uint8_t schedule)
{
    uint16_t schedule_tmp = 0;

    if (weekday_now == 0) {
        weekday_now = 7;
    }

    schedule_tmp = ((schedule | ((uint16_t) schedule << 7)) >> (weekday_now - 1)) & 0x7f;

    uint8_t day_diff = 0;

    while (day_diff < 7) {
        if ((schedule_tmp >> day_diff)  & 0x0001) {
            break;
        }
        day_diff++;
    }

    return day_diff;
}

static inline uint8_t is_weekday_match(uint8_t weekday_now, uint8_t schedule)
{
    uint8_t weekday_mask = weekday_now ? (uint8_t)(1 << (weekday_now - 1)) : (uint8_t)(1 << 6);
    return (weekday_mask == (schedule & weekday_mask));
}

static inline uint8_t next_weekday(uint8_t weekday_now)
{
    return (weekday_now + 1) % 7;
}

static int genie_timer_save(void)
{
#ifdef GENIE_VENDOR_TIMER_STORE
    int ret = 0;

    ret = ble_mesh_nvs_store(NVS_HANDLE, GENIE_STORE_VENDOR_TIMER, &g_timing_data, sizeof(g_timing_data));
    if (ret) {
        ESP_LOGI(TAG, "vendor timers save fail %d", ret);
    }

    return ret;
#else
    return -1;
#endif
}

static int genie_timer_restore(void)
{
#ifdef GENIE_VENDOR_TIMER_STORE
    int  ret   = 0;
    bool exist = false;

    memset(&g_timing_data, 0, sizeof(g_timing_data));

    ret = ble_mesh_nvs_restore(NVS_HANDLE, GENIE_STORE_VENDOR_TIMER, &g_timing_data, sizeof(g_timing_data), &exist);
    if (ret) {
        ESP_LOGI(TAG, "vendor timers restore fail %d", ret);
        return ret;
    }

    if (g_timing_data.magic != 0xABCD
            || g_timing_data.timezone < -12
            || g_timing_data.timezone > 12) {
        ESP_LOGI(TAG, "vendor timers restore missmatch");
        return -1;
    }

    for (int i = 0; i < VT_NUM; i++) {
        if (g_timing_data.timer_data[i].state != TIMER_INVAILD) {
            genie_slist_append(&g_genie_timer.timer_list_active, &g_timing_data.timer_data[i].next);
        } else {
            genie_slist_append(&g_genie_timer.timer_list_idle, &g_timing_data.timer_data[i].next);
        }

        ESP_LOGI(TAG, "restore vendor timer index %d state %d periodic %d periodic_time %d schedule %d unixtime_match %d",
                 g_timing_data.timer_data[i].index, g_timing_data.timer_data[i].state,
                 g_timing_data.timer_data[i].periodic, g_timing_data.timer_data[i].periodic_time,
                 g_timing_data.timer_data[i].schedule, g_timing_data.timer_data[i].unixtime_match);
    }

    return 0;
#else
    return -1;
#endif
}

static uint8_t is_genie_timer_timeout(struct genie_timer_t *vendor_timer)
{
    if (vendor_timer->state == TIMER_INVAILD) {
        return 0;
    }

    if (vendor_timer->periodic) {
        if (is_weekday_match(local_time.weekday, vendor_timer->schedule)
                && vendor_timer->unixtime_match < g_genie_timer.unix_time) {
            vendor_timer->unixtime_match += (1 + next_weekday_diff_get(next_weekday(local_time.weekday), vendor_timer->schedule)) * DAY;
        }
    }

    return vendor_timer->unixtime_match == g_genie_timer.unix_time;
}

static void genie_timer_check(void)
{
    struct genie_timer_t *tmp, *node;
    GENIE_SLIST_FOR_EACH_CONTAINER_SAFE(&g_genie_timer.timer_list_active, node,  tmp, next) {
        if (is_genie_timer_timeout(node)) {
            if (g_genie_timer.cb) {
                g_genie_timer.cb(GENIE_TIME_EVT_TIMEOUT, node->index, &node->attr_data);
            }

            VT_LOCK;

            if (!node->periodic) {
                node->unixtime_match = 0xffffffff;
                node->state          = TIMER_INVAILD;
                genie_slist_find_and_remove(&g_genie_timer.timer_list_active, &node->next);
                genie_slist_append(&g_genie_timer.timer_list_idle, &node->next);
            } else {
                node->unixtime_match += 24 * HOUR;
            }

            VT_UNLOCK;
            genie_timer_save();
        }
    }

    if (g_genie_timer.unix_time_sync_match
            && g_genie_timer.unix_time_sync_match <= g_genie_timer.unix_time) {
        if (g_genie_timer.cb) {
            int ret = g_genie_timer.cb(GENIE_TIME_EVT_TIMING_SYNC, 0, NULL);

            if (ret) {
                if (g_genie_timer.unix_time_sync_retry_times > 0) {
                    g_genie_timer.unix_time_sync_match += g_timing_data.timing_sync_config.retry_delay * MINU;
                    g_genie_timer.unix_time_sync_retry_times--;
                    return;
                }
            }
        }

        g_genie_timer.unix_time_sync_match       = g_genie_timer.unix_time + g_timing_data.timing_sync_config.period_time * MINU;
        g_genie_timer.unix_time_sync_retry_times = g_timing_data.timing_sync_config.retry_times;
    }
}

static void genie_timer_check_work(struct k_work *work)
{
    genie_timer_check();
}

static genie_timer_handle_t genie_timer_find(uint8_t index)
{
    ENTER_FUNC();
    if (index >= VT_NUM) {
        return NULL;
    }

    VT_LOCK;
    struct genie_timer_t *tmp, *node = NULL;
    genie_slist_t *list = NULL;
    list = &g_genie_timer.timer_list_active;

    GENIE_SLIST_FOR_EACH_CONTAINER_SAFE(list, node,  tmp, next) {
        if (node->index == index) {
            VT_UNLOCK;
            return node;
        }
    }
    VT_UNLOCK;

    return NULL;
}

static struct genie_timer_t *genie_timer_new(void)
{
    ENTER_FUNC();
    struct genie_timer_t *free_timer = NULL;
    VT_LOCK;
    free_timer = (struct genie_timer_t *)genie_slist_get(&g_genie_timer.timer_list_idle);
    VT_UNLOCK;
    ESP_LOGI(TAG, "timer new %p", free_timer);
    return free_timer;
}

int genie_timer_start(uint8_t index, uint32_t unix_time, genie_timer_attr_data_t *attr_data)
{
    ENTER_FUNC();
    struct genie_timer_t *vendor_timer = NULL;

    if (!attr_data) {
        return -GENIE_TIMER_ERR_PARAM;
    }

    ESP_LOGI(TAG, "timer start index %d expect unix_time %d attr_type %d",
             index,  unix_time, attr_data->type);

    if (!g_genie_timer.init) {
        return -GENIE_TIMER_ERR_INIT;
    }

    if (!g_genie_timer.update) {
        return -GENIE_TIMER_ERR_LOCALTIME_NOTSET;
    }

    if (index >= VT_NUM) {
        //return -GENIE_TIMER_ERR_INDEX;
    }

    if (unix_time <= g_genie_timer.unix_time) {
        return -GENIE_TIMER_ERR_PARAM;
    }

    vendor_timer = genie_timer_find(index);

    if (vendor_timer == NULL) {
        vendor_timer = genie_timer_new();

        if (vendor_timer == NULL) {
            return -GENIE_TIMER_ERR_NORESOURCE;
        }
    } else {
        VT_LOCK;
        genie_slist_find_and_remove(&g_genie_timer.timer_list_active, &vendor_timer->next);
        VT_UNLOCK;
    }

    vendor_timer->index          = index;
    vendor_timer->unixtime_match = unix_time;        // + g_genie_timer.timezone * HOUR;
    vendor_timer->state          = TIMER_ON;
    vendor_timer->attr_data.type = attr_data->type;
    vendor_timer->attr_data.para = attr_data->para;

    VT_LOCK;
    genie_slist_append(&g_genie_timer.timer_list_active, &vendor_timer->next);
    VT_UNLOCK;

    genie_timer_save();

    return 0;
}

int genie_timer_periodic_start(uint8_t index, uint16_t periodic_time, uint8_t schedule, genie_timer_attr_data_t *attr_data)
{
    ENTER_FUNC();
    struct genie_timer_t *vendor_timer = NULL;

    ESP_LOGI(TAG, "periodic timer start index %d periodic_time %d schedule %d attr_para %d",
             index,  periodic_time,  schedule, attr_data->para);

    if (!g_genie_timer.init) {
        return -GENIE_TIMER_ERR_INIT;
    }

    if (!g_genie_timer.update) {
        return -GENIE_TIMER_ERR_LOCALTIME_NOTSET;
    }

    if (index >= VT_NUM) {
        //return -GENIE_TIMER_ERR_INDEX;
    }

    if (schedule == 0) {
        return -GENIE_TIMER_ERR_PARAM;
    }

    vendor_timer = genie_timer_find(index);

    if (vendor_timer == NULL) {
        vendor_timer = genie_timer_new();

        if (vendor_timer == NULL) {
            return -GENIE_TIMER_ERR_NORESOURCE;
        }
    } else {
        VT_LOCK;
        genie_slist_find_and_remove(&g_genie_timer.timer_list_active, &vendor_timer->next);
        VT_UNLOCK;
    }

    vendor_timer->index         = index;
    vendor_timer->periodic      = 1;
    vendor_timer->periodic_time = periodic_time;
    vendor_timer->schedule      = schedule;
    vendor_timer->state         = TIMER_ON;

    utc_time_t utc         = local_time;
    utc.hour    = 0;
    utc.minutes = 0;
    utc.seconds = 0;
    utc.day     = utc.day + next_weekday_diff_get(local_time.weekday, schedule);

    vendor_timer->unixtime_match = convert_utc_to_unix(&utc) + periodic_time - g_timing_data.timezone * HOUR;

    ESP_LOGI(TAG, "periodic timer unixtime_match %d", vendor_timer->unixtime_match);

    VT_LOCK;
    genie_slist_append(&g_genie_timer.timer_list_active, &vendor_timer->next);
    VT_UNLOCK;
    genie_timer_save();
    return 0;
}

static int genie_timer_stop(int8_t index)
{
    ENTER_FUNC();
    ESP_LOGI(TAG, "timer stop %d", index);

    if (!g_genie_timer.init) {
        return -GENIE_TIMER_ERR_INIT;
    }

    if (!g_genie_timer.update) {
        return -GENIE_TIMER_ERR_LOCALTIME_NOTSET;
    }

    if (index >= VT_NUM) {
        return -GENIE_TIMER_ERR_INDEX;
    }

    struct genie_timer_t *vendor_timer = genie_timer_find(index);

    if (vendor_timer == NULL) {
        return -GENIE_TIMER_ERR_INDEX;
    } else {
        VT_LOCK;
        vendor_timer->index          = 0xFF;
        vendor_timer->state          = TIMER_INVAILD;
        vendor_timer->unixtime_match = 0;
        genie_slist_find_and_remove(&g_genie_timer.timer_list_active, &vendor_timer->next);
        genie_slist_append(&g_genie_timer.timer_list_idle, &vendor_timer->next);
        VT_UNLOCK;
    }

    return 0;
}

int genie_timer_remove(uint8_t index)
{
    ENTER_FUNC();
    int i   = 0;
    int ret = 0;

    ESP_LOGI(TAG, "timer remove %d", index);

    /* remove alll timers */
    if (index == 0xFF) {
        for (i = 0; i < VT_NUM; i++) {
            genie_timer_stop(i);
        }

        return 0;
    }

    ret = genie_timer_stop(index);

    genie_timer_save();

    return ret;
}

void genie_timer_local_time_show(void)
{
    ENTER_FUNC();
    ESP_LOGI(TAG, "unix_time revert %d", convert_utc_to_unix(&local_time));
    ESP_LOGI(TAG, "%4d/%2d/%2d %2d:%2d:%d weekday %2d %04d",
             local_time.year, local_time.month + 1, local_time.day,
             local_time.hour, local_time.minutes, local_time.seconds,
             local_time.weekday, g_timing_data.timezone);
}

int genie_timer_timezone_update(int8_t timezone)
{
    ENTER_FUNC();
    ESP_LOGI(TAG, "timezone update %d", timezone);

    if (timezone < -12 || timezone > 12) {
        return -GENIE_TIMER_ERR_PARAM;
    }

    if (!g_genie_timer.init) {
        return -GENIE_TIMER_ERR_INIT;
    }

    g_timing_data.timezone = timezone;

    return 0;
}

int8_t genie_timer_timezone_get(void)
{
    ENTER_FUNC();

    return g_timing_data.timezone;
}

int genie_timer_time_sync_set(uint16_t period_time, uint8_t retry_delay,   uint8_t retry_times)
{
    ENTER_FUNC();
    ESP_LOGI(TAG, "timing sync set period_time %d retry_delay %d retry_times %d",
             period_time, retry_delay, retry_times);

    if (period_time == 0 || retry_delay == 0 || retry_times) {
        return -GENIE_TIMER_ERR_PARAM;
    }

    g_timing_data.timing_sync_config.period_time = period_time;
    g_timing_data.timing_sync_config.retry_delay = retry_delay;
    g_timing_data.timing_sync_config.retry_times = retry_times;

    g_genie_timer.unix_time_sync_match       = g_genie_timer.unix_time +  g_timing_data.timing_sync_config.period_time * MINU;
    g_genie_timer.unix_time_sync_retry_times = retry_times;

    return 0;
}

int genie_timer_time_sync_get(uint16_t *period_time, uint8_t *retry_delay, uint8_t *retry_times)
{
    ENTER_FUNC();

    *period_time = g_timing_data.timing_sync_config.period_time;
    *retry_delay = g_timing_data.timing_sync_config.retry_delay;
    *retry_times = g_timing_data.timing_sync_config.retry_times;

    return 0;
}

int genie_timer_local_time_update(uint32_t unix_time)
{
    ENTER_FUNC();
    if (!g_genie_timer.init) {
        ESP_LOGE(TAG, "g_genie_timer not init");
        return -GENIE_TIMER_ERR_INIT;
    }

    g_genie_timer.update    = 1;
    g_genie_timer.unix_time = unix_time;

    local_time = convert_unix_to_utc(unix_time + g_timing_data.timezone * HOUR);

    ESP_LOGI(TAG, "unix_time %d", unix_time);
    ESP_LOGI(TAG, "localtime update %4d/%2d/%2d %2d:%2d:%d weekday %2d",
             local_time.year, local_time.month + 1, local_time.day,
             local_time.hour, local_time.minutes, local_time.seconds,
             local_time.weekday);
    ESP_LOGI(TAG, "unix_time revert %d", convert_utc_to_unix(&local_time));

    return 0;
}

int genie_timer_init(genie_timer_event_func_t cb)
{
    ENTER_FUNC();
    int i = 0;

    if (g_genie_timer.init) {
        return 0;
    }

    if (cb == NULL) {
        return -GENIE_TIMER_ERR_INIT;
    }

    memset(&g_genie_timer, 0, sizeof(g_genie_timer));
    memset(&local_time, 0, sizeof(local_time));

    g_genie_timer.cb = cb;

    genie_slist_init(&g_genie_timer.timer_list_active);
    genie_slist_init(&g_genie_timer.timer_list_idle);

    util_semaphore_init(&g_genie_timer.lock, 1, 1);

    k_work_init(&g_genie_timer.work, genie_timer_check_work);

    esp_timer_create_args_t create_args = {
        .callback = genie_timer_update,
        .name     = "genie_timer"
    };

    ESP_ERROR_CHECK(esp_timer_create(&create_args, &g_genie_timer.timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(g_genie_timer.timer, 1000 * 1000));

    g_genie_timer.init = 1;

    if (genie_timer_restore()) {
        memset(&g_timing_data, 0, sizeof(g_timing_data));
        g_timing_data.timezone = 8;
        g_timing_data.magic    = 0xABCD;

        for (i = 0; i < VT_NUM; i++) {
            g_timing_data.timer_data[i].unixtime_match = 0xffffffff;
            g_timing_data.timer_data[i].index = 0xFF;
            g_timing_data.timer_data[i].state = TIMER_INVAILD;
            genie_slist_append(&g_genie_timer.timer_list_idle, &g_timing_data.timer_data[i].next);
        }
    }

    /* sync timing */
    // g_genie_timer.cb(GENIE_TIME_EVT_TIMING_SYNC, 0, NULL);

    return 0;
}
