#!/usr/bin/env rdmd
module sdp.sisu_document_parser;
import
  sdp.conf.compile_time_info,
  sdp.meta.metadoc;
import
  std.getopt,
  std.file,
  std.path,
  std.process;
import
  sdp.meta,
  sdp.meta.metadoc_summary,
  sdp.meta.metadoc_from_src,
  sdp.meta.conf_make_meta_structs,
  sdp.meta.conf_make_meta_toml,
  sdp.meta.conf_make_meta_json,
  sdp.meta.defaults,
  sdp.meta.doc_debugs,
  sdp.meta.read_config_files,
  sdp.meta.read_source_files,
  sdp.meta.rgx,
  sdp.output.hub,
  sdp.output.paths_source;
mixin(import("version.txt"));
mixin CompileTimeInfo;
/++
name        "sdp"
description "A SiSU inspired document parser writen in D."
homepage    "http://sisudoc.org"
+/
void main(string[] args) {
  mixin SiSUrgxInit;
  mixin contentJSONtoSiSUstruct;
  mixin SiSUbiblio;
  mixin SiSUrgxInitFlags;
  mixin outputHub;
  string flag_action;
  string arg_unrecognized;
  enum dAM { abstraction, matters }
  static auto rgx = Rgx();
  scope(success) {
    debug(checkdoc) {
      writefln(
        "~ run complete, ok ~ (sdp-%s.%s.%s, %s v%s, %s %s)",
        ver.major, ver.minor, ver.patch,
        __VENDOR__, __VERSION__,
        bits, os,
      );
    }
  }
  scope(failure) {
    debug(checkdoc) {
      stderr.writefln(
        "run failure",
      );
    }
  }
  bool[string] opts = [
    "assertions"         : false,
    "concordance"        : false,
    "debug"              : false,
    "digest"             : false,
    "docbook"            : false,
    "epub"               : false,
    "html"               : false,
    "html-seg"           : false,
    "html-scroll"        : false,
    "manifest"           : false,
    "ocn"                : true,
    "odt"                : false,
    "pdf"                : false,
    "postgresql"         : false,
    "qrcode"             : false,
    "sisupod"            : false,
    "source"             : false,
    "sqlite-discrete"    : false,
    "sqlite-update"      : false,
    "sqlite-create"      : false,
    "sqlite-drop"        : false,
    "text"               : false,
    "verbose"            : false,
    "xhtml"              : false,
    "xml-dom"            : false,
    "xml-sax"            : false,
    "section_toc"        : true,
    "section_body"       : true,
    "section_endnotes"   : true,
    "section_glossary"   : true,
    "section_biblio"     : true,
    "section_bookindex"  : true,
    "section_blurb"      : true,
    "backmatter"         : true,
    "skip-output"        : false,
  ];
  string[string] settings = [
    "output-dir"         : "",
    "site-config-dir"    : "",
    "lang"               : "all",
  ];
  auto helpInfo = getopt(args,
    std.getopt.config.passThrough,
    "assert",             "--assert set optional assertions on",                        &opts["assertions"],
    "concordance",        "--concordance file for document",                            &opts["concordance"],
    "debug",              "--debug",                                                    &opts["debug"],
    "digest",             "--digest hash digest for each object",                       &opts["digest"],
    "docbook",            "--docbook process docbook output",                           &opts["docbook"],
    "epub",               "--epub process epub output",                                 &opts["epub"],
    "html",               "--html process html output",                                 &opts["html"],
    "html-seg",           "--html-seg process html output",                             &opts["html-seg"],
    "html-scroll",        "--html-seg process html output",                             &opts["html-scroll"],
    "manifest",           "--manifest process manifest output",                         &opts["manifest"],
    "ocn",                "--ocn object cite numbers (default)",                        &opts["ocn"],
    "odf",                "--odf process odf:odt output",                               &opts["odt"],
    "odt",                "--odt process odf:odt output",                               &opts["odt"],
    "pdf",                "--pdf process pdf output",                                   &opts["pdf"],
    "pg",                 "--pg process postgresql output",                             &opts["postgresql"],
    "postgresql",         "--postgresql process postgresql output",                     &opts["postgresql"],
    "qrcode",             "--qrcode with document metadata",                            &opts["qrcode"],
    "sisupod",            "--sisupod sisupod source content bundled",                   &opts["sisupod"],
    "source",             "--source markup source text content",                        &opts["source"],
    "sqlite-discrete",    "--sqlite process discrete sqlite output",                    &opts["sqlite-discrete"],
    "sqlite-create",      "--sqlite-create create db, create tables",                   &opts["sqlite-create"],
    "sqlite-drop",        "--sqlite-drop drop tables & db",                             &opts["sqlite-drop"],
    "sqlite-update",      "--sqlite process sqlite output",                             &opts["sqlite-update"],
    "text",               "--text process text output",                                 &opts["text"],
    "txt",                "--txt process text output",                                  &opts["text"],
    "verbose|v",          "--verbose output to terminal",                               &opts["verbose"],
    "xhtml",              "--xhtml process xhtml output",                               &opts["xhtml"],
    "xml-dom",            "--xml-dom process xml dom output",                           &opts["xml-dom"],
    "xml-sax",            "--xml-sax process xml sax output",                           &opts["xml-sax"],
    "section-toc",        "--section-toc process table of contents (default)",          &opts["section_toc"],
    "section-body",       "--section-body process document body (default)",             &opts["section_body"],
    "section-endnotes",   "--section-endnotes process document endnotes (default)",     &opts["section_endnotes"],
    "section-glossary",   "--section-glossary process document glossary (default)",     &opts["section_glossary"],
    "section-biblio",     "--section-biblio process document biblio (default)",         &opts["section_biblio"],
    "section-bookindex",  "--section-bookindex process document bookindex (default)",   &opts["section_bookindex"],
    "section-blurb",      "--section-blurb process document blurb (default)",           &opts["section_blurb"],
    "backmatter",         "--section-backmatter process document backmatter (default)", &opts["backmatter"],
    "skip-output",        "--skip-output",                                              &opts["skip-output"],
    "output-dir",         "--output-dir=[dir path]",                                    &settings["output-dir"],
    "site-config-dir",    "--site-config-dir=[dir path]",                               &settings["site-config-dir"],
    "lang",               "--lang=[lang code e.g. =en or =en,es]",                      &settings["lang"],
  );
  if (helpInfo.helpWanted) {
    defaultGetoptPrinter("Some information about the program.", helpInfo.options);
  }
  struct OptActions {
    auto assertions() {
      return opts["assertions"];
    }
    auto concordance() {
      return opts["concordance"];
    }
    auto debug_do() {
      return opts["debug"];
    }
    auto digest() {
      return opts["digest"];
    }
    auto docbook() {
      return opts["docbook"];
    }
    auto epub() {
      return opts["epub"];
    }
    auto html() {
      return opts["html"];
    }
    auto html_seg() {
      return opts["html-seg"];
    }
    auto html_scroll() {
      return opts["html-scroll"];
    }
    auto manifest() {
      return opts["manifest"];
    }
    auto ocn() {
      return opts["ocn"];
    }
    auto odt() {
      return opts["odt"];
    }
    auto pdf() {
      return opts["pdf"];
    }
    auto postgresql() {
      return opts["postgresql"];
    }
    auto qrcode() {
      return opts["qrcode"];
    }
    auto sisupod() {
      return opts["sisupod"];
    }
    auto source() {
      return opts["source"];
    }
    auto sqlite_discrete() {
      return opts["sqlite-discrete"];
    }
    auto sqlite_update() {
      return opts["sqlite-update"];
    }
    auto sqlite_create() {
      return opts["sqlite-create"];
    }
    auto sqlite_drop() {
      return opts["sqlite-drop"];
    }
    auto text() {
      return opts["text"];
    }
    auto verbose() {
      return opts["verbose"];
    }
    auto xhtml() {
      return opts["xhtml"];
    }
    auto xml_dom() {
      return opts["xml-dom"];
    }
    auto xml_sax() {
      return opts["xml-sax"];
    }
    auto section_toc() {
      return opts["section_toc"];
    }
    auto section_body() {
      return opts["section_body"];
    }
    auto section_endnotes() {
      return opts["section_endnotes"];
    }
    auto section_glossary() {
      return opts["section_glossary"];
    }
    auto section_biblio() {
      return opts["section_biblio"];
    }
    auto section_bookindex() {
      return opts["section_bookindex"];
    }
    auto section_blurb() {
      return opts["section_blurb"];
    }
    auto backmatter() {
      return opts["backmatter"];
    }
    auto skip_output() {
      return opts["skip-output"];
    }
    auto languages_set() {
      return settings["lang"].split(",");
    }
    auto output_dir_set() {
      return settings["output-dir"];
    }
  }
  auto _opt_action = OptActions();
  auto _env = [
    "pwd" : environment["PWD"],
    "home" : environment["HOME"],
  ];
  auto _manifest_start = PodManifest!()("");
  auto _manifest_matter = PathMatters!()(_opt_action, _env, "");
  auto _manifests = [ _manifest_matter ];
  foreach(arg; args[1..$]) {
    _manifest_start = PodManifest!()(arg);
    if (arg.match(rgx.flag_action)) {
      flag_action ~= " " ~ arg;   // flags not taken by getopt
    } else if (
      !(arg.match(rgx.src_pth_sst_or_ssm))
      && _manifest_start.pod_manifest_file_with_path
    ) {
      string contents_location_raw_;
      string contents_location_;
      string sisudoc_txt_ = _manifest_start.pod_manifest_file_with_path;
      enforce(
        exists(sisudoc_txt_)!=0,
        "file not found: «" ~
        sisudoc_txt_ ~ "»"
      );
      if (exists(sisudoc_txt_)) {
        try {
          if (exists(sisudoc_txt_)) {
            contents_location_raw_ = sisudoc_txt_.readText;
          }
        }
        catch (ErrnoException ex) {
        }
        catch (FileException ex) {
          // Handle errors
        }
        if (contents_location_raw_.match(rgx.pod_content_location)) { // (file name followed by language codes \n)+
          foreach (m; contents_location_raw_.matchAll(rgx.pod_content_location)) {
            foreach (n; m.captures[2].matchAll(rgx.language_codes)) {
              contents_location_ ~= "media/text/" ~ n.captures[1].to!string ~ "/" ~ m.captures[1].to!string ~ "\n";
            }
          }
        } else {
          contents_location_ = contents_location_raw_;
        }
      } else {
        writeln("manifest not found: ", sisudoc_txt_);
      }
      auto contents_locations_arr
        = (cast(char[]) contents_location_).split;
      auto tmp_dir_ = (sisudoc_txt_).dirName.array;
      foreach (contents_location; contents_locations_arr) {
        assert(contents_location.match(rgx.src_pth_sst_or_ssm),
          "not a recognised file: «" ~
          contents_location ~ "»"
        );
        auto contents_location_pth_ = (contents_location).to!string;
        auto lang_rgx_ = regex(r"/(" ~ _opt_action.languages_set.join("|") ~ ")/");
        if (_opt_action.languages_set[0] == "all"
          || (contents_location_pth_).match(lang_rgx_)
        ) {
          auto _fns = (((tmp_dir_).chainPath(contents_location_pth_)).array).to!string;
          _manifest_matter = PathMatters!()(_opt_action, _env, arg, _fns, contents_locations_arr);
          _manifests ~= _manifest_matter; // TODO how to capture?
        }
      }
    } else if (arg.match(rgx.src_pth_sst_or_ssm)) {
      if (exists(arg)==0) {
        writeln("ERROR >> Processing Skipped! File not found: ", arg);
      } else {
        _manifests ~= PathMatters!()(_opt_action, _env, arg, arg); // gather input markup source file names for processing
      }
    } else if (arg.match(rgx.src_pth_zip)) {
      // fns_src ~= arg;             // gather input markup source file names for processing
    } else {                      // anything remaining, unused
      arg_unrecognized ~= " " ~ arg;
    }
  }
  if (!(_opt_action.skip_output)) {
    debug(steps) {
      writeln("step0 commence → (without processing files)");
    }
    outputHubOp!()(_env, _opt_action);
    debug(steps) {
      writeln("- step0 complete");
    }
  }
  if (_manifests.length > 1) { // _manifests[0] initialized dummy element
    foreach(manifest; _manifests[1..$]) {
      if (!empty(manifest.src.filename)) {
        scope(success) {
          debug(checkdoc) {
            writefln(
              "%s\n%s",
              "~ document complete, ok ~",
              "------------------------------------------------------------------",
            );
          }
        }
        scope(failure) {
          debug(checkdoc) {
            stderr.writefln(
              "~ document run failure ~ (%s  v%s)\n\t%s",
              __VENDOR__, __VERSION__,
              manifest.src.filename
            );
          }
        }
        enforce(
          manifest.src.filename.match(rgx.src_pth_types),
          "not a sisu markup filename: «" ~
          manifest.src.filename ~ "»"
        );
        debug(steps) {
          writeln("--->\nstepX commence → (document abstraction)");
        }
        auto t = SiSUabstraction!()(_env, _opt_action, manifest);
        static assert(!isTypeTuple!(t));
        static assert(t.length==2);
        auto doc_abstraction = t[dAM.abstraction];
        auto doc_matters = t[dAM.matters];
        debug(steps) {
          writeln("- stepX complete");
        }
        /+ ↓ debugs +/
        if (doc_matters.opt.action.verbose) {
          SiSUabstractionSummary!()(doc_abstraction, doc_matters);
        }
        /+ ↓ debugs +/
        if ((doc_matters.opt.action.debug_do)
        || (doc_matters.opt.action.verbose)
        ) {
          SiSUdebugs!()(doc_abstraction, doc_matters);
        }
        /+ ↓ output hub +/
        if (!(doc_matters.opt.action.skip_output)) {
          debug(steps) {
            writeln("step5 commence → (process outputs)");
          }
          outputHub!()(doc_abstraction, doc_matters);
          debug(steps) {
            writeln("- step5 complete");
          }
        }
        scope(exit) {
          debug(checkdoc) {
            writefln(
              "processed file: %s",
              manifest.src.filename
            );
          }
          destroy(manifest);
        }
      } else {
        /+ no recognized filename provided +/
        writeln("no recognized filename");
        break; // terminate, stop
      }
    }
  }
}
unittest {
}