#include "youku.h"
#include "../logger.h"
#include "../process_url.h"
#include "../utils/ffmpeg.h"
#include "../utils/utils.h"
#include <cjson/cJSON.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static callback_struct_t callback_struct;

static int generate_api(Youku_options *youku_options) {
  char params[UCHAR_MAX];
  snprintf(params, sizeof(params),
           "vid=%s&ccode=%s&client_ip=192.168.1.1&client_ts=%d&utid=%s",
           youku_options->vid, youku_options->ccode,
           (int)(youku_options->client_ts / 1000), youku_options->utid);
  youku_options->api = malloc(strlen(YOUKU_API) + strlen(params) + 1);
  strcpy(youku_options->api, YOUKU_API);
  strcat(youku_options->api, params);
  return 0;
}

static int parse_jsondata(cJSON *json, Youku_data *youku_data) {
  cJSON *data_obj = cJSON_GetObjectItem(json, "data");
  if (!data_obj) {
    LOG("cJSON", "Parse API data failed.\n");
    return 1;
  }

  cJSON *video_obj = cJSON_GetObjectItem(data_obj, "video");
  cJSON *streams_obj = cJSON_GetObjectItem(data_obj, "stream");
  if (!video_obj || !streams_obj) {
    LOG("cJSON", "Parse API data child object failed.\n");
    return 1;
  }
  cJSON *title_obj = cJSON_GetObjectItem(video_obj, "title");
  if (!title_obj) {
    LOG("cJSON", "Parse API data.video.title failed.\n");
    return 1;
  }
  youku_data->title = title_obj->valuestring;
  DEBUG_PRINT("data.video.title: %s\n", youku_data->title);

  cJSON *e;
  int max_size = 0;
  cJSON_ArrayForEach(e, streams_obj) {
    cJSON *size_obj = cJSON_GetObjectItem(e, "size");
    cJSON *width_obj = cJSON_GetObjectItem(e, "width");
    cJSON *height_obj = cJSON_GetObjectItem(e, "height");
    cJSON *m3u8_url_obj = cJSON_GetObjectItem(e, "m3u8_url");
    if (!size_obj || !width_obj || !height_obj || !m3u8_url_obj) {
      LOG("cJSON", "Parse API data.stream child object failed.\n");
      return 1;
    }
    if (size_obj->valueint > max_size) {
      max_size = size_obj->valueint;
      youku_data->width = width_obj->valueint;
      youku_data->height = height_obj->valueint;
      youku_data->m3u8_url = m3u8_url_obj->valuestring;
    }
  }
  DEBUG_PRINT("width: %d, height: %d\n", youku_data->width, youku_data->height);

  return 0;
}

static void youku_options_cleanup(Youku_options *youku_options) {
  FREE_AND_NULLIFY(youku_options->vid);
  FREE_AND_NULLIFY(youku_options->utid);
  FREE_AND_NULLIFY(youku_options->api);
  cJSON_Delete(youku_options->json);
}

static int youku_convert(void *v) {
  callback_struct_t *callback_struct = (callback_struct_t *)v;
  const char *m3u8fn = get_str_element(callback_struct, 0);
  const char *filename = get_str_element(callback_struct, 1);
  DEBUG_PRINT("m3u8fn: %s\n", m3u8fn);
  DEBUG_PRINT("filename: %s\n", filename);

  int r = remux(m3u8fn, filename);
  LOG("Youku", "All done!\n");
  free_str_array(callback_struct);
  return r;
}

void youku_extract(Options *options) {
  Youku_options youku_options = {0};
  str_array_t results = create_str_array(0);
  {
    const char *patterns_str[2] = {"id_(.+?)\\.html", "id_(.+)"};
    const str_array_t patterns = {(char **)patterns_str, 2};
    int r = regex_match(options->URL, patterns, &results);
    if (r) {
      LOG("Youku", "Parse URL failed.\n");
      goto end;
    }
    // for (unsigned short i = 0; i < results.n; i++) {
    //   DEBUG_PRINT("%s\n", results.str[i]);
    // }
    if (!results.str[0]) {
      LOG("Youku", "Get vid failed.\n");
      goto end;
    }
    youku_options.vid = malloc(strlen(results.str[0]) + 1);
    strcpy(youku_options.vid, results.str[0]);
    DEBUG_PRINT("vid: %s\n", youku_options.vid);
  }

  {
    resize_str_array(&results, 0);
    char *cookie = NULL;
    set_referer("https://v.youku.com");
    get_info("http://log.mmstat.com/eg.js", NULL, NULL, &cookie);
    if (!cookie) {
      goto end;
    }
    const char *patterns_str[1] = {"cna=(.+?);"};
    const str_array_t patterns = {(char **)patterns_str, 1};
    int r = regex_match(cookie, patterns, &results);
    if (r) {
      LOG("Youku", "Parse cookie failed.\n");
      goto end;
    }
    // for (unsigned short i = 0; i < results.n; i++) {
    //   DEBUG_PRINT("%s\n", results.str[i]);
    // }
    if (!results.str[0]) {
      LOG("Youku", "Get utid failed.\n");
      goto end;
    }
    youku_options.utid = malloc(strlen(results.str[0]) + 1);
    strcpy(youku_options.utid, results.str[0]);
    DEBUG_PRINT("utid: %s\n", youku_options.utid);
  }

  youku_options.ccode = "0524";
  youku_options.client_ip = "192.168.1.1";
  youku_options.client_ts = time(NULL);
  if (generate_api(&youku_options)) {
    LOG("Youku", "Generate API failed.\n");
    goto end;
  }
  DEBUG_PRINT("Generated API: %s\n", youku_options.api);
  char *resp = NULL;
  if (get(youku_options.api, &resp)) {
    LOG("Youku", "Get API data failed.\n");
    FREE_AND_NULLIFY(resp);
    goto end;
  }
  // DEBUG_PRINT("resp: %s\n", resp);

  youku_options.json = cJSON_Parse(resp);
  FREE_AND_NULLIFY(resp);
  Youku_data youku_data = {0};
  if (parse_jsondata(youku_options.json, &youku_data)) {
    LOG("Youku", "Parse jsondata failed.\n");
    goto end;
  }

  callback_struct = create_str_array(2);
  char *m3u8fn = malloc(strlen(youku_data.title) + 18);
  sprintf(m3u8fn, "%s-[%dx%d].m3u8", youku_data.title, youku_data.width,
          youku_data.height);
  set_str_element(&callback_struct, 0, m3u8fn);
  char *filename = malloc(strlen(youku_data.title) + 17);
  sprintf(filename, "%s-[%dx%d].mp4", youku_data.title, youku_data.width,
          youku_data.height);
  set_str_element(&callback_struct, 1, filename);
  add_url(youku_data.m3u8_url, NULL, m3u8fn, youku_convert, &callback_struct);
  FREE_AND_NULLIFY(m3u8fn);
  FREE_AND_NULLIFY(filename);

end:
  free_str_array(&results);
  youku_options_cleanup(&youku_options);
}