# modules/outline.js

    /**
     * @license
     * Copyright (c) 2014 Steven Spungin (TwelveTone LLC)  steven@twelvetone.tv
     *
     * Licensed under the MIT License.
     * http://opensource.org/licenses/mit-license
     */

    import { jsPDF } from "../jspdf.js";

    /**
     * jsPDF Outline PlugIn
     *
     * Generates a PDF Outline
     * @name outline
     * @module
     */
    (function(jsPDFAPI) {
      "use strict";

      var namesOid;
      //var destsGoto = [];

      jsPDFAPI.events.push([
        "postPutResources",
        function() {
          var pdf = this;
          var rx = /^(\d+) 0 obj$/;

          // Write action goto objects for each page
          // this.outline.destsGoto = [];
          // for (var i = 0; i < totalPages; i++) {
          // var id = pdf.internal.newObject();
          // this.outline.destsGoto.push(id);
          // pdf.internal.write("<</D[" + (i * 2 + 3) + " 0 R /XYZ null
          // null null]/S/GoTo>> endobj");
          // }
          //
          // for (var i = 0; i < dests.length; i++) {
          // pdf.internal.write("(page_" + (i + 1) + ")" + dests[i] + " 0
          // R");
          // }
          //
          if (this.outline.root.children.length > 0) {
            var lines = pdf.outline.render().split(/\r\n/);
            for (var i = 0; i < lines.length; i++) {
              var line = lines[i];
              var m = rx.exec(line);
              if (m != null) {
                var oid = m[1];
                pdf.internal.newObjectDeferredBegin(oid, false);
              }
              pdf.internal.write(line);
            }
          }

          // This code will write named destination for each page reference
          // (page_1, etc)
          if (this.outline.createNamedDestinations) {
            var totalPages = this.internal.pages.length;
            // WARNING: this assumes jsPDF starts on page 3 and pageIDs
            // follow 5, 7, 9, etc
            // Write destination objects for each page
            var dests = [];
            for (var i = 0; i < totalPages; i++) {
              var id = pdf.internal.newObject();
              dests.push(id);
              var info = pdf.internal.getPageInfo(i + 1);
              pdf.internal.write(
                "<< /D[" + info.objId + " 0 R /XYZ null null null]>> endobj"
              );
            }

            // assign a name for each destination
            var names2Oid = pdf.internal.newObject();
            pdf.internal.write("<< /Names [ ");
            for (var i = 0; i < dests.length; i++) {
              pdf.internal.write("(page_" + (i + 1) + ")" + dests[i] + " 0 R");
            }
            pdf.internal.write(" ] >>", "endobj");

            // var kids = pdf.internal.newObject();
            // pdf.internal.write('<< /Kids [ ' + names2Oid + ' 0 R');
            // pdf.internal.write(' ] >>', 'endobj');

            namesOid = pdf.internal.newObject();
            pdf.internal.write("<< /Dests " + names2Oid + " 0 R");
            pdf.internal.write(">>", "endobj");
          }
        }
      ]);

      jsPDFAPI.events.push([
        "putCatalog",
        function() {
          var pdf = this;
          if (pdf.outline.root.children.length > 0) {
            pdf.internal.write(
              "/Outlines",
              this.outline.makeRef(this.outline.root)
            );
            if (this.outline.createNamedDestinations) {
              pdf.internal.write("/Names " + namesOid + " 0 R");
            }
            // Open with Bookmarks showing
            // pdf.internal.write("/PageMode /UseOutlines");
          }
        }
      ]);

      jsPDFAPI.events.push([
        "initialized",
        function() {
          var pdf = this;

          pdf.outline = {
            createNamedDestinations: false,
            root: {
              children: []
            }
          };

          /**
           * Options: pageNumber
           */
          pdf.outline.add = function(parent, title, options) {
            var item = {
              title: title,
              options: options,
              children: []
            };
            if (parent == null) {
              parent = this.root;
            }
            parent.children.push(item);
            return item;
          };

          pdf.outline.render = function() {
            this.ctx = {};
            this.ctx.val = "";
            this.ctx.pdf = pdf;

            this.genIds_r(this.root);
            this.renderRoot(this.root);
            this.renderItems(this.root);

            return this.ctx.val;
          };

          pdf.outline.genIds_r = function(node) {
            node.id = pdf.internal.newObjectDeferred();
            for (var i = 0; i < node.children.length; i++) {
              this.genIds_r(node.children[i]);
            }
          };

          pdf.outline.renderRoot = function(node) {
            this.objStart(node);
            this.line("/Type /Outlines");
            if (node.children.length > 0) {
              this.line("/First " + this.makeRef(node.children[0]));
              this.line(
                "/Last " + this.makeRef(node.children[node.children.length - 1])
              );
            }
            this.line(
              "/Count " +
                this.count_r(
                  {
                    count: 0
                  },
                  node
                )
            );
            this.objEnd();
          };

          pdf.outline.renderItems = function(node) {
            var getVerticalCoordinateString = this.ctx.pdf.internal
              .getVerticalCoordinateString;
            for (var i = 0; i < node.children.length; i++) {
              var item = node.children[i];
              this.objStart(item);

              this.line("/Title " + this.makeString(item.title));

              this.line("/Parent " + this.makeRef(node));
              if (i > 0) {
                this.line("/Prev " + this.makeRef(node.children[i - 1]));
              }
              if (i < node.children.length - 1) {
                this.line("/Next " + this.makeRef(node.children[i + 1]));
              }
              if (item.children.length > 0) {
                this.line("/First " + this.makeRef(item.children[0]));
                this.line(
                  "/Last " + this.makeRef(item.children[item.children.length - 1])
                );
              }

              var count = (this.count = this.count_r(
                {
                  count: 0
                },
                item
              ));
              if (count > 0) {
                this.line("/Count " + count);
              }

              if (item.options) {
                if (item.options.pageNumber) {
                  // Explicit Destination
                  //WARNING this assumes page ids are 3,5,7, etc.
                  var info = pdf.internal.getPageInfo(item.options.pageNumber);
                  this.line(
                    "/Dest " +
                      "[" +
                      info.objId +
                      " 0 R /XYZ 0 " +
                      getVerticalCoordinateString(0) +
                      " 0]"
                  );
                  // this line does not work on all clients (pageNumber instead of page ref)
                  //this.line('/Dest ' + '[' + (item.options.pageNumber - 1) + ' /XYZ 0 ' + this.ctx.pdf.internal.pageSize.getHeight() + ' 0]');

                  // Named Destination
                  // this.line('/Dest (page_' + (item.options.pageNumber) + ')');

                  // Action Destination
                  // var id = pdf.internal.newObject();
                  // pdf.internal.write('<</D[' + (item.options.pageNumber - 1) + ' /XYZ null null null]/S/GoTo>> endobj');
                  // this.line('/A ' + id + ' 0 R' );
                }
              }
              this.objEnd();
            }
            for (var z = 0; z < node.children.length; z++) {
              this.renderItems(node.children[z]);
            }
          };

          pdf.outline.line = function(text) {
            this.ctx.val += text + "\r\n";
          };

          pdf.outline.makeRef = function(node) {
            return node.id + " 0 R";
          };

          pdf.outline.makeString = function(val) {
            return "(" + pdf.internal.pdfEscape(val) + ")";
          };

          pdf.outline.objStart = function(node) {
            this.ctx.val += "\r\n" + node.id + " 0 obj" + "\r\n<<\r\n";
          };

          pdf.outline.objEnd = function() {
            this.ctx.val += ">> \r\n" + "endobj" + "\r\n";
          };

          pdf.outline.count_r = function(ctx, node) {
            for (var i = 0; i < node.children.length; i++) {
              ctx.count++;
              this.count_r(ctx, node.children[i]);
            }
            return ctx.count;
          };
        }
      ]);

      return this;
    })(jsPDF.API);