package main

import (
	"fmt"
	"io"
	"math"

	"github.com/ajstarks/svgo"
)

func demoSVG(w io.Writer) {
	var data []quartiles
	for i := 0; i < 50; i++ {
		data = append(data, quartiles{0.00003, 0.14, 0.20, 0.7, 263.5})
	}

	qc := chart{
		tick:   4,
		height: 60,

		yscale: (log10{logmin: -3, logmax: 1, perDecade: 15}).scale,
		yticks: []float64{1, 0.1, 0.01},
	}

	ac := chart{
		tick:   4,
		height: 60,

		yscale: (log10{logmin: 0, logmax: 6, perDecade: 10}).scale,
	}

	g := svg.New(w)
	g.Start(1000, 500)
	(chart{tick: 3, height: 40}).plotQuartiles(g, data)

	g.Translate(70, 300)
	(chart{tick: 3, height: 40}).plotQuartiles(g, data[:20])

	g.Translate(200, 0)

	qc.plotQuartiles(g, data)

	g.Translate(0, 60)
	ac.plotArea(g, []int{82, 0, 0, 1, 4, 10, 7, 36, 80, 253, 231, 317, 427, 381, 856, 1293, 38},
		"fill:silver; fill-opacity:1.0")
	ac.plotArea(g, []int{20, 0, 0, 0, 1, 2, 1, 5, 0, 53, 22, 6, 2, 4, 423, 32, 5},
		"fill:red; fill-opacity:0.5")
	g.Gend()

	g.Gend()

	g.Gend()

	g.End()
}

type log10 struct {
	logmin    float64
	logmax    float64
	perDecade float64
}

func (lg log10) scale(v float64) float64 {
	var l float64 = lg.logmin
	if v > 0 {
		l = math.Log10(v)
	}

	if l <= lg.logmin {
		l = lg.logmin
	}
	if l >= lg.logmax {
		l = lg.logmax
	}
	return (l - lg.logmin) * lg.perDecade
}

type quartiles [5]float64

type chart struct {
	tick   int
	height int

	yscale func(float64) float64

	yticks []float64
}

func (c chart) clamp(v float64) float64 {
	height := float64(c.height)
	if v > height {
		v = height
	}
	if v < 0 {
		v = 0
	}
	return v
}

func (c chart) yscaleFloat(v float64) float64 {
	if c.yscale != nil {
		v = c.yscale(v)
	}
	return float64(c.height) - c.clamp(v)
}

func (c chart) plotQuartiles(g *svg.SVG, data []quartiles) {
	for i, h := range c.yticks {
		width := 1
		if i == 0 {
			width = 2
		}
		g.Line(-1, int(c.yscaleFloat(h)), c.tick*(len(data)-1)+1, int(c.yscaleFloat(h)),
			fmt.Sprintf("stroke:silver;stroke-width:%d", width))
	}

	for i, data := range data {
		var qv quartiles
		for i, q := range data {
			qv[i] = c.yscaleFloat(q)
		}

		// line from q0 to q1, width 1, min height 1
		a, b := int(qv[0]), int(qv[1])
		if d, m := a-b, 1; d < m {
			b = a - m // grow up
		}
		g.Line(i*c.tick, a, i*c.tick, b, "stroke:black;stroke-width:1")
		// line (dot) for q2, width 2, height 2, blue
		a = int(qv[2])
		g.Line(i*c.tick, a-1, i*c.tick, a+1, "stroke:blue;stroke-width:2")
		// line from q3 to q4, width 1, min height 1
		a, b = int(qv[3]), int(qv[4])
		if d, m := a-b, 1; d < m {
			a = b + m // grow down
		}
		g.Line(i*c.tick, a, i*c.tick, b, "stroke:black;stroke-width:1")
	}
}

func (c chart) plotArea(g *svg.SVG, data []int, s ...string) {
	var prev float64
	for i, v := range data {
		l := c.yscaleFloat(float64(v))

		if i == 0 {
			xl, xr := -c.tick/2, 0
			yl, yr := int(l), int(l)
			g.Polygon([]int{xl, xl, xr, xr}, []int{c.height, yl, yr, c.height}, s...)
		}
		if i > 0 {
			xl, xr := (i-1)*c.tick, i*c.tick
			yl, yr := int(prev), int(l)
			g.Polygon([]int{xl, xl, xr, xr}, []int{c.height, yl, yr, c.height}, s...)
		}
		if i == len(data)-1 {
			xl, xr := i*c.tick, i*c.tick+c.tick/2
			yl, yr := int(l), int(l)
			g.Polygon([]int{xl, xl, xr, xr}, []int{c.height, yl, yr, c.height}, s...)
		}

		prev = l
	}
}
