/* Copyright 2008 David Moews (dmoews@fastmail.fm)

This program 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. 
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <iostream>

#include <unistd.h>     // getopt(), opt...

#include "io.h"
#include "Iso9660.h"
#include "Iso9660Check.h"

void usage(const char *nm)
{
  std::cerr <<
  "Usage: " << nm << " [-x] [-w] [-v] [-V] [-c [-f volume-descr-number] [-p sorttype] [-s sorttype]]\n\n\n"
  << "-x: check size vs. volume size (don't bother to read past header)\n"
  << "-w: print volume descriptor information (don't bother to read past header)\n"
  << "-v: print volume descriptor information\n"
  << "-V: print volume descriptor information and directory hierarchies\n"
  << "-c: report on consistency of filesystem\n"
  << "-p sorttype: specify sorting order for primary volume descriptors\n"
  << "-s sorttype: specify sorting order for secondary volume descriptors\n"
  << "-f volume-descr-number: specify which volume descriptor to check (0, 1, ...);\n"
  << "                        default: check all.\n"
  << "\n\n`sorttype' may be `none' (to not check ordering),\n"
  << "           `lex' (for lexicographic ordering),\n"
  << "           or `iso9660' (for ordering as specified by ISO 9660.)\n";
}

// Returns succ/failure
bool get_sorting_type(const char *c, SortingType *p_t)
{
  if (strcmp(c, "none") == 0)
     *p_t = Sort_None;
  else if (strcmp(c, "lex") == 0)
     *p_t = Sort_Lex;
  else if (strcmp(c, "iso9660") == 0)
     *p_t = Sort_Iso9660;
  else
     return false;

  return true;
}

int main(int argc, char **argv)
{
  Volume vol;
  int descr_number = -1;
  bool size_chk_simple = false;
  bool print_hdr_simple = false;
  bool print_hdr = false;
  bool print_hierarchy = false;
  bool report = false;
  SortingType p = Sort_Iso9660, s = Sort_Lex;

  for (;;)
  {
    int c = getopt(argc, argv, "xwvVcp:s:f:");

    if (c == -1)
       break;

    switch (c)
    {
      case 'x':
           size_chk_simple = true;
           break;

      case 'w':
           print_hdr_simple = true;
           break;

      case 'v':
           print_hdr = true;
           break;

      case 'V':
           print_hierarchy = true;
           break;

      case 'c':
           report = true;
           break;

      case 'f':
           descr_number = atoi(optarg);
           break;
    
      case 'p':
           if (!get_sorting_type(optarg, &p))
           {
             usage(argv[0]);
             return 1;
           }
           break;

      case 's':
           if (!get_sorting_type(optarg, &s))
           {
             usage(argv[0]);
             return 1;
           }
           break;

      case '?':
           usage(argv[0]);
           return 1;
           break;

      default:
           assert(0);
           break;
    }
  }
  
  if (optind != argc)
  {
    usage(argv[0]);
    return 1;
  }
 
  if (size_chk_simple)
  {
    bool b = read_iso9660_header(std::cin, 2048u, &vol);
    if (!b)
    {
      std::cerr << "Can't read ISO 9660 header.\n";
      return 2;
    }

    std::cout << "File size check:\n";
  
    // check size
    u32 sz;
    if (!file_size(std::cin, vol.volume_info.logical_block_size, &sz))
       std::cout << "can't get file size\n";
    else if (sz != vol.volume_info.volume_space_size)
    {
       std::cout 
             << "file size doesn't match volume space size (multivolume?)\n";
       std::cout << "size: " << sz << " volume space size: "
	         << vol.volume_info.volume_space_size << '\n';
    }
    else
       std::cout << "OK\n";
    return 0;
  }

  if (print_hdr_simple)
  {
    bool b = read_iso9660_header(std::cin, 2048u, &vol);
    if (!b)
    {
      std::cerr << "Can't read ISO 9660 header.\n";
      return 2;
    }

    print_iso9660(std::cout, vol, false);
    return 0;
  }

  bool b = read_iso9660(std::cin, 2048u, &vol);
  if (!b)
  {
    std::cerr << "Can't read ISO 9660 filesystem.\n";
    return 2;
  }

  if (print_hdr || print_hierarchy)
    print_iso9660(std::cout, vol, print_hierarchy);

  if (report)
  {
    CheckingOptions opts;
    CheckingOutput out;

    opts.do_volume_descriptor = descr_number;
    opts.primary_sorting_type = p;
    opts.secondary_sorting_type = s;

    check_volume(vol, opts, &out);
    std::cout << "Options:\n\n";
    print_options_report(std::cout, opts);
    std::cout << "\n\nResults:\n\n";
    print_output_report(std::cout, out);

    std::cout << "File size check:\n";
  
    // check size
    u32 sz;
    if (!file_size(std::cin, vol.volume_info.logical_block_size, &sz))
       std::cout << "can't get file size\n";
    else if (sz != vol.volume_info.volume_space_size)
    {
       std::cout 
             << "file size doesn't match volume space size (multivolume?)\n";
       std::cout << "size: " << sz << " volume space size: "
	         << vol.volume_info.volume_space_size << '\n';
    }
    else
       std::cout << "OK\n";
  }

  return 0; 
}
