package types

import (
	"bytes"
	"fmt"
	"github.com/gammazero/deque"
	"lab.zaar.be/thefish/alchemyst-go/engine/ecs"
	"lab.zaar.be/thefish/alchemyst-go/util"
)
import blt "lab.zaar.be/thefish/bearlibterminal"

var crng = util.NewRNG()

type ColorHolder interface {
	GetColor() uint32
}

type cdeque struct {
	deque.Deque
}

func (c *cdeque) Next() uint8 {
	c.Rotate(1)
	return c.Front().(uint8)
}

type DanceColorHolder struct {
	A uint8
	R *cdeque
	G *cdeque
	B *cdeque
}

func (dch DanceColorHolder) GetColor() uint32 {
	return blt.ColorFromARGB(
		dch.A,
		dch.R.Next(),
		dch.G.Next(),
		dch.B.Next(),
	)
}

type PlainColorHolder struct {
	A uint8
	R uint8
	G uint8
	B uint8
}

func (chb PlainColorHolder) GetColor() uint32 {
	return blt.ColorFromARGB(
		chb.A,
		chb.R,
		chb.G,
		chb.B,
	)
}

type TileColorSet struct {
	Fg     ColorHolder `json:"fg"`
	Bg     ColorHolder `json:"bg"`
	DarkFg ColorHolder `json:"darkfg"`
	DarkBg ColorHolder `json:"darkbg"`
}


type GlyphHolder interface {
	GetGlyph() string
}

type PlainGlyphHolder struct {
	Glyph string
}

func (pgh PlainGlyphHolder) GetGlyph() string {
	return pgh.Glyph
}

type Appearance struct {
	Glyph    GlyphHolder   `json:"glyph"`
	ColorSet TileColorSet `json:"colorSet"`
}

func SingleColorRing(colorValue uint8) *cdeque {
	c := &cdeque{}
	c.PushBack(colorValue)
	return c
}

func FillColorRing(colorValue uint8, minGlow, maxGlow, step int) *cdeque {
	q := make([]uint8, 0)
	color := int(colorValue)
	for color < maxGlow {
		q = append(q, uint8(color))
		color = crng.Range(1, step) + color
	}
	color = crng.Range(0, step+minGlow)
	q = append(q, colorValue)
	//for uint8(color) < uint8(colorValue) {
	//	q = append(q, uint8(color))
	//	color = crng.Range(1, step+minGlow)
	//}

	c := &cdeque{}
	for _, v := range q {
		c.PushBack(uint8(v))
	}
	return c
}

func DeviatedColorRing(colorValue uint8, minGlow, maxGlow int) *cdeque {
	q := make([]uint8, 0)
	color := int(colorValue)
	color = crng.Range(minGlow, maxGlow) + color
	q = append(q, colorValue)
	c := &cdeque{}
	c.PushBack(uint8(color))
	return c
}

func (app Appearance) Type() string {
	return ecs.AppearanceComponent
}

func (app *Appearance) MarshalJSON() ([]byte, error) {
	buffer := bytes.NewBufferString("{")
	//glyph
	buffer.WriteString(`"glyph":{`)
	if _, ok := app.Glyph.(*PlainGlyphHolder); ok {
		buffer.WriteString(fmt.Sprintf(`"type":"plain", "chars":"%s"`, app.Glyph.GetGlyph()))
	}
	buffer.WriteString("},")

	//color
	buffer.WriteString(`"color":{`)
	buffer.WriteString(getColorJson("fg", app.ColorSet.Fg) + ",")
	buffer.WriteString(getColorJson("bg", app.ColorSet.Bg) + ",")
	buffer.WriteString(getColorJson("darkfg", app.ColorSet.DarkFg) + ",")
	buffer.WriteString(getColorJson("darkbg", app.ColorSet.DarkBg))

	buffer.WriteString("}")

	buffer.WriteString("}")

	fmt.Printf("\n\nbuffer: %s\n\n", buffer.String())

	return buffer.Bytes(), nil
}

func (app *Appearance) UnmarshalJSON(buffer []byte) error {
	return nil
}

func getColorJson(field string, holder ColorHolder) string {
	result := ""
	if _, dch := holder.(*DanceColorHolder); dch {
		result = fmt.Sprintf(`{"dance":{`)
		result = result + detectRing("a", holder.(*DanceColorHolder).A) + ","
		result = result + detectRing("r", holder.(*DanceColorHolder).R) + ","
		result = result + detectRing("g", holder.(*DanceColorHolder).G) + ","
		result = result + detectRing("b", holder.(*DanceColorHolder).B)
		result = result + "}}"
	}
	if _, pch := holder.(*PlainColorHolder); pch {
		result = fmt.Sprintf(`{"plain":[%d,%d,%d,%d]}`,
			holder.(*PlainColorHolder).A,
			holder.(*PlainColorHolder).R,
			holder.(*PlainColorHolder).G,
			holder.(*PlainColorHolder).B,
		)
	}
	return fmt.Sprintf(`"%s":%s`, field, result)
}

func detectRing(channel string, something interface{}) string {
	result := ""

	switch something.(type) {
	case (int),(uint8):
		result += fmt.Sprintf(`"%s":{"plain":%d}`, channel, something)
	case (*cdeque):
		fmt.Printf("%v", something)
		if something.(*cdeque).Len() == 1 {
			//fixme right now we can not distinct plain and deviated
			result += fmt.Sprintf(`"%s":{"single":[%v]}`, channel, something.(*cdeque).Front())
		} else {
			result += fmt.Sprintf(`"%s":{"fill":[%v, %v, %v, %v]}`,
				channel,
				something.(*cdeque).Front(),
				something.(*cdeque).Next(),
				something.(*cdeque).Next(),
				something.(*cdeque).Next(),
			)
		}
	}
	return result
}