/**************************************************************************
 * 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 : user_player.c
 * Author      : yuanshifeng@unisound.com
 * Date        : 2021.06.17
 *
 **************************************************************************/
#include <autoconf.h>
#include <lvp_board.h>
#include <driver/pwm_buzzer.h>
#include <stdlib.h>

#include "user_player.h"
#include "types.h"
#include "printf.h"
#include "string.h"
#include "fakelibc.h"
#include "board_misc_config.h"
#ifndef USE_MP3
#include "lvp_voice_player.h"
#else
#include "lvp_mp3_player.h"
#include "decoder/mp3_decoder.h"
#endif

#ifndef CONFIG_HW_EXT_FLASH
#ifndef USE_MP3
#include "pcm_bin.h"
#else
#include "mp3_bin.h"
#endif
#else
#include <driver/uni_flash.h>
#include <driver/uni_clock.h>
#endif

#include "uni_auto_control.h"
#include "uni_pcm_array.h"
#include "uni_cust_config.h"

#define LOG_TAG "[user_player]"

/* tone
#define L1 262         
#define L2 284
#define L3 311
#define L4 349
#define L5 392
#define L6 440
#define L7 494
#define Z1 523
#define Z2 587
#define Z3 659
#define Z4 698
#define Z5 784
#define Z6 880
#define Z7 987
#define H1 1046
#define H2 1174
#define H3 1318
#define H4 1396
#define H5 1567
#define H6 1760
#define H7 1975
*/

#define _BEAT_MS(x)  (40 * x)
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
static buzzer_tone_t tone_0_in[] = {
  {523, _BEAT_MS(4)},
  {0, _BEAT_MS(4)},
  {1046, _BEAT_MS(4)}
};

static buzzer_tone_t tone_1_action[] = {
  {659, _BEAT_MS(4)},
  {0, _BEAT_MS(4)},
  {659, _BEAT_MS(4)}, 
};

static buzzer_tone_t tone_2_out[] = {
  {1046, _BEAT_MS(4)},
  {0, _BEAT_MS(4)},
  {523, _BEAT_MS(4)}
};

static buzzer_tone_t tone_3_welcome[] = {
  {880, _BEAT_MS(4)},
  {987, _BEAT_MS(4)},
  {1046, _BEAT_MS(4)}, 
};

typedef struct {
  buzzer_tone_t *tone;
  unsigned int size;
}buzzer_tone_info_t;

buzzer_tone_info_t buzzer_tone_list[] = {
  {tone_0_in, ARRAY_SIZE(tone_0_in)},
  {tone_1_action, ARRAY_SIZE(tone_1_action)},
  {tone_2_out, ARRAY_SIZE(tone_2_out)},
  {tone_3_welcome, ARRAY_SIZE(tone_3_welcome)}
};

#define LIST_COUNT_MAX  30

/*[1,2,3]*/
static int value_list[LIST_COUNT_MAX] = {0};

int _parse_pcm_list(const char *str_list) {
  int count = 0, sum = 0;
  char last_ch = 0;
  const char *p = str_list;
  if (!str_list || str_list[0] != '[') {
    return -1;
  }
  while (*p) {
    if (*p == '[') {
      last_ch = *p;
      p ++;
      continue;
    }
    if (*p == ',' || *p == ']') {
      if ((last_ch == '[') || (last_ch == ',')) {
        /* invalid style */
        sum = 0;
        last_ch = *p;
        if (*p == ']') {
          /* finished if ']' */
          break;
        }
        continue;
      }
      value_list[count ++] = sum;
      sum = 0;
      last_ch = *p;
      if (count >= LIST_COUNT_MAX) break;
      if (*p == ']') {
          /* finished if ']' */
          break;
      }
    } else if (*p >= '0' && *p <= '9') {
      sum = sum * 10 + *p - '0';
      last_ch = *p;
    } else if (*p != ' ' && *p != '\t') {
      /* failed style if there is other char */
      return -1;
    }
    p ++;
  }
  if (count <= 0) {
    return -1;
  }
  return count;
}

int uni_get_number_pcm(const char *str_list, int num) {
  int count = 0;
  if (num < 0) {
    return -1;
  }
  count = _parse_pcm_list(str_list);
  if (count <= 0 || count <= num) {
    return -1;
  }
  return value_list[num];
}

int uni_get_random_pcm(const char *str_list) {
  int count = _parse_pcm_list(str_list);
  if (count <= 0) {
    return -1;
  }
  return value_list[rand() % count];
}

int uni_get_random_pcm_or_default(const char *pcm_list,
                                             const char *default_pcm_list) {
  int pcm = uni_get_random_pcm(pcm_list);
  if (pcm == -1) {
    return uni_get_random_pcm(default_pcm_list);
  } else {
    return pcm;
  }
}

#if UNI_REPLY_TYPE == UNI_REPLY_TYPE_SPEAKER
int PcmGetResourceInfo(int file_num, unsigned char **play_addr, uint32_t *play_len) {
#ifndef CONFIG_HW_EXT_FLASH
  const pcm_data_info_t *p_info = NULL;
  int i = 0;
  for (i = 0; i < sizeof(g_pcm_arry)/sizeof(pcm_data_info_t); i++) {
    if (file_num == g_pcm_arry[i].file_num) {
      p_info = &g_pcm_arry[i];
      break;
    }
  }
  if (!p_info) {
    printf(LOG_TAG"cannot found file %d\n", file_num);
    return -1;
  }
  *play_addr = (unsigned char *)&pcm_bin[p_info->offset];
  *play_len = p_info->length;
#else
  extern UNI_FLASH_DEV nor_spi_dev;
  pcm_data_info_t p_info = {0};
  uni_spi_flash_readdata(&nor_spi_dev, 16 * file_num, (unsigned char *)&p_info, sizeof(pcm_data_info_t));
  if (p_info.file_num > 255) {
    printf(LOG_TAG"cannot found file %d\n", file_num);
    return -1;
  }
  *play_addr = (unsigned char *)p_info.offset;
  *play_len = p_info.length;
#endif

  return -1;
}

static int _uni_play_filenum(int play_num) {
  unsigned char *play_addr = NULL;
  uint32_t play_len = 0;
  if (play_num < 0) {
    printf(LOG_TAG"play file %d invalid\n", play_num);
    return -1;
  }

  PcmGetResourceInfo(play_num, &play_addr, &play_len);
  printf("play %d file, addr=%x, len=%d\n", play_num, play_addr, play_len);
  ArptPrint("TTS START\n");
  #ifndef USE_MP3
  return LvpVoicePlayerPlayPcm(play_addr, play_len, 16000, 1, 0);
  #else
  return LvpMp3PlayerPlay(play_addr, play_len, 0);
  #endif
}
#endif

int user_player_play_filenum(int play_num) {
#if UNI_REPLY_TYPE == UNI_REPLY_TYPE_SPEAKER
  return _uni_play_filenum(play_num);
#elif UNI_REPLY_TYPE == UNI_REPLY_TYPE_BUZZER
  if (play_num > (ARRAY_SIZE(buzzer_tone_list) - 1)) {
    printf(LOG_TAG"cannot found buzzer %d\n", play_num);
    return -1;
  }
  buzzer_tone_info_t buzzer_info = buzzer_tone_list[play_num];
  ArptPrint("TTS START\n");
  return user_buzzer_play(buzzer_info.tone, buzzer_info.size);
#else
  return -1;
#endif
}

int user_player_play(const char *file) {
  if (file == NULL) {
    printf(LOG_TAG"play file invalid\n");
    return -1;
  }
  return user_player_play_filenum(uni_get_number_pcm(file, 0));
}

int user_player_reply_list_num(const char *file_list, int num) {
  int play_num = 0;
  if (NULL == file_list) {
    printf(LOG_TAG"%s:Invalid file name is NULL\n", __func__);
    return -1;
  }
  play_num = uni_get_number_pcm(file_list, num);
  if (play_num == -1) {
    printf(LOG_TAG"Cannot found %dst file in list %s\n", num, file_list);
    return -1;
  }
  return user_player_play_filenum(play_num);
}

int user_player_reply_list_random(const char *file_list) {
  int play_num = 0;
  if (NULL == file_list) {
    printf(LOG_TAG"%s:Invalid file name is NULL\n", __func__);
    return -1;
  }
  play_num = uni_get_random_pcm(file_list);
  if (play_num == -1) {
    printf(LOG_TAG"Cannot found any file in list %s\n", file_list);
    return -1;
  }
  return user_player_play_filenum(play_num);
}

int user_player_reply_list_in_order(const char *file_list) {
  int count = 0;
  int i;

  if (NULL == file_list) {
    printf(LOG_TAG"%s:Invalid file name is NULL\n", __func__);
    return -1;
  }

  count = _parse_pcm_list(file_list);
  if (count <= 0) {
    return -1;
  }
  for (i = 0; i < count; ++i) {
    user_player_play_filenum(value_list[i]);
  }
  return 0;
}

int user_player_stop(void) {
  //TBD
  return 0;
}

int user_player_set_volume_min(void) {
  //TBD
  return 0;
}

int user_player_set_volume_max(void) {
  //TBD
  return 0;
}

int user_player_set_volume_mid(void) {
  //TBD
  return 0;
}

int user_player_set_volume_up(void) {
  //TBD
  return 0;
}

int user_player_set_volume_down(void) {
  //TBD
  return 0;
}

int user_player_speaker_mute(void) {
  //TBD
  return 0;
}

int user_player_speaker_unmute(void) {
  //TBD
  return 0;
}

int user_player_shutup_mode(void) {
  //TBD
  return 0;
}

int user_player_shutup_exit(void) {
  //TBD
  return 0;
}

