#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#ifndef WIN32
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <cmml.h>
#define BUFSIZE 100000
static int verbose;
static void
PrintUsage(char *prog) {
  fprintf(stderr, "Usage: %s [options] filename\n", prog);
  fprintf(stderr, "Validate a CMML file.\n\n");
  fprintf(stderr, "Possible options:\n");
#ifdef HAVE_GETOPT_LONG
  fprintf(stderr, "  -i clip_id, --id clip_id\n");
  fprintf(stderr, "                 Start parsing from the named clip.\n");
  fprintf(stderr, "  -s seconds, --sec seconds\n");
  fprintf(stderr, "                 Start parsing from the given seconds offset\n");
  fprintf(stderr, "  -u utc,     --utc utc\n");
  fprintf(stderr, "                 Start parsing from the given utc time\n");
  fprintf(stderr, "  -b, --verbose  Output parsed file to stdout\n");
  fprintf(stderr, "  -h, --help     Display this help information\n");
  fprintf(stderr, "  -v, --version  Display version information\n");
#else
  fprintf(stderr, "  -i clip_id     Start parsing from the named clip\n");
  fprintf(stderr, "  -s seconds     Start parsing from the given seconds offset\n");
  fprintf(stderr, "  -u utc         Start parsing from the given utc time\n");
  fprintf(stderr, "  -b             Output parsed file to stdout\n");
  fprintf(stderr, "  -h             Display this help information\n");
  fprintf(stderr, "  -v             Display version information\n");
#endif
  fprintf(stderr, "\nPlease report bugs to <libcmml-devel@cmis.csiro.au>.\n");
  exit(1);
}
static int
read_stream (CMML * cmml, const CMML_Stream * stream, void * user_data) {
  char buf[BUFSIZE];
  CMML_Error * err;
  if ((err = cmml_get_last_error(cmml)) != NULL) {
    cmml_error_snprint(buf, BUFSIZE, err, cmml);
    fprintf(stderr, "cmml-validate: Parsing stream tag %s\n", buf);
    fprintf(stderr, "cmml-validate: Non-recoverable error\n");
    return -1;
  } else {
    
    if (verbose) {
      cmml_stream_pretty_snprint (buf, BUFSIZE, (CMML_Stream *) stream);
      fprintf(stdout, "%s\n", buf);
    }
  }  return 0;
}
static int
read_head (CMML * cmml, const CMML_Head * head, void * user_data) {
  char buf[BUFSIZE];
  CMML_Error * err;
  if ((err = cmml_get_last_error(cmml)) != NULL) {
    cmml_error_snprint(buf, BUFSIZE, err, cmml);
    fprintf(stderr, "cmml-validate: Parsing head tag %s\n", buf);
    fprintf(stderr, "cmml-validate: Non-recoverable error\n");
    return -1;
  } else {
    if (verbose) {
      cmml_head_pretty_snprint (buf, BUFSIZE, (CMML_Head *) head);
      fprintf(stdout, "%s\n", buf);
    }
  }
  return 0;
}
static int
read_clip (CMML * cmml, const CMML_Clip * clip, void * user_data) {
  char buf[BUFSIZE];
  CMML_Error * err;
  if ((err = cmml_get_last_error(cmml)) != NULL) {
    cmml_error_snprint(buf, BUFSIZE, err, cmml);
    fprintf(stderr, "cmml-validate: Parsing clip %s\n", buf);
    fprintf(stderr, "cmml-validate: Skipping clip\n\n");
    return -1;
  } else {
    if (verbose) {
      cmml_clip_pretty_snprint (buf, BUFSIZE, (CMML_Clip *) clip);
      fprintf(stdout, "%s\n", buf);
    }
  }
  return 0;
}
int main(int argc, char *argv[])
{
  char *pathfile = NULL;
  int i;
  char buf[BUFSIZE];
  CMML * doc;
  CMML_Error * err;
  CMML_Preamble * pre;
  long n = 0;
  char * clip_id = NULL;
  double secs = -1.0;
  char * utc = NULL;
  int sloppy = 0;
  verbose = 0;
  while (1) {
    char * optstring = "hvbyi:s:u:";
#ifdef HAVE_GETOPT_LONG
    static struct option long_options[] = {
      {"help",no_argument,0,'h'},
      {"version",no_argument,0, 'v'},
      {"verbose",no_argument,0,'b'},
      {"sloppy",no_argument,0,'y'},
      {"id",required_argument,0,'i'},
      {"sec",required_argument,0,'s'},
      {"utc",required_argument,0,'u'},
      {0,0,0,0}
    };
    i = getopt_long(argc, argv, optstring, long_options, NULL);
#else
    i = getopt(argc, argv, optstring);
#endif
    if (i == -1) break;
    if (i == ':') PrintUsage(argv[0]);
    switch (i) {
    case 'h': 
      PrintUsage(argv[0]);
      break;
    case 'v': 
      fprintf(stdout, "cmml-validate version " VERSION "\n");
      fprintf(stdout, "# cmml-validate, Copyright (C) 2003 CSIRO Australia www.csiro.au ; www.annodex.net\n");
      break;
    case 'i': 
      clip_id = optarg;
      break;
    case 's': 
      if (!isalpha(optarg[0])) {
        secs = atof(optarg);
      }
      break;
    case 'u': 
      utc = optarg;
      break;
    case 'y': 
      sloppy = 1;
      break;
    case 'b': 
      verbose = 1;
      break;
    default:
      break;
    }
  }
  
  if (optind > argc) {
    PrintUsage(argv[0]);
  }
  
  if (optind == argc) {
    pathfile = "-";
  } else {
    pathfile = argv[optind++];
  }
  
  errno=0;
  if (strcmp (pathfile, "-") == 0) {
    doc = cmml_new (stdin);
  } else {
    doc = cmml_open (pathfile);
  }
  if (doc == NULL) {
    if (errno == 0) {
      fprintf(stderr, "%s: %s: CMML error opening file\n", argv[0], pathfile);
    } else {
      fprintf(stderr, "%s: %s: %s\n", argv[0], pathfile, strerror(errno));
    }
    PrintUsage(argv[0]);
  }
  
  if (sloppy) {
    cmml_set_sloppy(doc, 1);
  }
  
  if (verbose) {
    pre = cmml_get_preamble(doc);
    cmml_preamble_snprint(buf, BUFSIZE, pre);
    fprintf(stdout, "%s\n", buf);
  }
  
  if (clip_id != NULL) {
    
    cmml_set_read_callbacks (doc, read_stream, read_head, NULL, NULL);
    cmml_skip_to_id (doc, clip_id);
  }
  
  if (secs > 0 || utc != NULL) {
    
    cmml_set_read_callbacks (doc, read_stream, read_head, NULL, NULL);
    if (secs > 0) {
      cmml_skip_to_secs (doc, secs);
    } else { 
      cmml_skip_to_utc (doc, utc);
    }
  }
  
  cmml_set_read_callbacks (doc, read_stream, read_head, read_clip, NULL);
  
  while ((n = cmml_read (doc, BUFSIZE)) > 0) {
    
    if ((err = cmml_get_last_error(doc)) != NULL && err->type != CMML_EOF) {
      char *filename;
      filename = (strrchr(pathfile, '/') == NULL ? pathfile
                  : strrchr(pathfile, '/')+1);
      cmml_error_snprint(buf, BUFSIZE, err, doc);
      fprintf (stderr, "%s:%s\n", filename, buf);
      goto cleanup;
    }
  }
  err = cmml_get_last_error(doc);
  if (err!=NULL && err->type != CMML_EOF) {
      char *filename;
      filename = (strrchr(pathfile, '/') == NULL ? pathfile
                  : strrchr(pathfile, '/')+1);
      cmml_error_snprint(buf, BUFSIZE, err, doc);
      fprintf (stderr, "%s:%s\n", filename, buf);
      goto cleanup;
  }
  
  if (verbose) {
    fprintf(stdout, "</cmml>\n");
  }
  
 cleanup:
  
  cmml_close(doc);
  return 0;
}