#!/usr/bin/env rdmd
/+
  sdp
+/
/+ sdp: sisu document parser, see http://sisudoc.org +/
import
  compile_time_info,
  ao_abstract_doc_source,
  ao_conf_make_meta,
  ao_conf_make_meta_native,
  ao_conf_make_meta_sdlang,
  ao_defaults,
  ao_output_debugs,
  ao_read_config_files,
  ao_read_source_files,
  ao_rgx,
  output_hub,
  output_epub,
  output_html,
  output_xhtmls,
  source_sisupod;
/+ sdlang http://sdlang.org +/
import sdlang;                            // sdlang.d
/+ std +/
private import
  std.algorithm,
  std.array,
  std.container,
  std.exception,
  std.getopt,
  std.json,
  std.process,
  std.stdio,
  std.file,
  std.path,
  std.range,
  std.regex,
  std.string,
  std.traits,
  std.typecons,
  std.utf,
  std.conv : to;

mixin(import("version.txt"));
mixin CompileTimeInfo;
mixin RgxInit;
/++ A SiSU document parser writen in D. +/
void main(string[] args) {
  mixin SiSUregisters;
  mixin SiSUheaderExtractHub;
  mixin SiSUheaderExtractSDLang;
  mixin SiSUnode;
  mixin SiSUbiblio;
  mixin SiSUrgxInitFlags;
  mixin SiSUconfigSDLangHub;
  mixin SiSUmarkupRaw;
  mixin SiSUdocAbstraction;
  mixin SiSUoutputDebugs;
  mixin SiSUoutputHub;
  auto raw = MarkupRaw();
  auto head = HeaderDocMetadataAndMake();
  auto abs = Abstraction();
  auto dbg = SDPoutputDebugs();
  auto output = SDPoutput();
  /+
  struct DocumentParts {
    string[string][] contents;
    string[string][string] meta_aa;
    string[string][string] make_aa;
    string[][string][string] bookindex_unordered_hashes;
    JSONValue[] biblio;
  }
  +/
  string[] fns_src;
  string flag_action;
  string arg_unrecognized;
  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] _opt_action_bool = [
    "assertions"         : false,
    "concordance"        : 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"             : 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,
  ];
  auto helpInfo = getopt(args,
    std.getopt.config.passThrough,
    "assert",             "--assert set optional assertions on",                        &_opt_action_bool["assertions"],
    "concordance",        "--concordance file for document",                            &_opt_action_bool["concordance"],
    "digest",             "--digest hash digest for each object",                       &_opt_action_bool["digest"],
    "docbook",            "--docbook process docbook output",                           &_opt_action_bool["docbook"],
    "epub",               "--epub process epub output",                                 &_opt_action_bool["epub"],
    "html",               "--html process html output",                                 &_opt_action_bool["html"],
    "html_seg",           "--html-seg process html output",                             &_opt_action_bool["html_seg"],
    "html_scroll",        "--html-seg process html output",                             &_opt_action_bool["html_scroll"],
    "manifest",           "--manifest process manifest output",                         &_opt_action_bool["manifest"],
    "ocn",                "--ocn object cite numbers (default)",                        &_opt_action_bool["ocn"],
    "odf",                "--odf process odf:odt output",                               &_opt_action_bool["odt"],
    "odt",                "--odt process odf:odt output",                               &_opt_action_bool["odt"],
    "pdf",                "--pdf process pdf output",                                   &_opt_action_bool["pdf"],
    "pg",                 "--pg process postgresql output",                             &_opt_action_bool["postgresql"],
    "postgresql",         "--postgresql process postgresql output",                     &_opt_action_bool["postgresql"],
    "qrcode",             "--qrcode with document metadata",                            &_opt_action_bool["qrcode"],
    "sisupod",            "--sisupod sisupod source content bundled",                   &_opt_action_bool["sisupod"],
    "source",             "--source markup source text content",                        &_opt_action_bool["source"],
    "sqlite",             "--sqlite process sqlite output",                             &_opt_action_bool["sqlite"],
    "text",               "--text process text output",                                 &_opt_action_bool["text"],
    "txt",                "--txt process text output",                                  &_opt_action_bool["text"],
    "verbose|v",          "--verbose output to terminal",                               &_opt_action_bool["verbose"],
    "xhtml",              "--xhtml process xhtml output",                               &_opt_action_bool["xhtml"],
    "xml-dom",            "--xml-dom process xml dom output",                           &_opt_action_bool["xml_dom"],
    "xml-sax",            "--xml-sax process xml sax output",                           &_opt_action_bool["xml_sax"],
    "section-toc",        "--section-toc process table of contents (default)",          &_opt_action_bool["section_toc"],
    "section-body",       "--section-body process document body (default)",             &_opt_action_bool["section_body"],
    "section-endnotes",   "--section-endnotes process document endnotes (default)",     &_opt_action_bool["section_endnotes"],
    "section-glossary",   "--section-glossary process document glossary (default)",     &_opt_action_bool["section_glossary"],
    "section-biblio",     "--section-biblio process document biblio (default)",         &_opt_action_bool["section_biblio"],
    "section-bookindex",  "--section-bookindex process document bookindex (default)",   &_opt_action_bool["section_bookindex"],
    "section-blurb",      "--section-blurb process document blurb (default)",           &_opt_action_bool["section_blurb"],
    "backmatter",         "--section-backmatter process document backmatter (default)", &_opt_action_bool["backmatter"],
    "skip_output",        "--skip-output",                                              &_opt_action_bool["skip_output"],
  );
  if (helpInfo.helpWanted) {
    defaultGetoptPrinter("Some information about the program.", helpInfo.options);
  }
  foreach(arg; args) {
    if (match(arg, rgx.flag_action)) {
      flag_action ~= " " ~ arg;   // flags not taken by getopt
    } else if (match(arg, rgx.src_pth)) {
      fns_src ~= arg;             // gather input markup source file names for processing
    } else {                      // anything remaining, unused
      arg_unrecognized ~= " " ~ arg;
    }
  }
  auto conf = ConfigHub();
  auto sdl_root_configuration = conf.configSDLang("conf.sdl");
  auto sdl_root_doc_make = conf.configSDLang("sisu_document_make");
  auto confsdl = HeaderExtractSDL();
  auto conf_settings_aa = confsdl.configSettingsSDLangToAAmake(sdl_root_configuration);
  auto conf_doc_make_aa = confsdl.documentMakeSDLangToAAmake(sdl_root_doc_make);
  foreach(fn_src; fns_src) {
    if (!empty(fn_src)) {
      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__,
            fn_src
          );
        }
      }
      enforce(
        match(fn_src, rgx.src_pth),
        "not a sisu markup filename"
      );
      /+ ↓ read file +/
      auto read_in_file_string = raw.sourceContent(fn_src);
      /+ ↓ file tuple of header and content +/
      auto header_and_body_tuple = raw.sourceContentSplitIntoHeaderAndBody(read_in_file_string, fn_src);
      auto header = header_and_body_tuple[0];
      auto content_body = header_and_body_tuple[1];
      debug(header_and_body) {
        writeln(header);
        writeln(header_and_body_tuple.length);
        writeln(content_body[0]);
      }
      /+ ↓ split header into make and meta +/
      auto header_make_and_meta_tuple = head.headerContentAA(header, conf_doc_make_aa);
      static assert(!isTypeTuple!(header_make_and_meta_tuple));
      string[string][string] _dochead_make = header_make_and_meta_tuple[0];
      string[string][string] _dochead_meta = header_make_and_meta_tuple[1];
      /+ ↓ document abstraction: process document, return abstraction as tuple +/
      auto t = abs.abstract_doc_source(content_body, _dochead_make, _dochead_meta, _opt_action_bool);
      static assert(!isTypeTuple!(t));
      auto doc_abstraction = t[0]; // head ~ toc ~ contents ~ endnotes_seg ~ glossary ~ bibliography ~ bookindex ~blurb;
      string[] doc_html_segnames = t[1];
      string[][string] document_section_keys_sequenced = [
        "seg":    ["head", "toc_seg", "body",],
        "scroll": ["head", "toc_scroll", "body",]
      ];
      if (doc_abstraction["endnotes"].length > 1) {
        document_section_keys_sequenced["seg"]    ~= "endnotes";
        document_section_keys_sequenced["scroll"] ~= "endnotes";
      }
      if (doc_abstraction["glossary"].length > 1) {
        document_section_keys_sequenced["seg"]    ~= "glossary";
        document_section_keys_sequenced["scroll"] ~= "glossary";
      }
      if (doc_abstraction["bibliography"].length > 1) {
        document_section_keys_sequenced["seg"]    ~= "bibliography";
        document_section_keys_sequenced["scroll"] ~= "bibliography";
      }
      if (doc_abstraction["bookindex_seg"].length > 1) {
        document_section_keys_sequenced["seg"]    ~= "bookindex_seg";
      }
      if (doc_abstraction["bookindex_scroll"].length > 1) {
        document_section_keys_sequenced["scroll"] ~= "bookindex_scroll";
      }
      if (doc_abstraction["blurb"].length > 1) {
        document_section_keys_sequenced["seg"]    ~= "blurb";
        document_section_keys_sequenced["scroll"] ~= "blurb";
      }
      if ((_opt_action_bool["html"])
      || (_opt_action_bool["html_scroll"])
      || (_opt_action_bool["html_seg"])
      || (_opt_action_bool["epub"])) {
        document_section_keys_sequenced["seg"]    ~= "tail";
        document_section_keys_sequenced["scroll"] ~= "tail";
      }
      struct DocumentMatters {
        string[] keys_seq_seg() {
          string[] _k = document_section_keys_sequenced["seg"];
          return _k;
        }
        string[] keys_seq_scroll() {
          string[] _k = document_section_keys_sequenced["scroll"];
          return _k;
        }
        string[] segnames() {
          string[] _k = doc_html_segnames;
          return _k;
        }
        auto dochead_make() {
          string[string][string] _k = _dochead_make;
          return _k;
        }
        auto dochead_meta() {
          string[string][string] _k = _dochead_meta;
          return _k;
        }
        auto source_filename() {
          string _k = fn_src;
          return _k;
        }
        auto opt_action_bool() {
          bool[string] _k = _opt_action_bool;
          return _k;
        }
      }
      auto doc_matters = DocumentMatters();
      /+ ↓ debugs +/
      debug(checkdoc) {
        dbg.abstract_doc_source_debugs(
          doc_abstraction,
          doc_matters,
        );
      }
      /+ ↓ output hub +/
      if (!(_opt_action_bool["skip_output"])) {
        output.hub(
          doc_abstraction,
          doc_matters,
        );
      }
      scope(exit) {
        debug(checkdoc) {
          writefln(
            "processed file: %s",
            fn_src
          );
        }
        destroy(content_body);
        destroy(t);
        destroy(doc_abstraction);
        destroy(doc_html_segnames);
        destroy(fn_src);
      }
    } else {
      /+ no recognized filename provided +/
      writeln("no recognized filename");
      break;
    }
  }
}
unittest {
  /++
	name        "sdp"
	description "A SiSU document parser writen in D."
	homepage    "http://sisudoc.org"
  +/
}