#include <argagg/argagg.hpp>

#include <cstdlib>
#include <fstream>
#include <iostream>
#include <sstream>

static std::ofstream g_dev_null;

int main(
  int argc,
  const char** argv)
{
  using argagg::parser_results;
  using argagg::parser;
  using std::cerr;
  using std::cout;
  using std::endl;
  using std::ofstream;
  using std::ostream;
  using std::ostringstream;
  using std::string;

  // Use an initializer list to define the argument parser. The first brace
  // starts the initializer list, the second brace starts the initializer list
  // for the definitions vector in the argagg::parser struct.
  parser argparser {{

      // Each entry here is an initializer list for an `argagg::definition`
      // struct.
      {
        // Name of the option. This is the key used to retrieve the flag parser
        // results.
        "help",

        // The strings ("flags") that must be matched to activate this option.
        {"-h", "--help"},

        // The help string that is streamed out when the argagg::parser object
        // itself is streamed out.
        "displays help information",

        // Number of arguments needed by this option. Should be 0 or 1.
        0},
      {
        "verbose", {"-v", "--verbose"},
        "increases verbosity", 0},
      {
        "lorem-ipsum", {"--lorem-ipsum"},
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
        "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim "
        "ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
        "aliquip ex ea commodo consequat. Duis aute irure dolor in "
        "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
        "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in "
        "culpa qui officia deserunt mollit anim id est laborum.", 0},
      {
        "sep", {"-s", "--sep"},
        "separator (default ',')", 1},
      {
        "output", {"-o", "--output"},
        "output filename (stdout if not specified)", 1},
    }};

  // Define our usage text.
  ostringstream usage;
  usage
    << "Joins all positional arguments together with a separator" << endl
    << endl
    << "Usage: " << argv[0] << " [options] ARG [ARG...]" << endl
    << endl;

  // Use our argument parser to... parse the command line arguments. If there
  // are any problems then just spit out the usage and help text and exit.
  argagg::parser_results args;
  try {
    args = argparser.parse(argc, argv);
  } catch (const std::exception& e) {
    argagg::fmt_ostream help(cerr);
    help << usage.str() << argparser << endl
         << "Encountered exception while parsing arguments: " << e.what()
         << endl;
    return EXIT_FAILURE;
  }

  // If the help flag was specified then spit out the usage and help text and
  // exit.
  if (args["help"]) {
    argagg::fmt_ostream help(cerr);
    help << usage.str() << argparser;
    return EXIT_SUCCESS;
  }

  // Respect verbosity. Okay, the logging here is a little ludicrous. The point
  // I want to show here is that you can quickly get the number of times an
  // option shows up.
  int verbose_level = args["verbose"].count();

  // Set up our verbose log output stream selector that selects stderr if the
  // requested log level is lower than or equal to the currently set verbose
  // level.
  g_dev_null.open("/dev/null"); // portable? eh... simple? yes!
  auto vlog = [&](int level) -> ostream& {
      return verbose_level >= level ? cerr : g_dev_null;
    };

  vlog(1) << "verbose log level: " << verbose_level << endl;

  // Use comma as the separator unless one was specified.
  auto sep = args["sep"].as<string>(",");
  vlog(1) << "set separator to '" << sep << "'" << endl;

  // Determine output stream.
  ofstream output_file;
  ostream* output = &std::cout;
  if (args["output"]) {
    string filename = args["output"];
    output_file.open(filename);
    output = &output_file;
    vlog(1) << "outputting to file at '" << filename << "'" << endl;
  } else {
    vlog(1) << "outputting to stdout" << endl;
  }

  // Join the arguments.
  if (args.count() < 1) {
    vlog(0) << usage.str() << argparser << endl
            << "Not enough arguments" << endl;
    return EXIT_FAILURE;
  }
  for (auto& arg : args.pos) {
    vlog(2) << "writing argument" << endl;
    vlog(4) << "argument is '" << arg << "'" << endl;
    *output << arg;
    if (arg != args.pos.back()) {
      vlog(3) << "writing separator" << endl;
      *output << sep;
    }
  }
  vlog(4) << "writing endl" << endl;
  *output << endl;

  vlog(4) << "everything a-okay" << endl;
  return EXIT_SUCCESS;  
}
