HTMLWidgets.widget({

  name: 'DiagrammeR',

  type: 'output',

  initialize: function(el, width, height) {

    /* wait to initialize until renderValue
        since x not provided until then
        and mermaid will try to build the diagram
        as soon as class of the div is set to "mermaid"
    */

    /* to prevent auto init() by mermaid
        not documented but
        see lines https://github.com/knsv/mermaid/blob/master/src/main.js#L100-L109
          mermaid_config in global with mermaid_config.startOnLoad = false
        appears to turn off the auto init behavior
        allowing us to callback after manually init and then callback
        after complete
    */
    window.mermaid.startOnLoad = false;

    // set config options for Gantt
    //   undocumented but these can be provided
    //   so from R
    //   m1 <- mermaid(spec)
    //   m1$x$config = list(ganttConfig = list( barHeight = 100 ) )
    mermaid.ganttConfig = {
        titleTopMargin:25,
        barHeight:20,
        barGap:4,
        topPadding:50,
        sidePadding:100,
        gridLineStartPadding:35,
        fontSize:11,
        numberSectionStyles:4,
        axisFormatter: [
            // Within a day
            ["%I:%M", function (d) {
                return d.getHours();
            }],
            // Monday a week
            ["w. %U", function (d) {
                return d.getDay() == 1;
            }],
            // Day within a week (not monday)
            ["%a %d", function (d) {
                return d.getDay() && d.getDate() != 1;
            }],
            // within a month
            ["%b %d", function (d) {
                return d.getDate() != 1;
            }],
            // Month
            ["%m-%y", function (d) {
                return d.getMonth();
            }]
        ]
    };

    return {
      // TODO: add instance fields as required
    }

  },

  renderValue: function(el, x, instance) {

    // if no diagram provided then assume
    // that the diagrams are provided through htmltools tags
    // and DiagrammeR was just used for dependencies
    if ( x.diagram != "" ) {
      el.innerHTML = x.diagram;
      //if dynamic such as shiny remove data-processed
      // so mermaid will reprocess and redraw
      el.removeAttribute("data-processed");
      el.classList.add('mermaid');
      //make sure if shiny that we turn display back on
      el.style.display = "";
      //again if dynamic such as shiny
      //  explicitly run mermaid.init()
    } else {
      // set display to none
      // should we remove instead??
      el.style.display = "none";
    }

    // check for undocumented ganttConfig
    //   to override the defaults manually entered
    //   in initialize above
    //   note this is really sloppy and will not
    //   work well if multiple gantt charts
    //   with custom configs here
    if( typeof x.config !== "undefined" &&
         typeof x.config.ganttConfig !== "undefined" ){
      Object.keys(x.config.ganttConfig).map(function(k){
        window.mermaid.ganttConfig[k] = x.config.ganttConfig[k];
      })
    }


    // use this to sort of make our diagram responsive
    //  or at a minimum fit within the bounds set by htmlwidgets
    //  for the parent container
    function makeResponsive(el){
       var svg = el.getElementsByTagName("svg")[0];
       if(svg){
        if(svg.width) {svg.removeAttribute("width")};
        if(svg.height) {svg.removeAttribute("height")};
        svg.style.width = "100%";
        svg.style.height = "100%";
       }
    };


    // get all DiagrammeR mermaids widgets
    dg = document.getElementsByClassName("DiagrammeR");
    // run mermaid.init
    //  but use try catch block
    //  to send error to the htmlwidget for display
    try{
      mermaid.init( el );

      // sort of make our diagram responsive
      //   should we make this an option?
      //   if so, then could easily add to list of post process tasks
      makeResponsive( el );

      if (HTMLWidgets.shinyMode) {
        // Get widget id
        var id = el.id;

        $("#" + id + " .node").click(function(e) {
          // Build return object *obj* with node-id and node textContent
          var obj = {
            id: e.currentTarget.id,
            nodeValues: e.currentTarget.textContent
          };
          // Send *obj* to Shiny's inputs (input$[id]+_click  e.g.: input$vtree_click))
          Shiny.setInputValue(id + "_click", obj, {priority: "event"});
        });
      }

      /*
      // change the id of our SVG assigned by mermaid to prevent conflict
      //   mermaid.init has a counter that will reset to 0
      //   and cause duplication of SVG id if multiple
      d3.select(el).select("svg")
        .attr("id", "mermaidChart-" + el.id);
      // now we have to change the styling assigned by mermaid
      //   to point to our new id that we have assigned
      //   will add if since sequence diagrams do not have stylesheet
      if(d3.select(el).select("svg").select("style")[0][0]){
        d3.select(el).select("svg").select("style")[0][0].innerHTML = d3.select(el).select("svg")
          .select("style")[0][0].innerHTML
      */
      /// sep comment for / in regex    .replace(/mermaidChart[0-9]*/gi, "mermaidChart-" + el.id);
      /*}
      */

      // set up a container for tasks to perform after completion
      //  one example would be add callbacks for event handling
      //  styling
      if (!(typeof x.tasks === "undefined") ){
        if ( (typeof x.tasks.length === "undefined") ||
         (typeof x.tasks === "function" ) ) {
           // handle a function not enclosed in array
           // should be able to remove once using jsonlite
           x.tasks = [x.tasks];
        }
        x.tasks.map(function(t){
          // for each tasks add it to the mermaid.tasks with el
          t.call(el);
        })
      }

    } catch(e) {
      // if error look for last processed DiagrammeR
      //  and send error to the container div
      //  with pre containing the errors
      var processedDg = d3.selectAll(".DiagrammeR[data-processed=true]");
      // select the last
      processedDg = d3.select(processedDg[0][processedDg[0].length - 1])
      // remove the svg
      processedDg.select("svg").remove();

      //if dynamic such as shiny remove data-processed
      // so mermaid will reprocess and redraw
      if (HTMLWidgets.shinyMode) {
        el.removeAttribute("data-processed")
      }

      processedDg.append("pre").html( ["parse error with " + x.diagram, e.message].join("\n") )
    }

  },

  resize: function(el, width, height, instance) {

  }


});
