class Spoom::Coverage::D3::Timeline

@abstract

def area(y:, color: "#ccc", curve: "curveCatmullRom.alpha(1)")

: (y: String, ?color: String, ?curve: String) -> String
def area(y:, color: "#ccc", curve: "curveCatmullRom.alpha(1)")
  <<~HTML
    svg_#{id}.append("path")
      .datum(data_#{id}.filter((d) => #{y}))
      .attr("class", "area")
      .attr("d", d3.area()
        .defined((d) => #{y})
        .x((d) => xScale_#{id}(parseDate(d.timestamp)))
        .y0(yScale_#{id}(0))
        .y1((d) => yScale_#{id}(#{y}))
        .curve(d3.#{curve}))
      .attr("fill", "#{color}")
  HTML
end

def header_script

: -> String
def header_script
  <<~JS
    var parseVersion = function(version) {
      if (!version) {
        return null;
      }
      return parseFloat(version.replaceAll("0.", ""));
    }
    function tooltipTimeline(d, kind) {
      moveTooltip(d)
        .html("commit <b>" + d.data.commit + "</b><br>"
          + d3.timeFormat("%y/%m/%d")(parseDate(d.data.timestamp)) + "<br><br>"
          + "<b>typed: " + d.key + "</b><br><br>"
          + "<b>" + (d.data.values[d.key] ? d.data.values[d.key] : 0) + "</b> " + kind +"<br>"
          + "<b>" + toPercent(d.data.values[d.key] ? d.data.values[d.key] : 0, d.data.total) + "%")
    }
  JS
end

def header_style

: -> String
def header_style
  <<~CSS
    .domain {
      stroke: transparent;
    }
    .grid line {
      stroke: #ccc;
    }
    .axis text {
      font: 12px Arial, sans-serif;
      fill: #333;
      text-anchor: right;
      pointer-events: none;
    }
    .area {
      fill-opacity: 0.5;
    }
    .line {
      stroke-width: 2;
      fill: transparent;
    }
    .dot {
      r: 2;
      fill: #888;
    }
    .inverted .grid line {
      stroke: #777;
    }
    .inverted .area {
      fill-opacity: 0.9;
    }
    .inverted .axis text {
      fill: #fff;
    }
    .inverted .axis line {
      stroke: #fff;
    }
    .inverted .dot {
      fill: #fff;
    }
  CSS
end

def initialize(id, data, keys)

: (String id, untyped data, Array[String] keys) -> void
def initialize(id, data, keys)
  super(id, data)
  @keys = keys
end

def line(y:, color: "#ccc", curve: "curveCatmullRom.alpha(1)")

: (y: String, ?color: String, ?curve: String) -> String
def line(y:, color: "#ccc", curve: "curveCatmullRom.alpha(1)")
  <<~HTML
    svg_#{id}.append("path")
       .datum(data_#{id}.filter((d) => #{y}))
       .attr("class", "line")
       .attr("d", d3.line()
         .x((d) => xScale_#{id}(parseDate(d.timestamp)))
         .y((d) => yScale_#{id}(#{y}))
         .curve(d3.#{curve}))
       .attr("stroke", "#{color}")
  HTML
end

def plot; end

Other tags:
    Abstract: -
def plot; end

def points(y:)

: (y: String) -> String
def points(y:)
  <<~HTML
    svg_#{id}.selectAll("circle")
      .data(data_#{id})
      .enter()
        .append("circle")
        .attr("class", "dot")
        .attr("cx", (d) => xScale_#{id}(parseDate(d.timestamp)))
        .attr("cy", (d, i) => yScale_#{id}(#{y}))
        .on("mouseover", (d) => tooltip.style("opacity", 1))
        .on("mousemove", tooltip_#{id})
        .on("mouseleave", (d) => tooltip.style("opacity", 0));
  HTML
end

def script

: -> String
@override
def script
  <<~HTML
    #{tooltip}
    var data_#{id} = #{@data.to_json};
    function draw_#{id}() {
      var width_#{id} = document.getElementById("#{id}").clientWidth;
      var height_#{id} = 200;
      d3.select("##{id}").selectAll("*").remove()
      var svg_#{id} = d3.select("##{id}")
        .attr("width", width_#{id})
        .attr("height", height_#{id})
      #{plot}
    }
    draw_#{id}();
    window.addEventListener("resize", draw_#{id});
  HTML
end

def x_scale

: -> String
def x_scale
  <<~HTML
    var xScale_#{id} = d3.scaleTime()
      .range([0, width_#{id}])
      .domain(d3.extent(data_#{id}, (d) => parseDate(d.timestamp)));
    svg_#{id}.append("g")
      .attr("class", "grid")
      .attr("transform", "translate(0," + height_#{id} + ")")
      .call(d3.axisBottom(xScale_#{id})
        .tickFormat("")
        .tickSize(-height_#{id}))
  HTML
end

def x_ticks

: -> String
def x_ticks
  <<~HTML
    svg_#{id}.append("g")
      .attr("class", "axis x")
      .attr("transform", "translate(0," + height_#{id} + ")")
      .call(d3.axisBottom(xScale_#{id})
        .tickFormat(d3.timeFormat("%y/%m/%d"))
        .tickPadding(-15)
        .tickSize(-3));
  HTML
end

def y_scale(min:, max:, ticks:)

: (min: String, max: String, ticks: String) -> String
def y_scale(min:, max:, ticks:)
  <<~HTML
    var yScale_#{id} = d3.scaleLinear()
      .range([height_#{id}, 0])
      .domain([#{min}, #{max}]);
    svg_#{id}.append("g")
      .attr("class", "grid")
      .call(d3.axisLeft(yScale_#{id})
        .#{ticks}
        .tickFormat("")
        .tickSize(-width_#{id}))
  HTML
end

def y_ticks(ticks:, format:, padding:)

: (ticks: String, format: String, padding: Integer) -> String
def y_ticks(ticks:, format:, padding:)
  <<~HTML
    svg_#{id}.append("g")
      .attr("class", "axis y")
      .call(d3.axisLeft(yScale_#{id})
        .#{ticks}
        .tickSize(-3)
        .tickFormat((d) => #{format})
        .tickPadding(-#{padding}))
  HTML
end