/*
 *  Copyright 1994-2022 Olivier Girondel
 *
 *  This file is part of lebiniou.
 *
 *  lebiniou 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
 *  (at your option) any later version.
 *
 *  lebiniou 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 lebiniou. If not, see <http://www.gnu.org/licenses/>.
 */

#include "context.h"
#include "sequences.h"


char *playlist_filename = NULL;
extern uint8_t use_hard_timers;


static void
check_tracks(Context_t *ctx)
{
  json_t *tracks = json_object_get(ctx->playlist, "tracks");

  if (!json_array_size(tracks)) {
    fprintf(stderr, "[!] Playlist %s: no tracks\n", playlist_filename);
    exit(1);
  }

  VERBOSE(printf("[P] Validating playlist %s\n", playlist_filename));
  for (uint16_t i = 0; i < json_array_size(tracks); i++) {
    json_t *track = json_array_get(tracks, i);
    json_t *sequence = json_object_get(track, "sequence");
    json_t *duration = json_object_get(track, "duration");

    if (NULL == sequence) {
      fprintf(stderr, "[!] Track %d: no sequence\n", i + 1);
      exit(1);
    }
    if (NULL == Sequences_find_by_name(json_string_value(sequence))) {
      fprintf(stderr, "[!] Track %d: sequence '%s' not found\n", i + 1, json_string_value(sequence));
      exit(1);
    }

    if (NULL == duration) {
      fprintf(stderr, "[!] Track %d: no duration\n", i + 1);
      exit(1);
    }
    if (!json_is_number(duration) || (json_number_value(duration) <= 0)) {
      fprintf(stderr, "[!] Track %d: duration must be an integer or a float and > 0\n", i + 1);
      exit(1);
    }
  }
  ctx->playlist_shuffler = Shuffler_new(json_array_size(tracks));
}


static void
init_shuffler(Context_t *ctx)
{
  json_t *mode = json_object_get(ctx->playlist, "mode");

  if (NULL == mode) {
    Shuffler_set_mode(ctx->playlist_shuffler, BS_CYCLE);
  } else {
    Shuffler_set_mode(ctx->playlist_shuffler, Shuffler_parse_mode(json_string_value(mode)));
  }
}


static void
parse_options(Context_t *ctx)
{
  const json_t *p = ctx->playlist;
  json_t *soft_timers = json_object_get(p, "soft timers");
  json_t *end = json_object_get(p, "end");
  json_t *seed = json_object_get(p, "seed");

  if (NULL != soft_timers) {
    if (json_is_boolean(soft_timers)) {
      use_hard_timers = !json_boolean_value(soft_timers);
    } else {
      fprintf(stderr, "[!] Playlist: 'soft timers' must be a boolean\n");
      exit(1);
    }
  } else {
    use_hard_timers = 0;
  }

  if (NULL != end) {
    if (!json_is_string(end)) {
      fprintf(stderr, "[!] Playlist: 'end' must be a string\n");
      exit(1);
    }
  }

  if (NULL != seed) {
    if (json_is_integer(seed)) {
      VERBOSE(printf("[i] Using seed: %lld\n", json_integer_value(seed)));
      b_rand_set_seed(json_integer_value(seed));
    } else {
      fprintf(stderr, "[!] Playlist: 'seed' must be an integer\n");
      exit(1);
    }
  }
}

void
Context_init_playlist(Context_t *ctx)
{
  json_error_t error;

  ctx->playlist = json_load_file(playlist_filename, 0, &error);
  if (NULL == ctx->playlist) {
    fprintf(stderr, "[!] Error loading playlist %s: (line %d, column %d): %s\n", playlist_filename, error.line, error.column, error.text);
    exit(1);
  }
  if (json_is_object(ctx->playlist)) {
    check_tracks(ctx);
    init_shuffler(ctx);
    ctx->track_timer = Timer_new("track_timer");
    parse_options(ctx);
  } else {
    fprintf(stderr, "[!] Playlist %s is not an object\n", playlist_filename);
    exit(1);
  }
}


void
Context_next_track(Context_t *ctx)
{
  if (Shuffler_is_done(ctx->playlist_shuffler)) {
    json_t *end = json_object_get(ctx->playlist, "end");

    VERBOSE(printf("[P] End of playlist reached\n"));
    if (NULL != end) {
      assert(json_is_string(end));
      const char *action = json_string_value(end);
      VERBOSE(printf("[P] End of playlist reached: action= '%s'\n", action));
      if (is_equal(action, "exit")) {
        ctx->running = 0;
      }
    }
    json_decref(ctx->playlist);
    ctx->playlist = NULL;
    Alarm_init(ctx->a_random);
  } else {
    const uint16_t t = Shuffler_get(ctx->playlist_shuffler);
    json_t *tracks = json_object_get(ctx->playlist, "tracks");
    json_t *track = json_array_get(tracks, t);
    const char *sequence = json_string_value(json_object_get(track, "sequence"));
    const json_t *duration = json_object_get(track, "duration");

    assert(NULL != track);
    ctx->sm->curseq = Sequences_find_by_name(sequence);
    assert(NULL != ctx->sm->curseq); // should be validated
    Sequence_copy(ctx, ctx->sm->curseq->data, ctx->sm->next);
    Context_set(ctx);

    VERBOSE(printf("[P] Track %03d: %s (%f seconds)\n", t + 1, sequence, json_number_value(duration)));
    Timer_start(ctx->track_timer);
    ctx->track_duration = json_number_value(duration);
  }
}
