// Copyright (c) 2022 David Vogel
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

package main

import (
	"encoding/json"
	"image"
	"image/color"
	"math"
	"os"

	"github.com/tdewolff/canvas"
	"github.com/tdewolff/canvas/renderers/rasterizer"
)

var playerPathDisplayStyle = canvas.Style{
	Fill: canvas.Paint{},
	//Stroke:       canvas.Paint{Color: color.RGBA{0, 0, 0, 127}},
	StrokeWidth:  3.0,
	StrokeCapper: canvas.ButtCap,
	StrokeJoiner: canvas.MiterJoin,
	DashOffset:   0.0,
	Dashes:       []float64{},
	FillRule:     canvas.NonZero,
}

type PlayerPathElement struct {
	From        [2]float64 `json:"from"`
	To          [2]float64 `json:"to"`
	HP          float64    `json:"hp"`
	MaxHP       float64    `json:"maxHP"`
	Polymorphed bool       `json:"polymorphed"`
}

type PlayerPath []PlayerPathElement

func LoadPlayerPath(path string) (PlayerPath, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	var result PlayerPath

	jsonDec := json.NewDecoder(file)
	if err := jsonDec.Decode(&result); err != nil {
		return nil, err
	}

	return result, nil
}

// Draw implements the StitchedImageOverlay interface.
func (p PlayerPath) Draw(destImage *image.RGBA) {
	destRect := destImage.Bounds()

	// Same as destImage, but top left is translated to (0, 0).
	originImage := destImage.SubImage(destRect).(*image.RGBA)
	originImage.Rect = originImage.Rect.Sub(destRect.Min)

	c := canvas.New(float64(destRect.Dx()), float64(destRect.Dy()))
	ctx := canvas.NewContext(c)
	ctx.SetCoordSystem(canvas.CartesianIV)
	ctx.SetCoordRect(canvas.Rect{X: -float64(destRect.Min.X), Y: -float64(destRect.Min.Y), W: float64(destRect.Dx()), H: float64(destRect.Dy())}, float64(destRect.Dx()), float64(destRect.Dy()))

	// Set drawing style.
	ctx.Style = playerPathDisplayStyle

	for _, pathElement := range p {
		from, to := pathElement.From, pathElement.To

		// Only draw if the path may cross the image rectangle.
		pathRect := image.Rectangle{image.Point{int(from[0]), int(from[1])}, image.Point{int(to[0]), int(to[1])}}.Canon().Inset(int(-playerPathDisplayStyle.StrokeWidth) - 1)
		if pathRect.Overlaps(destRect) {
			path := &canvas.Path{}
			path.MoveTo(from[0], from[1])
			path.LineTo(to[0], to[1])

			if pathElement.Polymorphed {
				// Set stroke color to typically polymorph color.
				ctx.Style.Stroke.Color = color.RGBA{127, 50, 83, 127}
			} else {
				// Set stroke color depending on HP level.
				hpFactor := math.Max(math.Min(pathElement.HP/pathElement.MaxHP, 1), 0)
				hpFactorInv := 1 - hpFactor
				r, g, b, a := uint8((0*hpFactor+1*hpFactorInv)*127), uint8((1*hpFactor+0*hpFactorInv)*127), uint8(0), uint8(127)
				ctx.Style.Stroke.Color = color.RGBA{r, g, b, a}
			}

			ctx.DrawPath(0, 0, path)
		}
	}

	// Theoretically we would need to linearize imgRGBA first, but DefaultColorSpace assumes that the color space is linear already.
	r := rasterizer.FromImage(originImage, canvas.DPMM(1.0), canvas.DefaultColorSpace)
	c.RenderTo(r)
	r.Close() // This just transforms the image's luminance curve back from linear into non linear.
}
