# -*- coding:utf-8 -*-
# coding=utf-8

import sys
import os
import codecs
import shutil
import random
import re
import json
import struct

reload(sys)
sys.setdefaultencoding('utf-8')

os.chdir(sys.path[0])
GPIO_CONFIG_FILE = "./custom_config.json"
GPIO_CONFIG_CODE_FILE = "../../app/ivm/user/uni_auto_control.c"
AUTOCONF_FILE = "../../include/autoconf.h"

GPIO_DEF_VOLTAGE_MAP = {}

setting_pinmux_str = ''
setting_resume_str = ''
setting_suspend_str = ''
setting_init_str = ''
setting_handle_kws_event_str = ''
setting_volume_kws_event = []

'''
/* Default padmux config DONOT edit */
static const UNI_PIN_CONFIG padmux_table[] = {
/*   id  func     func0 | func1 |  func2 | func3 | func4 | ... */
    { 0,  0},  // 0:JTAG_TMS | 1:GPIO_00 | 2:I2S_IN_BCLK_OUT_M | 3:I2S_OUT_BCLK_OUT_M | 4:SPI_CSn_M
               // 5:SPI_CSn_S | 6:REC_BCLK_OUT | 7:PDM_DATA | 8:I2S_OUT_BCLK_IN_S
               // 9:REC_BCLK_IN | 10:I2S_IN_BCLK_IN_S
    { 1,  0},  // 0:JTAG_CK | 1:GPIO_01 | 2:I2S_IN_DATA_IN | 3:I2S_OUT_DATA_OUT | 4:SPI_MISO_M
               // 5:SPI_MISO_S | 6:REC_DATA_OUT | 7:PDM_CLK_IN | 8:PWM_CLK_IN
               // 9:PDM_CLK_OUT
    { 2,  0},  // 0:GPIO_02 | 1:PWM_CLK_IN
    { 3,  0},  // 0:I2C0_SDA | 1:GPIO_03 | 2:PDM_CLK_IN | 3:PDM_CLK_OUT
    { 4,  0},  // 0:I2C0_SCL | 1:GPIO_04 | 2:PDM_DATA
    { 5,  0},  // 0:UART0_TX | 1:GPIO_05 | 2:I2C1_SDA | 3:PDM_CLK_IN | 4:DAC_OUT_L
               // 5:PDM_CLK_OUT
    { 6,  0},  // 0:UART0_RX | 1:GPIO_06 | 2:I2C1_SCL | 3:PDM_DATA | 4:DAC_OUT_R
               // 5:PWM_CLK_IN
    { 7,  0},  // 0:PDM_CLK_IN | 1:GPIO_07 | 2:I2S_IN_MCLK_OUT_M | 3:I2S_OUT_MCLK_OUT_M | 4:SPI_SCLK_M
               // 5:SPI_SCLK_S | 6:REC_MCLK_OUT | 7:DAC_OUT_L | 8:I2S_OUT_MCLK_IN_S
               // 9:REC_MCLK_IN | 10:I2S_IN_MCLK_IN_S | 11:UART0_CTS | 12:PDM_CLK_OUT
    { 8,  0},  // 0:PDM_DATA | 1:GPIO_08 | 2:I2S_IN_LRCLK_OUT_M | 3:I2S_OUT_LRCLK_OUT_M | 4:SPI_MOSI_M
               // 5:SPI_MOSI_S | 6:REC_LRCLK_OUT | 7:DAC_OUT_R | 8:I2S_OUT_LRCLK_IN_S
               // 9:REC_LRCLK_IN | 10:I2S_IN_LRCLK_IN_S | 11:UART0_RTS | 12:PWM_CLK_IN
    { 9,  0},  // 0:I2C1_SDA | 1:GPIO_09 | 2:I2S_IN_BCLK_OUT_M | 3:I2S_OUT_BCLK_OUT_M | 4:SPI_CSn_M
               // 5:SPI_CSn_S | 6:REC_BCLK_OUT | 7:UART1_CTS | 8:I2S_OUT_BCLK_IN_S
               // 9:REC_BCLK_IN | 10:I2S_IN_BCLK_IN_S
    {10,  0},  // 0:I2C1_SCL | 1:GPIO_10 | 2:I2S_IN_DATA_IN | 3:I2S_OUT_DATA_OUT | 4:SPI_MISO_M
               // 5:SPI_MISO_S | 6:REC_DATA_OUT | 7:UART1_RTS | 8:PWM_CLK_IN
    {11,  0},  // 0:UART1_TX | 1:GPIO_11 | 2:I2S_IN_MCLK_OUT_M | 3:I2S_OUT_MCLK_OUT_M | 4:SPI_SCLK_M
               // 5:SPI_SCLK_S | 6:REC_MCLK_OUT | 7:DAC_OUT_L | 8:I2S_OUT_MCLK_IN_S
               // 9:REC_MCLK_IN | 10:I2S_IN_MCLK_IN_S
    {12,  0},  // 0:UART1_RX | 1:GPIO_12 | 2:I2S_IN_LRCLK_OUT_M | 3:I2S_OUT_LRCLK_OUT_M | 4:SPI_MOSI_M
               // 5:SPI_MOSI_S | 6:REC_LRCLK_OUT | 7:DAC_OUT_R | 8:I2S_OUT_LRCLK_IN_S
               // 9:REC_LRCLK_IN | 10:I2S_IN_LRCLK_IN_S
    {13,  0},  // 0:SPI_FLASH_SCLK | 1:GPIO_13
    {14,  0},  // 0:SPI_FLASH_MOSI | 1:GPIO_14
    {15,  0},  // 0:SPI_FLASH_CSn | 1:GPIO_15
    {16,  0},  // 0:SPI_FLASH_MISO | 1:GPIO_16
    {17,  0},  // 0:SPI_FLASH_WP | 1:GPIO_17
    {18,  0},  // 0:SPI_FLASH_HOLD | 1:GPIO_18
    {19,  0},  // NA
    {20,  0},  // NA
    {21,  0},  // NA
    {22,  0},  // NA
    {23,  0},  // NA
    {24,  0},  // NA
    {25,  0},  // NA
    {26,  0},  // NA
    {27,  0},  // NA
    {28,  0},  // NA
    {29,  0},  // NA
    {30,  0},  // NA
    {31,  0},  // NA
};
'''

UNI_AUTO_CONTROL_C_TEMPLATE = '''\
/**************************************************************************
 * Copyright (C) 2020-2020  Unisound
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 **************************************************************************
 *
 * Description : uni_auto_control.c
 * Author      : yuanshifeng@unisound.com
 * Date        : 2021.03.01
 *
 **************************************************************************/
#include "user_gpio.h"
#include "lvp_mp3_player.h"
#include "printf.h"

#define LOG_TAG "[uni_auto_ctrl]"

static void _user_gpio_set_pinmux(void) {
%(setting_pinmux)s
}

void user_gpio_resume(void) {
  _user_gpio_set_pinmux();
%(setting_resume)s
}

void user_gpio_suspend(void) {
%(setting_suspend)s
}

int user_gpio_init(void) {
  _user_gpio_set_pinmux();

%(setting_init)s

  printf(LOG_TAG " %%s success\\n", __func__);
  return 0;
}

int user_gpio_handle_kws_event(uni_kws_result_t *kws_result,
                               uint8_t *need_reply) {
  if (kws_result != NULL) {
    printf(LOG_TAG "handle kws result action: %%s\\n", kws_result->action);
%(setting_handle_kws_event)s
  }

  return 0;
}
'''


def _gen_uart_init_str(pin_config):
    tmp_str = ''
    if 'params' in pin_config:
        user_pin = 'USER_PIN_NUM_' + str(pin_config["pin"])
        baud_rate = pin_config["params"]["baud_rate"]
        data_bits = pin_config["params"]["data_bits"]
        stop_bits = pin_config["params"]["stop_bits"]
        parity_bits = pin_config["params"]["parity_bits"]
        if parity_bits == "none":
            real_parity_bits = '0'
        elif parity_bits == "odd":
            real_parity_bits = '1'
        elif parity_bits == "even":
            real_parity_bits = '2'
        else:
            real_parity_bits = '0'
        tmp_str = '  user_uart_init(%(user_pin)s, %(baud_rate)s, %(data_bits)s, %(stop_bits)s, %(parity_bits)s);' % \
                  {'user_pin': user_pin, 'baud_rate': str(baud_rate), 'data_bits': str(data_bits),
                   'stop_bits': str(stop_bits), 'parity_bits': real_parity_bits}
    return tmp_str


def _get_setting_pinmux(pin_config_arry, reply_type):
    global setting_pinmux_str
    log_enabled = True
    tmp_str_list = []
    for pin_config in pin_config_arry:
        user_pin = 'USER_PIN_NUM_' + str(pin_config["pin"])
        if 'GPIO' == pin_config["function"][0:4] or 'PWM' == pin_config["function"][0:3]:
            pin_func = 'USER_PIN_FUNC_GPIO_PWM'
        elif 'UART' == pin_config["function"][0:4]:
            pin_func = 'USER_PIN_FUNC_UART'
        elif 'DEFAULT_P05' == pin_config["function"]:
            if reply_type == 'speaker':
                pin_func = 'USER_PIN_FUNC_DAC'
            else:
                pin_func = 'USER_PIN_FUNC_GPIO_PWM'
        elif 'DEFAULT_P06' == pin_config["function"] or 'DEFAULT_P01' == pin_config["function"]:
            pin_func = 'USER_PIN_FUNC_GPIO_PWM'
        elif 'DEFAULT_P11' == pin_config["function"] or 'DEFAULT_P12' == pin_config["function"]:
            pin_func = 'USER_PIN_FUNC_UART'
        else:
            print("Unknown pin num: " + str(pin_config["pin"]) + ' func ' + pin_config["function"])
            continue
        if pin_config["pin"] in [12, 13] and not pin_config["function"][0:9] == 'DEFAULT_P':
            print("pin",pin_config["pin"],"is used as a function pin, will disable log print")
            log_enabled = False
        
        tmp_str_list.append('  user_pin_set_func(%(user_pin)s, %(pin_func)s);'
                            % {'user_pin': user_pin, 'pin_func': pin_func})
    if not log_enabled:
        # Comment out CONFIG_MCU_ENABLE_PRINTF
        autoconf_file = open(AUTOCONF_FILE, "r")
        content = autoconf_file.read()
        autoconf_file.close()
        t = content.replace("#define CONFIG_MCU_ENABLE_PRINTF", "// #define CONFIG_MCU_ENABLE_PRINTF")
        with open(AUTOCONF_FILE, "w") as autoconf_file:
            autoconf_file.write(t)
    setting_pinmux_str = '\n'.join(tmp_str_list)


def _get_setting_resume(pin_config_arry, reply_type):
    global setting_resume_str
    tmp_str_list = []
    for pin_config in pin_config_arry:
        if 'UART' == pin_config["function"][0:4] and pin_config["function"][6:] == "TX":
            tmp_str = _gen_uart_init_str(pin_config)
            if tmp_str != '':
                tmp_str_list.append(tmp_str)
        elif 'DEFAULT_P06' == pin_config["function"]:
            user_pin = 'USER_PIN_NUM_' + str(pin_config["pin"])
            tmp_str_list.extend(_gen_gpio_level_set_str(user_pin, 'UNI_GPIO_LEVEL_LOW'))
        elif 'DEFAULT_P01' == pin_config["function"]:
            user_pin = 'USER_PIN_NUM_' + str(pin_config["pin"])
            tmp_str_list.extend(_gen_gpio_level_set_str(user_pin, 'UNI_GPIO_LEVEL_HIGH'))
    setting_resume_str = '\n'.join(tmp_str_list)


def _get_setting_suspend(pin_config_arry, reply_type):
    global setting_suspend_str
    tmp_str_list = []
    for pin_config in pin_config_arry:
        if 'DEFAULT_P06' == pin_config["function"]:
            user_pin = 'USER_PIN_NUM_' + str(pin_config["pin"])
            tmp_str_list.extend(_gen_gpio_level_set_str(user_pin, 'UNI_GPIO_LEVEL_LOW'))
    setting_suspend_str = '\n'.join(tmp_str_list)


def _gen_gpio_level_set_str(user_pin, def_voltage, is_init=False):
    tmp_str_list = []
    if is_init:
        tmp_str_list.append('  user_gpio_set_direction({user_pin}, UNI_GPIO_DIRECTION_OUTPUT);'.format(
            user_pin=user_pin))
    tmp_str_list.append('  user_gpio_set_level({user_pin}, {def_voltage});'.format(
        user_pin=user_pin, def_voltage=def_voltage))
    return tmp_str_list


def _get_setting_init(pin_config_arry, reply_type):
    global setting_init_str
    tmp_str_list = []
    for pin_config in pin_config_arry:
        if pin_config["function"][0:4] == 'GPIO':
            if 'params' in pin_config and "def_voltage" in pin_config["params"]:
                user_pin = 'USER_PIN_NUM_' + str(pin_config["pin"])
                def_voltage = pin_config["params"]["def_voltage"]
                def_voltage_str = 'UNI_GPIO_LEVEL_LOW' if def_voltage == 'low' else 'UNI_GPIO_LEVEL_HIGH'
                GPIO_DEF_VOLTAGE_MAP[pin_config["function"]] = '0' if def_voltage == 'low' else '1'
                tmp_str_list.extend(_gen_gpio_level_set_str(user_pin, def_voltage_str, True))
        elif pin_config["function"][0:3] == 'PWM':
            if 'params' in pin_config:
                user_pin = 'USER_PIN_NUM_' + str(pin_config["pin"])
                frequency = pin_config["params"]["frequency"]
                inverse_str = '0'
                duty_str = '0'
                reverse_voltage = pin_config["params"]["reverse_voltage"]
                if reverse_voltage == "enable":
                    inverse_str = '1'
                elif reverse_voltage == "disable":
                    inverse_str = '0'
                else:
                    print("Unknown reverse_voltage: " + reverse_voltage)
                if 'duty' in pin_config["params"]:
                    duty_str = str(pin_config["params"]["duty"])
                tmp_str_list.append('  user_pwm_init({user_pin}, {freq}, {inverse}, {duty});'.format(
                    user_pin=user_pin, freq=frequency, inverse=inverse_str, duty=duty_str))
        elif pin_config["function"][0:4] == 'UART' and pin_config["function"][6:] == "TX":
            if 'params' in pin_config:
                tmp_str = _gen_uart_init_str(pin_config)
                if tmp_str != '':
                    tmp_str_list.append(tmp_str)
        elif 'DEFAULT_P05' == pin_config["function"]:
            user_pin = 'USER_PIN_NUM_' + str(pin_config["pin"])
            if reply_type != 'speaker':
                tmp_str_list.extend(_gen_gpio_level_set_str(user_pin, 'UNI_GPIO_LEVEL_HIGH', True))
        elif 'DEFAULT_P06' == pin_config["function"]:
            user_pin = 'USER_PIN_NUM_' + str(pin_config["pin"])
            tmp_str_list.extend(_gen_gpio_level_set_str(user_pin, 'UNI_GPIO_LEVEL_LOW', True))
        elif 'DEFAULT_P01' == pin_config["function"]:
            user_pin = 'USER_PIN_NUM_' + str(pin_config["pin"])
            tmp_str_list.extend(_gen_gpio_level_set_str(user_pin, 'UNI_GPIO_LEVEL_HIGH', True))
    setting_init_str = '\n'.join(tmp_str_list)


def _gen_pwm_ctrl_code(function, command, params):
    tmp_str_list = []
    real_pin_num = str(int(function[-2:]))
    duty_str = str(params["duty"])
    if command == "set":
        tmp_str_list.append('      user_pwm_enable({pin_num}, {duty});'.format(pin_num=real_pin_num, duty=duty_str))
    elif command == "increase":
        tmp_str_list.append('      user_pwm_duty_inc({pin_num}, {duty});'.format(pin_num=real_pin_num, duty=duty_str))
    elif command == "decrease":
        tmp_str_list.append('      user_pwm_duty_dec({pin_num}, {duty});'.format(pin_num=real_pin_num, duty=duty_str))
    return tmp_str_list


def _gen_gpio_ctrl_code(function, command, params):
    tmp_str_list = []
    real_pin_num = str(int(function[-2:]))
    if command == "set":
        level_str = '0'
        if params['voltage'] == 'high':
            level_str = '1'
        tmp_str_list.append('      user_gpio_set_level({pin_num}, {level});'.format(pin_num=real_pin_num,
                                                                                    level=level_str))
    elif command == "pulse":
        period_str = str(params['period'])
        times_str = str(params['times'])
        def_val_str = '0' if function not in GPIO_DEF_VOLTAGE_MAP else GPIO_DEF_VOLTAGE_MAP[function]
        tmp_str_list.append('      user_sw_timer_gpio_pulse({pin_num}, {period}, {times}, {def_val});'.format(
            pin_num=real_pin_num, period=period_str, times=times_str, def_val=def_val_str))
    return tmp_str_list


def _gen_uart_ctrl_code(function, command, params):
    tmp_str_list = []
    if command == 'send':
        uart_port = str(int(function[4]))
        send_buf_str = 'send_buf_' + function[0:5]
        data_split = params["data"].split()
        if len(data_split) > 16:
            print("UART send data must less than 16")
            return []
        tmp_str_list.append('      unsigned char {send_buf}[{data_size_str}] = {{{data_str}}};'.format(
            send_buf=send_buf_str, data_size_str=str(len(data_split)), data_str="0x" + ", 0x".join(data_split)))
        tmp_str_list.append('      uni_uart_write({uart_port}, {send_buf}, sizeof({send_buf}));'.format(
            send_buf=send_buf_str, uart_port=uart_port))
    return tmp_str_list

def _gen_volume_ctrl_code(command):
    tmp_str_list = []
    volume = ''
    if command == "volumeUpUni":
        # Volume plus 10
        tmp_str_list.append('      int vol = LvpMp3PlayerGetVolume();')
        volume = 'vol + 10'
        pass
    elif command == "volumeDownUni":
        # Volume minus 10, but should not be 0
        tmp_str_list.append('      int vol = LvpMp3PlayerGetVolume();')
        volume = 'vol > 10 ? (vol - 10):10'
        pass
    elif command == "volumeMaxUni":
        # volume set max value(100)
        volume = '100'
    elif command == "volumeMidUni":
        # volume set mid value(50)
        volume = '50'
    elif command == "volumeMinUni":
        # volume set min value(10)
        volume = '10'
    tmp_str_list.append('      LvpMp3PlayerSetVolume({vol});'.format(vol=volume))
    return tmp_str_list

def _get_setting_handle_kws_event(action_config_arry, reply_type):
    global setting_handle_kws_event_str,setting_volume_kws_event
    tmp_str_list = []
    is_first = True
    for action_config in action_config_arry:
        cmd = action_config['action']
        if is_first:
            tmp_str_list.append('    if (0 == strcmp(kws_result->action, "{cmd}")) {{'.format(cmd=cmd))
            is_first = False
        else:
            tmp_str_list.append('    }} else if (0 == strcmp(kws_result->action, "{cmd}")) {{'.format(cmd=cmd))
        if 'ctrl_items' in action_config:
            for ctrl_config in action_config['ctrl_items']:
                function = ctrl_config['function']
                command = ctrl_config['command']
                params = ctrl_config['params']
                if function[0:3] == "PWM":
                    tmp_str_list.extend(_gen_pwm_ctrl_code(function, command, params))
                elif function[0:4] == "UART":
                    tmp_str_list.extend(_gen_uart_ctrl_code(function, command, params))
                elif function[0:4] == "GPIO":
                    tmp_str_list.extend(_gen_gpio_ctrl_code(function, command, params))
                else:
                    print("Unknown function: " + function)

        # if it is a volume control event, add control
        if cmd in setting_volume_kws_event:
            tmp_str_list.extend(_gen_volume_ctrl_code(cmd))
            setting_volume_kws_event.remove(cmd)

    # check whether all volume control event is added
    for volume_kws_event in setting_volume_kws_event:
        if is_first:
            tmp_str_list.append('    if (0 == strcmp(kws_result->action, "{cmd}")) {{'.format(cmd=volume_kws_event))
            is_first = False
        else:
            tmp_str_list.append('    }} else if (0 == strcmp(kws_result->action, "{cmd}")) {{'.format(cmd=volume_kws_event))
        tmp_str_list.extend(_gen_volume_ctrl_code(volume_kws_event))
    setting_volume_kws_event = []

    tmp_str_list.append('    }')
    setting_handle_kws_event_str = '\n'.join(tmp_str_list)


# create gpio config demo code
def create_gpio_config_code(ctrl):
    global setting_pinmux_str, setting_resume_str, setting_suspend_str, setting_init_str, setting_handle_kws_event_str
    if ctrl == "auto":
        code_file = open(GPIO_CONFIG_CODE_FILE, "w")
        setting_pinmux_str = ''
        setting_resume_str = ''
        setting_suspend_str = ''
        setting_init_str = ''
        setting_handle_kws_event_str = ''
        with open(GPIO_CONFIG_FILE, "r") as json_file:
            config_json = json.load(json_file)
        try:
            pin_config_arry = config_json["local"]["pin_config"]
        except Exception as e:
            print('pin_config prase err:' + str(e))
            code_file.write(UNI_AUTO_CONTROL_C_TEMPLATE % {'setting_pinmux': setting_pinmux_str,
                                                           'setting_resume': setting_resume_str,
                                                           'setting_suspend': setting_suspend_str,
                                                           'setting_init': setting_init_str,
                                                           'setting_handle_kws_event': setting_handle_kws_event_str})
            code_file.close()
            return None
        try:
            action_config_arry = config_json["local"]["action_config"]
        except Exception as e:
            print('action_config prase err:' + str(e))
            action_config_arry = []
        try:
            reply_type = config_json["custom_voice"]["type"]
        except Exception as e:
            print('action_config prase err:' + str(e))
            reply_type = 'buzzer'
        _get_setting_pinmux(pin_config_arry, reply_type)
        _get_setting_resume(pin_config_arry, reply_type)
        _get_setting_suspend(pin_config_arry, reply_type)
        _get_setting_init(pin_config_arry, reply_type)
        if len(action_config_arry) > 0 or len(setting_volume_kws_event) > 0 :
            _get_setting_handle_kws_event(action_config_arry, reply_type)
        code_file.write(UNI_AUTO_CONTROL_C_TEMPLATE % {'setting_pinmux': setting_pinmux_str,
                                                       'setting_resume': setting_resume_str,
                                                       'setting_suspend': setting_suspend_str,
                                                       'setting_init': setting_init_str,
                                                       'setting_handle_kws_event': setting_handle_kws_event_str})
        code_file.close()
