module sdp.output.html;
template outputHTML() {
  import sdp.output;
  import
    std.digest.sha,
    std.file,
    std.outbuffer,
    std.zip,
    std.conv : to;
  import
    sdp.output.create_zip_file,
    sdp.output.xmls,
    sdp.output.xmls_css;
  mixin outputXHTMLs;
  void scroll(D,I)(
    auto return ref const D    doc_abstraction,
    auto return ref I          doc_matters,
  ) {
    mixin SiSUoutputRgxInit;
    auto xhtml_format = outputXHTMLs();
    auto rgx = Rgx();
    string[] doc_html;
    string[] doc;
    string suffix = ".html";
    string previous_part = "";
    string delimit = "";
    foreach (part; doc_matters.xml.keys_seq.scroll) {
      foreach (obj; doc_abstraction[part]) {
        delimit = xhtml_format.div_delimit(part, previous_part);
        string _txt = xhtml_format.special_characters(obj, obj.text);
        switch (obj.typeinfo.of_part) {
        case "frontmatter":              assert(part == "head" || "toc_scroll");
          switch (obj.typeinfo.is_of) {
          case "para":
            switch (obj.typeinfo.is_a) {
            case "heading":
              doc_html ~= delimit ~ xhtml_format.heading_scroll(obj, _txt, suffix);
              break;
            case "toc":
              doc_html ~= xhtml_format.para_scroll(obj, _txt, suffix);
              break;
            default:
              if ((doc_matters.opt.action.debug_do)) {
                writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_a);
              }
              break;
            }
            break;
          default:
            if ((doc_matters.opt.action.debug_do)) {
              writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_of);
            }
            break;
          }
          break;
        case "body":                     assert(part == "body" || "head"); // surprise
          switch (obj.typeinfo.is_of) {
          case "para":
            switch (obj.typeinfo.is_a) {
            case "heading":
              doc_html ~= delimit ~ xhtml_format.heading_scroll(obj, _txt, suffix);
              break;
            case "para":
              doc_html ~= xhtml_format.para_scroll(obj, _txt, suffix);
              break;
            default:
              if ((doc_matters.opt.action.debug_do)) {
                writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_a);
              }
              break;
            }
            break;
          case "block":
            switch (obj.typeinfo.is_a) {
            case "quote":
              doc_html ~= xhtml_format.quote_scroll(obj, _txt);
              break;
            case "group":
              doc_html ~= xhtml_format.group_scroll(obj, _txt);
              break;
            case "block":
              doc_html ~= xhtml_format.block_scroll(obj, _txt);
              break;
            case "poem":
              break;
            case "verse":
              doc_html ~= xhtml_format.verse_scroll(obj, _txt, suffix);
              break;
            case "code":
              doc_html ~= xhtml_format.code(obj, _txt);
              break;
            case "table":
              doc_html ~= xhtml_format.table(obj, _txt);
              break;
            default:
              if ((doc_matters.opt.action.debug_do)) {
                writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_a);
              }
              break;
            }
            break;
          default:
            if ((doc_matters.opt.action.debug_do)) {
              writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_of);
            }
            break;
          }
          break;
        case "backmatter":
          assert(part == "endnotes" || "glossary" || "bibliography" || "bookindex_scroll" || "blurb" || "tail");
          switch (obj.typeinfo.is_of) {
          case "para":
            switch (obj.typeinfo.is_a) {
            case "heading":
              doc_html ~= delimit ~ xhtml_format.heading_scroll(obj, _txt, suffix);
              break;
            case "endnote":              assert(part == "endnotes");
              doc_html ~= xhtml_format.para_scroll(obj, _txt, suffix);
              break;
            case "glossary":             assert(part == "glossary");
              doc_html ~= xhtml_format.para_scroll(obj, _txt, suffix);
              break;
            case "bibliography":         assert(part == "bibliography");
              doc_html ~= xhtml_format.para_scroll(obj, _txt, suffix);
              break;
            case "bookindex":            assert(part == "bookindex_scroll");
              doc_html ~= xhtml_format.para_scroll(obj, _txt, suffix);
              break;
            case "blurb":                assert(part == "blurb");
              doc_html ~= xhtml_format.para_scroll(obj, _txt, suffix);
              break;
            default:
              if ((doc_matters.opt.action.debug_do)) {
                writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_a);
              }
              break;
            }
            break;
          default:
            if ((doc_matters.opt.action.debug_do)) {
              writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_of);
            }
            break;
          }
          break;
        case "comment":
          break;
        default:
          if ((doc_matters.opt.action.debug_do)) {
            writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.of_part);
            writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_a);
            writeln(__FILE__, ":", __LINE__, ": ", obj.text);
          }
          break;
        }
      }
    }
    doc = xhtml_format.html_head(doc_matters, "scroll") ~ doc_html ~ xhtml_format.tail;
    scroll_write_output(doc_matters, doc);
  }
  void scroll_write_output(M,C)(
    M doc_matters,
    C doc,
  ) {
    debug(asserts) {
      static assert(is(typeof(doc)    == string[]));
    }
    auto pth_html = SiSUpathsHTML!()(doc_matters.output_path, doc_matters.src.language);
    try {
      if (!exists(pth_html.base)) {
        pth_html.base.mkdirRecurse;
      }
      auto f = File(pth_html.fn_scroll(doc_matters.src.filename), "w");
      foreach (o; doc) {
        f.writeln(o);
      }
    }
    catch (ErrnoException ex) {
      // Handle error
    }
    writeln(" ", pth_html.fn_scroll(doc_matters.src.filename));
  }
  void seg(D,M)(
    auto return ref const D    doc_abstraction,
    auto ref              M    doc_matters,
  ) {
    mixin SiSUoutputRgxInit;
    auto rgx = Rgx();
    auto xhtml_format = outputXHTMLs();
    string[][string] doc_html;
    string[][string] doc_html_endnotes;
    string[] doc;
    string segment_filename;
    string[] top_level_headings = ["","","",""];
    string previous_seg_filename = "";
    string suffix = ".html";
    string previous_part = "";
    string delimit = "";
    foreach (part; doc_matters.xml.keys_seq.seg) {
      foreach (obj; doc_abstraction[part]) {
        delimit = xhtml_format.div_delimit(part, previous_part);
        string _txt = xhtml_format.special_characters(obj, obj.text);
        if (obj.typeinfo.is_a == "heading") {
          assert(part == "head" || "toc_seg" || "body" || "endnotes" || "glossary" || "bibliography" || "bookindex_seg" || "blurb" || "tail");
          switch (obj.node.heading_lev_markup) {
          case 0: .. case 3:
            /+ fill buffer, and replace with new levels from 1 to 3 +/
            switch (obj.node.heading_lev_markup) {
            case 0:
              top_level_headings[0] = "";
              top_level_headings[1] = "";
              top_level_headings[2] = "";
              top_level_headings[3] = "";
              goto default;
            case 1:
              top_level_headings[1] = "";
              top_level_headings[2] = "";
              top_level_headings[3] = "";
              goto default;
            case 2:
              top_level_headings[2] = "";
              top_level_headings[3] = "";
              goto default;
            case 3:
              top_level_headings[3] = "";
              goto default;
            default:
              auto t = xhtml_format.heading_seg(obj, _txt, suffix, "seg");
              top_level_headings[obj.node.heading_lev_markup] = t[0];
              break;
            }
            break;
          case 4:
            segment_filename = obj.tags.segment_anchor_tag;
            doc_html[segment_filename] ~= xhtml_format.html_head(doc_matters, "seg");
            auto navigation_bar = xhtml_format.nav_pre_next_svg(obj);
            doc_html[segment_filename] ~= navigation_bar.toc_pre_next; // navigation bar
            previous_seg_filename = segment_filename;
            foreach (top_level_heading; top_level_headings) {
              // writeln(top_level_heading);
              doc_html[segment_filename] ~= top_level_heading;
            }
            auto t = xhtml_format.heading_seg(obj, _txt, suffix, "seg");
            doc_html[segment_filename] ~= t[0].to!string;
            doc_html[segment_filename] ~= xhtml_format.lev4_heading_subtoc(obj);
            doc_html_endnotes[segment_filename] ~= t[1];
            break;
          case 5: .. case 7:
            auto t = xhtml_format.heading_seg(obj, _txt, suffix, "seg");
            doc_html[segment_filename] ~= t[0].to!string;
            doc_html_endnotes[segment_filename] ~= t[1];
            break;
          case 8: .. case 9:
            if ((doc_matters.opt.action.debug_do)) {
              writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_a, ": ", obj.node.heading_lev_markup);
              writeln(__FILE__, ":", __LINE__, ": ", obj.text);
            }
            break;
          default:
            if ((doc_matters.opt.action.debug_do)) {
              writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_a, ": ", obj.node.heading_lev_markup);
            }
            break;
          }
        } else {
          assert(part == "head" || "toc_seg" || "body" || "endnotes" || "glossary" || "bibliography" || "bookindex_seg" || "blurb" || "tail");
          switch (obj.typeinfo.of_part) {
          case "frontmatter":             assert(part == "head" || "toc_seg");
            switch (obj.typeinfo.is_of) {
            case "para":
              switch (obj.typeinfo.is_a) {
              case "toc":
                auto t = xhtml_format.para_seg(obj, _txt, suffix, "seg");
                doc_html[segment_filename] ~= t[0].to!string;
                break;
              default:
                if ((doc_matters.opt.action.debug_do)) {
                  writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_a);
                }
                break;
              }
              break;
            default:
              if ((doc_matters.opt.action.debug_do)) {
                writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_a);
              }
              break;
            }
            break;
          case "body":                    assert(part == "body");
            switch (obj.typeinfo.is_of) {
            case "para":
              switch (obj.typeinfo.is_a) {
              case "para":
                auto t = xhtml_format.para_seg(obj, _txt, suffix, "seg");
                doc_html[segment_filename] ~= t[0].to!string;
                doc_html_endnotes[segment_filename] ~= t[1];
                break;
              default:
                if ((doc_matters.opt.action.debug_do)) {
                  writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_a);
                }
                break;
              }
              break;
            case "block":
              switch (obj.typeinfo.is_a) {
              case "quote":
                auto t = xhtml_format.quote_seg(obj, _txt, suffix, "seg");
                doc_html[segment_filename] ~= t[0].to!string;
                doc_html_endnotes[segment_filename] ~= t[1];
                break;
              case "group":
                auto t = xhtml_format.group_seg(obj, _txt, suffix, "seg");
                doc_html[segment_filename] ~= t[0].to!string;
                doc_html_endnotes[segment_filename] ~= t[1];
                break;
              case "block":
                auto t = xhtml_format.block_seg(obj, _txt, suffix, "seg");
                doc_html[segment_filename] ~= t[0].to!string;
                doc_html_endnotes[segment_filename] ~= t[1];
                break;
              case "poem":
                break;
              case "verse":
                auto t = xhtml_format.verse_seg(obj, _txt, suffix, "seg");
                doc_html[segment_filename] ~= t[0].to!string;
                doc_html_endnotes[segment_filename] ~= t[1];
                break;
              case "code":
                doc_html[segment_filename] ~= xhtml_format.code(obj, _txt);
                break;
              case "table":
                doc_html[segment_filename] ~= xhtml_format.table(obj, _txt);
                doc_html_endnotes[segment_filename] ~= "";
                break;
              default:
                if ((doc_matters.opt.action.debug_do)) {
                  writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_a);
                }
                break;
              }
              break;
            default:
              if ((doc_matters.opt.action.debug_do)) {
                writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_of);
              }
              break;
            }
            break;
          case "backmatter":
            assert(part == "endnotes" || "glossary" || "bibliography" || "bookindex_seg" || "blurb" || "tail");
            switch (obj.typeinfo.is_of) {
            case "para":
              switch (obj.typeinfo.is_a) {
              case "endnote":             assert(part == "endnotes");
                auto t = xhtml_format.para_seg(obj, _txt, suffix, "seg");
                doc_html[segment_filename] ~= t[0];
                break;
              case "glossary":            assert(part == "glossary");
                auto t = xhtml_format.para_seg(obj, _txt, suffix, "seg");
                doc_html[segment_filename] ~= t[0];
                doc_html_endnotes[segment_filename] ~= t[1];
                break;
              case "bibliography":        assert(part == "bibliography");
                auto t = xhtml_format.para_seg(obj, _txt, suffix, "seg");
                doc_html[segment_filename] ~= t[0];
                doc_html_endnotes[segment_filename] ~= t[1];
                break;
              case "bookindex":           assert(part == "bookindex_seg");
                auto t = xhtml_format.para_seg(obj, _txt, suffix, "seg");
                doc_html[segment_filename] ~= t[0];
                doc_html_endnotes[segment_filename] ~= t[1];
                break;
              case "blurb":               assert(part == "blurb");
                auto t = xhtml_format.para_seg(obj, _txt, suffix, "seg");
                doc_html[segment_filename] ~= t[0];
                doc_html_endnotes[segment_filename] ~= t[1];
                break;
              default:
                if ((doc_matters.opt.action.debug_do)) {
                  writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_a);
                }
                break;
              }
              break;
            default:
              if ((doc_matters.opt.action.debug_do)) {
                writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.is_of);
              }
              break;
            }
            break;
          case "comment":
            break;
          default:
            if ((doc_matters.opt.action.debug_do)) {
              writeln(__FILE__, ":", __LINE__, ": ", obj.typeinfo.of_part);
            }
            break;
          }
        }
      }
    }
    seg_write_output(doc_matters, doc_html, doc_html_endnotes);
  }
  void seg_write_output(M,D,E)(
    M doc_matters,
    D doc_html,
    E doc_html_endnotes,
  ) {
    debug(asserts) {
      static assert(is(typeof(doc_html)      == string[][string]));
    }
    mixin SiSUoutputRgxInit;
    auto rgx = Rgx();
    auto pth_html = SiSUpathsHTML!()(doc_matters.output_path, doc_matters.src.language);
    auto xhtml_format = outputXHTMLs();
    auto m = doc_matters.src.filename.matchFirst(rgx.src_fn);
    try {
      if (!exists(pth_html.seg(doc_matters.src.filename))) {
        pth_html.seg(doc_matters.src.filename).mkdirRecurse;
      }
      foreach (seg_filename; doc_matters.xml.segnames) {
        auto f = File(pth_html.fn_seg(doc_matters.src.filename, seg_filename), "w");
        foreach (docseg; doc_html[seg_filename]) {
          f.writeln(docseg);
        }
        foreach (docseg; doc_html_endnotes[seg_filename]) {
          f.writeln(docseg);
        }
        f.writeln(xhtml_format.tail);
      }
    }
    catch (ErrnoException ex) {
      // handle error
    }
    writeln(" ", pth_html.fn_seg(doc_matters.src.filename, "toc"));
  }
  void css(M)(
    auto return ref M          doc_matters,
  ) {
    auto css = SiSUcss();
    auto pth_html = SiSUpathsHTML!()(doc_matters.output_path, doc_matters.src.language);
    try {
      if (!exists(pth_html.css)) {
        (pth_html.css).mkdirRecurse;
      }
      auto f = File(pth_html.fn_seg_css, "w");
      f.writeln(css.html_seg_css);
      f = File(pth_html.fn_scroll_css, "w");
      f.writeln(css.html_scroll_css);
    }
    catch (ErrnoException ex) {
      // Handle error
    }
  }
  void images_cp(M)(
    auto return ref M          doc_matters,
  ) {
    { /+ (copy html images) +/
  
      auto pth_html = SiSUpathsHTML!()(doc_matters.output_path, doc_matters.src.language);
      if (!exists(pth_html.image)) {
        pth_html.image.mkdirRecurse;
      }
      foreach (image; doc_matters.srcs.image_list) {
        auto fn_src_in = doc_matters.src.image_dir_path ~ "/" ~ image;
        auto fn_src_out = pth_html.image ~ "/" ~ image;
        debug(images_html) {
          writeln(fn_src_in, " -> ", fn_src_out);
        }
        if (exists(fn_src_in)) {
          fn_src_in.copy(fn_src_out);
        } else {
          writeln("WARNING image not found: ", fn_src_in);
        }
      }
    }
  }
}