package svg
import (
	"encoding/base64"
	"strings"
	"gno.land/p/nt/avl"
	"gno.land/p/nt/ufmt"
)
type Canvas struct {
	Width, Height int
	ViewBox       string
	Elems         []Elem
	Style         *avl.Tree
}
type Elem interface{ String() string }
func NewCanvas(width, height int) *Canvas {
	return &Canvas{
		Width:  width,
		Height: height,
		Style:  nil,
	}
}
func (c *Canvas) AddStyle(key, value string) *Canvas {
	if c.Style == nil {
		c.Style = avl.NewTree()
	}
	c.Style.Set(key, value)
	return c
}
func (c *Canvas) WithViewBox(x, y, width, height int) *Canvas {
	c.ViewBox = ufmt.Sprintf("%d %d %d %d", x, y, width, height)
	return c
}
// Render renders your canvas
func (c Canvas) Render(alt string) string {
	base64SVG := base64.StdEncoding.EncodeToString([]byte(c.String()))
	return ufmt.Sprintf("", alt, base64SVG)
}
func (c Canvas) String() string {
	out := ""
	out += ufmt.Sprintf(`"
	return out
}
func (c Canvas) Base64() string {
	out := c.String()
	return base64.StdEncoding.EncodeToString([]byte(out))
}
func (c *Canvas) Append(elem ...Elem) {
	c.Elems = append(c.Elems, elem...)
}
type BaseAttrs struct {
	ID          string
	Class       string
	Style       string
	Stroke      string
	StrokeWidth string
	Opacity     string
	Transform   string
	Visibility  string
}
func (b BaseAttrs) String() string {
	var elems []string
	if b.ID != "" {
		elems = append(elems, `id="`+b.ID+`"`)
	}
	if b.Class != "" {
		elems = append(elems, `class="`+b.Class+`"`)
	}
	if b.Style != "" {
		elems = append(elems, `style="`+b.Style+`"`)
	}
	if b.Stroke != "" {
		elems = append(elems, `stroke="`+b.Stroke+`"`)
	}
	if b.StrokeWidth != "" {
		elems = append(elems, `stroke-width="`+b.StrokeWidth+`"`)
	}
	if b.Opacity != "" {
		elems = append(elems, `opacity="`+b.Opacity+`"`)
	}
	if b.Transform != "" {
		elems = append(elems, `transform="`+b.Transform+`"`)
	}
	if b.Visibility != "" {
		elems = append(elems, `visibility="`+b.Visibility+`"`)
	}
	if len(elems) == 0 {
		return ""
	}
	return strings.Join(elems, " ")
}
type Circle struct {
	CX   int // center X
	CY   int // center Y
	R    int // radius
	Fill string
	Attr BaseAttrs
}
func (c Circle) String() string {
	return ufmt.Sprintf(``, c.CX, c.CY, c.R, c.Fill, c.Attr.String())
}
func NewCircle(cx, cy, r int, fill string) *Circle {
	return &Circle{
		CX:   cx,
		CY:   cy,
		R:    r,
		Fill: fill,
	}
}
func (c *Circle) WithClass(class string) *Circle {
	c.Attr.Class = class
	return c
}
type Ellipse struct {
	CX   int // center X
	CY   int // center Y
	RX   int // radius X
	RY   int // radius Y
	Fill string
	Attr BaseAttrs
}
func (e Ellipse) String() string {
	return ufmt.Sprintf(``, e.CX, e.CY, e.RX, e.RY, e.Fill, e.Attr.String())
}
func NewEllipse(cx, cy int, fill string) *Ellipse {
	return &Ellipse{
		CX:   cx,
		CY:   cy,
		Fill: fill,
	}
}
func (e *Ellipse) WithClass(class string) *Ellipse {
	e.Attr.Class = class
	return e
}
type Rectangle struct {
	X, Y, Width, Height int
	RX, RY              int // corner radiuses
	Fill                string
	Attr                BaseAttrs
}
func (r Rectangle) String() string {
	return ufmt.Sprintf(``, r.X, r.Y, r.Width, r.Height, r.RX, r.RY, r.Fill, r.Attr.String())
}
func NewRectangle(x, y, width, height int, fill string) *Rectangle {
	return &Rectangle{
		X:      x,
		Y:      y,
		Width:  width,
		Height: height,
		Fill:   fill,
	}
}
func (r *Rectangle) WithClass(class string) *Rectangle {
	r.Attr.Class = class
	return r
}
type Path struct {
	D    string
	Fill string
	Attr BaseAttrs
}
func (p Path) String() string {
	return ufmt.Sprintf(``, p.D, p.Fill, p.Attr.String())
}
func NewPath(d, fill string) *Path {
	return &Path{
		D:    d,
		Fill: fill,
	}
}
func (p *Path) WithClass(class string) *Path {
	p.Attr.Class = class
	return p
}
type Polygon struct { // closed shape
	Points string
	Fill   string
	Attr   BaseAttrs
}
func (p Polygon) String() string {
	return ufmt.Sprintf(``, p.Points, p.Fill, p.Attr.String())
}
func NewPolygon(points, fill string) *Polygon {
	return &Polygon{
		Points: points,
		Fill:   fill,
	}
}
func (p *Polygon) WithClass(class string) *Polygon {
	p.Attr.Class = class
	return p
}
type Polyline struct { // polygon but not necessarily closed
	Points string
	Fill   string
	Attr   BaseAttrs
}
func (p Polyline) String() string {
	return ufmt.Sprintf(``, p.Points, p.Fill, p.Attr.String())
}
func NewPolyline(points, fill string) *Polyline {
	return &Polyline{
		Points: points,
		Fill:   fill,
	}
}
func (p *Polyline) WithClass(class string) *Polyline {
	p.Attr.Class = class
	return p
}
type Text struct {
	X, Y       int
	DX, DY     int // shift text pos horizontally/ vertically
	Rotate     string
	Text, Fill string
	Attr       BaseAttrs
}
func (c Text) String() string {
	return ufmt.Sprintf(`%s`, c.X, c.Y, c.DX, c.DY, c.Rotate, c.Fill, c.Attr.String(), c.Text)
}
func NewText(x, y int, text, fill string) *Text {
	return &Text{
		X:    x,
		Y:    y,
		Text: text,
		Fill: fill,
	}
}
func (c *Text) WithClass(class string) *Text {
	c.Attr.Class = class
	return c
}
type Group struct {
	Elems []Elem
	Fill  string
	Attr  BaseAttrs
}
func (g Group) String() string {
	out := ""
	for _, e := range g.Elems {
		out += e.String()
	}
	return ufmt.Sprintf(`%s`, g.Fill, g.Attr.String(), out)
}
func NewGroup(fill string) *Group {
	return &Group{
		Fill: fill,
	}
}
func (g *Group) Append(elem ...Elem) {
	g.Elems = append(g.Elems, elem...)
}
func (g *Group) WithClass(class string) *Group {
	g.Attr.Class = class
	return g
}