/*
 * Copyright © 2010 Mozilla Foundation
 *
 * This program is made available under an ISC-style license.  See the
 * accompanying file LICENSE for details.
 */
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "nestegg/nestegg.h"

#undef DEBUG
#define SEEK_TEST

static int
stdio_read(void * p, size_t length, void * fp)
{
  size_t r;

  r = fread(p, length, 1, fp);
  if (r == 0 && feof(fp))
    return 0;
  return r == 0 ? -1 : 1;
}

static int
stdio_seek(int64_t offset, int whence, void * fp)
{
  return fseek(fp, offset, whence);
}

static int64_t
stdio_tell(void * fp)
{
  return ftell(fp);
}

static void
log_callback(nestegg * ctx, unsigned int severity, char const * fmt, ...)
{
  va_list ap;
  char const * sev = NULL;

#ifndef DEBUG
  if (severity < NESTEGG_LOG_WARNING)
    return;
#endif

  switch (severity) {
  case NESTEGG_LOG_DEBUG:
    sev = "debug:   ";
    break;
  case NESTEGG_LOG_WARNING:
    sev = "warning: ";
    break;
  case NESTEGG_LOG_CRITICAL:
    sev = "critical:";
    break;
  default:
    sev = "unknown: ";
  }

  fprintf(stderr, "%p %s ", (void *) ctx, sev);

  va_start(ap, fmt);
  vfprintf(stderr, fmt, ap);
  va_end(ap);

  fprintf(stderr, "\n");
}

int
main(int argc, char * argv[])
{
  FILE * fp;
  int r, type;
  nestegg * ctx;
  nestegg_audio_params aparams;
  nestegg_packet * pkt;
  nestegg_video_params vparams;
  size_t length, size;
  uint64_t duration, tstamp, pkt_tstamp;
  unsigned char * codec_data, * ptr;
  unsigned int cnt, i, j, track, tracks, pkt_cnt, pkt_track;
  unsigned int data_items = 0;
  nestegg_io io = {
    stdio_read,
    stdio_seek,
    stdio_tell,
    NULL
  };

  if (argc != 2)
    return EXIT_FAILURE;

  fp = fopen(argv[1], "rb");
  if (!fp)
    return EXIT_FAILURE;

  io.userdata = fp;

  ctx = NULL;
  r = nestegg_init(&ctx, io, log_callback);
  if (r != 0)
    return EXIT_FAILURE;

  nestegg_track_count(ctx, &tracks);
  nestegg_duration(ctx, &duration);
#ifdef DEBUG
  fprintf(stderr, "media has %u tracks and duration %fs\n", tracks, duration / 1e9);
#endif

  for (i = 0; i < tracks; ++i) {
    type = nestegg_track_type(ctx, i);
#ifdef DEBUG
    fprintf(stderr, "track %u: type: %d codec: %d", i,
            type, nestegg_track_codec_id(ctx, i));
#endif
    nestegg_track_codec_data_count(ctx, i, &data_items);
    for (j = 0; j < data_items; ++j) {
      nestegg_track_codec_data(ctx, i, j, &codec_data, &length);
#ifdef DEBUG
      fprintf(stderr, " (%p, %u)", codec_data, (unsigned int) length);
#endif
    }
    if (type == NESTEGG_TRACK_VIDEO) {
      nestegg_track_video_params(ctx, i, &vparams);
#ifdef DEBUG
      fprintf(stderr, " video: %ux%u (d: %ux%u %ux%ux%ux%u)",
              vparams.width, vparams.height,
              vparams.display_width, vparams.display_height,
              vparams.crop_top, vparams.crop_left, vparams.crop_bottom, vparams.crop_right);
#endif
    } else if (type == NESTEGG_TRACK_AUDIO) {
      nestegg_track_audio_params(ctx, i, &aparams);
#ifdef DEBUG
      fprintf(stderr, " audio: %.2fhz %u bit %u channels",
              aparams.rate, aparams.depth, aparams.channels);
#endif
    }
#ifdef DEBUG
    fprintf(stderr, "\n");
#endif
  }

#ifdef SEEK_TEST
#ifdef DEBUG
  fprintf(stderr, "seek to middle\n");
#endif
  r = nestegg_track_seek(ctx, 0, duration / 2);
  if (r == 0) {
#ifdef DEBUG
    fprintf(stderr, "middle ");
#endif
    r = nestegg_read_packet(ctx, &pkt);
    if (r == 1) {
      nestegg_packet_track(pkt, &track);
      nestegg_packet_count(pkt, &cnt);
      nestegg_packet_tstamp(pkt, &tstamp);
#ifdef DEBUG
      fprintf(stderr, "* t %u pts %f frames %u\n", track, tstamp / 1e9, cnt);
#endif
      nestegg_free_packet(pkt);
    } else {
#ifdef DEBUG
      fprintf(stderr, "middle seek failed\n");
#endif
    }
  }

#ifdef DEBUG
  fprintf(stderr, "seek to ~end\n");
#endif
  r = nestegg_track_seek(ctx, 0, duration - (duration / 10));
  if (r == 0) {
#ifdef DEBUG
    fprintf(stderr, "end ");
#endif
    r = nestegg_read_packet(ctx, &pkt);
    if (r == 1) {
      nestegg_packet_track(pkt, &track);
      nestegg_packet_count(pkt, &cnt);
      nestegg_packet_tstamp(pkt, &tstamp);
#ifdef DEBUG
      fprintf(stderr, "* t %u pts %f frames %u\n", track, tstamp / 1e9, cnt);
#endif
      nestegg_free_packet(pkt);
    } else {
#ifdef DEBUG
      fprintf(stderr, "end seek failed\n");
#endif
    }
  }

#ifdef DEBUG
  fprintf(stderr, "seek to ~start\n");
#endif
  r = nestegg_track_seek(ctx, 0, duration / 10);
  if (r == 0) {
#ifdef DEBUG
    fprintf(stderr, "start ");
#endif
    r = nestegg_read_packet(ctx, &pkt);
    if (r == 1) {
      nestegg_packet_track(pkt, &track);
      nestegg_packet_count(pkt, &cnt);
      nestegg_packet_tstamp(pkt, &tstamp);
#ifdef DEBUG
      fprintf(stderr, "* t %u pts %f frames %u\n", track, tstamp / 1e9, cnt);
#endif
      nestegg_free_packet(pkt);
    } else {
#ifdef DEBUG
      fprintf(stderr, "start seek failed\n");
#endif
    }
  }
#endif

  while (nestegg_read_packet(ctx, &pkt) > 0) {
    nestegg_packet_track(pkt, &pkt_track);
    nestegg_packet_count(pkt, &pkt_cnt);
    nestegg_packet_tstamp(pkt, &pkt_tstamp);

#ifdef DEBUG
    fprintf(stderr, "t %u pts %f frames %u: ", pkt_track, pkt_tstamp / 1e9, pkt_cnt);
#endif

    for (i = 0; i < pkt_cnt; ++i) {
      nestegg_packet_data(pkt, i, &ptr, &size);
#ifdef DEBUG
      fprintf(stderr, "%u ", (unsigned int) size);
#endif
    }
#ifdef DEBUG
    fprintf(stderr, "\n");
#endif

    nestegg_free_packet(pkt);
  }

  nestegg_destroy(ctx);
  fclose(fp);

  return EXIT_SUCCESS;
}