# include <getopt.h>	// getopt
# include <sys/types.h>	// u_int16_t, u_int32_t
# include <stdint.h>    // uintptr_t, ISO C99 types
# include <string.h>    // strncpy
# include "qfile.hh"
# include "qwavheader.hh"
# include "qexception.hh"
# include "endian.hh"

/* caved in and used C's printf in a few places that don't seem to
 * be expressible with ios
 */
#include <cstdio>
#include <iostream>

#ifdef NLS
  # include <locale.h>
  # include <libintl.h>
  # define _(s) gettext (s)
#else
  # define _(s) (s)
#endif

// see qwavheader.hh for more info about the wav header struct
struct header {

  char riff[4];
  u_int32_t rifflength;
  char wave[4];
     
  char fmt_[4];
  u_int32_t fmtlength;
  u_int16_t format;
  u_int16_t channels;
  u_int32_t samplerate;
  u_int32_t bytespersec;
  u_int16_t bytespersample;
  u_int16_t bitspersample;
    
  char data[4];
  u_int32_t datalength;
} __attribute__((packed));

const unsigned int HEADERSIZE = sizeof(struct header);


void usage () {

  fprintf(stderr,_(" %s: dump (and fix) wav header\n"),APPNAME);
  fprintf(stderr,_(" syntax: %s [option]... file...\n"),APPNAME);
  cerr << _("   -F, --fix: correct header. use with care\n");
  cerr << _("   -h, --help: show this help and exit\n");
  cerr << _("   -q, --quiet: no output messages\n");
  cerr << _("   -V, --version: show version and exit\n");
}


int main (int argc, char **argv) {

  int option;
  bool quiet=false, fix=false;

  static struct option long_options[] = {
    {"fix",no_argument,0,'F'},
    {"help",no_argument,0,'h'},
    {"quiet",no_argument,0,'q'},
    {"version",no_argument,0,'V'},
    {0,0,0,0}
  };

#ifdef NLS
  setlocale(LC_ALL, "");
  bindtextdomain(PACKAGE, LOCALEDIR);
  textdomain(PACKAGE);
#endif

 // un altre dia, per defecte llegir *.wav ...
  if (argc==1) {
    usage();
    return 1;
  }

 // supress getopt error message
  opterr = 0;	

  while ((option = getopt_long(argc, argv, "FhqV",long_options,0)) != EOF) 
    switch (option) {
      case 'F':
        fix=true;
      break;
      case 'h':
        usage();
        return 0;
      break;
      case 'q':
        quiet=true;
      break;
      case 'V':
        cerr << APPNAME << " - " << _("version") << ' ' << VERSION
	     << _("build") << ' ' << __DATE__ << '\n';
        return 0;
      break;      
      case '?':
      default: 
        cerr << APPNAME << ": " << _("option") << " '" << argv[optind-1]
	     << "' " << _("is not recognized or bad used") << '\n';
        usage();
        return 1;
    } 

  if (argc<=optind) {
   // haurķem de fer *.wav ...
    cerr << APPNAME << _(": no input file(s)") << endl;
    usage();
    return 1;
  }

  u_int32_t openmode = fix?qfile::READWRITE:qfile::READ;

  while (argv[optind]) {

    try {

      qfile f(argv[optind],openmode);
      
      if (f.getSize()<HEADERSIZE) {
        cerr << APPNAME << ": '" << f.getName()
	     << _("' has not enough room for a wav header\n");
        optind++;
        continue;
      }

	  if ((uintptr_t)f.getMap() % sizeof(u_int32_t) != 0) {
		// should never happen with a mapping at offset 0, but if it does,
		// we need to catch it rather than tossing subtle memory misread
		// bugs.  32-bit alignment is adequate since no integer in a WAV header
		// is longer
		cerr << APPNAME << _(": mapping of header in '") << f.getName()
			 << _("' is not 32-bit aligned\n");
		optind++;
		continue;
	  }

      struct header *header = (struct header*) f.getMap();

      cout << f.getName() << " (" << f.getSize() << " bytes):" << endl;

     // the file should have an integer number of samples...

      printf("\triff: '%.4s'\n",header->riff);
      if (strncmp(header->riff,"RIFF",4)) {
        if (!quiet) 
          fprintf(stderr,_("\t\triff field should be 'RIFF'\n"));
        if (fix) {
          strncpy(header->riff,"RIFF",4);
          if (!quiet)
            cerr << _("\t\tfixed\n");
        }
      }
      
      printf(_("\triff length: %d\n"),letohl(header->rifflength));
      if (letohl(header->rifflength)!=f.getSize()-8) {
        if (!quiet)
          fprintf(stderr,_("\t\triff length field should be %d\n"),f.getSize()-8);
        if (fix) {
          header->rifflength = htolel(f.getSize()-8);
          if (!quiet)
            cerr << _("\t\tfixed\n");
        }
      }

      printf("\twave: '%.4s'\n",header->wave);
      if (strncmp(header->wave,"WAVE",4)) {
        if (!quiet) 
          fprintf(stderr,_("\t\twave field should be 'WAVE'\n"));
        if (fix) {
          strncpy(header->wave,"WAVE",4);
          if (!quiet)
            cerr << _("\t\tfixed\n");
        }
      }
      
      printf("\tfmt: '%.4s'\n",header->fmt_);
      if (strncmp(header->fmt_,"fmt ",4)) {
        if (!quiet) 
          fprintf(stderr,_("\t\tfmt  field should be 'fmt '\n"));
        if (fix) {
          strncpy(header->fmt_,"fmt ",4);
          if (!quiet)
            cerr << _("\t\tfixed\n");
        }
      }

      printf(_("\tfmt length: %d\n"),letohl(header->fmtlength));
      if (letohl(header->fmtlength)!=16) {
        if (!quiet)
          fprintf(stderr,_("\t\tfmt length field should be %d\n"),16);
        if (fix) {
          header->fmtlength = htolel(16);
          if (!quiet)
            cerr << _("\t\tfixed\n");
        }
      }

      printf("\tformat: %d\n",letohs(header->format));
      if (letohs(header->format)!=1) {
        if (!quiet)
          fprintf(stderr,_("\t\tformat field should 1 (pcm tag)\n"));
        if (fix) {
          header->format = htoles(1);
          if (!quiet)
            cerr << _("\t\tfixed\n");
        }
      }

      printf(_("\tchannels: %d\n"),letohs(header->channels));
      if (letohs(header->channels)!=2 &&
		  letohs(header->channels)!=1) {
        if (!quiet)
          fprintf(stderr,_("\t\tchannels field should be 1 (mono) or 2 (stereo)\n"));
        if (fix) {
          cerr << _("\t\tdon't know which value must be set...\n") << endl;
          if (!quiet)
            cerr << _("\t\tfixed\n");
        }
      }

      printf(_("\tsample rate: %d\n"),letohl(header->samplerate));
      if (letohl(header->samplerate)>48000 ||
		  letohl(header->samplerate)<8000) {
        if (!quiet)
          fprintf(stderr,_("\t\tsample rate field should be between 8000 and 48000\n"));
        if (fix)
          cerr << _("\t\tdon't know which value must be set...\n") << endl;
      }

      printf(_("\tbytes/second: %d\n"),letohl(header->bytespersec));


      printf(_("\tbytes/sample: %d\n"),letohs(header->bytespersample));
      if (letohs(header->bytespersample)!=1 &&
          letohs(header->bytespersample)!=2 &&
          letohs(header->bytespersample)!=4) {
        if (!quiet)
          fprintf(stderr,_("\t\t bytes/sample field should be 1, 2 or 4\n"));
        if (fix)
          cerr << _("\t\tdon't know which value must be set...\n") << endl;
      }

      printf(_("\tbits/sample: %d\n"),letohs(header->bitspersample));

      printf("\tdata: '%.4s'\n",header->data);
      if (strncmp(header->data,"data",4)) {
        if (!quiet) 
          fprintf(stderr,_("\t\tdata field should be 'data'\n"));
        if (fix) {
          strncpy(header->data,"data",4);
          if (!quiet)
            cerr << _("\t\tfixed\n");
        }
      }

      printf(_("\tdata length: %d\n"),letohl(header->datalength));
      if (letohl(header->datalength)!=f.getSize()-44) {
        if (!quiet)
          fprintf(stderr,_("\t\tdata length field should be %d\n"),f.getSize()-44);
        if (fix) {
          header->datalength = htolel(f.getSize()-44);
          if (!quiet)
            cerr << _("\t\tfixed\n");
        }
      }
    }
    catch (qexception e) {
      cerr << argv[optind] << ": " << e << endl;
    }

    optind++;
  }
}
