package main

import (
	"flag"
	"image/color"
	"fmt"
	"log"
	"math"
	"math/rand"
	"os"
	"time"

	sg "github.com/d2fn/sumi/internal/graphics"

	gui "github.com/gen2brain/raylib-go/raygui"
	rl "github.com/gen2brain/raylib-go/raylib"
	"github.com/ojrac/opensimplex-go"
)

func Bootstrap() *Env {

	rl.InitWindow(800, 600, "bootstrap")

	monitor := rl.GetCurrentMonitor()
	fmt.Printf("Using monitor %d\n", monitor)

	// derive defaults from the monitor size
	monitorWidth := rl.GetMonitorWidth(monitor)
	monitorHeight := rl.GetMonitorHeight(monitor)

	// use a large portion of the available space, but not all of it!
	defaultWindowWidth := int(float32(monitorWidth) * 0.95)
	defaultWindowHeight := int(float32(monitorHeight) * 0.9)
	windowWidth := defaultWindowWidth
	windowHeight := defaultWindowHeight

	// set controls to use 1/6th of the width
	controlsRelWidth := 1.0 / 12.0

	defaultGraphicsWidth := 5 * int((float64(defaultWindowWidth) * (1.0 - controlsRelWidth)))
	defaultGraphicsHeight := 5 * defaultWindowHeight
	graphicsWidth := defaultGraphicsWidth
	graphicsHeight := defaultGraphicsHeight

	fmt.Printf("monitor : %d x %d / window : %d x %d / buffer : %d x %d",
		monitorWidth, monitorHeight,
		defaultWindowWidth, defaultWindowHeight,
		defaultGraphicsWidth, defaultWindowHeight)

	var snapshotsPath string

	flag.StringVar(&snapshotsPath, "path", "snapshots", "Path to snapshots and db")
	flag.IntVar(&graphicsWidth, "gw", defaultGraphicsWidth, "Width of the internal graphics buffer. Can be much larger than the screen.")
	flag.IntVar(&graphicsHeight, "gh", defaultGraphicsHeight, "Height of the internal graphics buffer. Can be much larger than the screen.")
	flag.IntVar(&windowWidth, "w", defaultWindowWidth, "Width of the display window")
	flag.IntVar(&windowHeight, "h", defaultWindowHeight, "Height of the display window")
	flag.Parse()

	controlsWidth := int(float64(windowWidth) * controlsRelWidth)
	viewportWidth := windowWidth - controlsWidth

	rl.CloseWindow()

	log.Printf("Storing snapshots at '%s'\n", snapshotsPath)

	os.MkdirAll(snapshotsPath, 0755)
	var err error
	storage, err := NewStorage(snapshotsPath)
	if err != nil {
		log.Printf("Error loading storage: %v\n", err)
		os.Exit(1)
	}

	layout := Layout {
		Monitor:   sg.Rect{X: 0, Y: 0, Width: float32(monitorWidth), Height: float32(monitorHeight)},
		Window:    sg.Rect{X: 0, Y: 0, Width: float32(windowWidth), Height: float32(windowHeight)},
		Controls:  sg.Rect{X: 0, Y: 0, Width: float32(controlsWidth), Height: float32(windowHeight)},
		Viewport:  sg.Rect{X: float32(controlsWidth), Y: 0, Width: float32(viewportWidth), Height: float32(windowHeight)},
		Offscreen: sg.Rect{X: 0, Y: 0, Width: float32(graphicsWidth), Height: float32(graphicsHeight)},
	}

	//rl.SetConfigFlags(rl.FlagMsaa4xHint)
	rl.InitWindow(int32(layout.Window.Width), int32(layout.Window.Height), "sumi sierpinski arrow")
	rl.SetTargetFPS(30)

	env := NewEnv()
	env.Layout = layout
	env.Window = sg.CreateGraphics(layout.Window)
	env.Viewport = sg.CreateGraphics(layout.Viewport)
	env.Offscreen = sg.CreateGraphics(layout.Offscreen)
	env.Controls = sg.CreateGraphics(layout.Controls)
	env.Storage = storage

	return env
}

func main() {

	log := log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile)

	env := Bootstrap()

	rl.SetTraceLogLevel(rl.LogError)

	// reproducable flourescent color cycle
	colorCycle := sg.NewFixedColorCycle(sg.FlourescentColors).Shuffle(0)

	rng := rand.New(rand.NewSource(env.Time.Unix()))
	imageField := NewImageField("/home/d/Dropbox/art/data/david.png")
	noiseField := &SimplexNoiseField{Noise: opensimplex.NewNormalized32(env.Time.Unix())}
	//imageField := NewImageField("/home/d/Dropbox/art/data/ramstatue.png")
	//imageField := NewImageField("/home/d/Dropbox/art/data/bassrockastro/Photo Dec 24 2025, 5 58 23 PM.jpg")
	//imageField := NewImageField("/home/d/Dropbox/art/data/bassrockastro/andromeda.jpg")
	//imageField := NewImageField("/home/d/Dropbox/art/data/moses_statue.jpg")
	//imageLayer := NewImageLayer("/home/d/Dropbox/art/data/moses_statue.jpg")

	field :=
		&TranslateField{
			x: -float32(env.Offscreen.Bounds.Width / 2.0),
			y: -float32(env.Offscreen.Bounds.Height / 2.0),
			field:
				&AdderField{
					fields: []Field{
						&ScaleField{scale: 3, field: imageField},
						&ScaleField{scale: 100, field: noiseField},
					},
				},
		}

	//sierpinskiLayer := &SierpinskiArrow { dirty: true }

	sketch := NewSketch(env)

	fieldColor := colorCycle.Next()
	fmt.Printf("field color = %v\n", fieldColor)

	sketch.AddColorLayer("background-blue", rl.Blue)
	sketch.AddColorLayer("background-black", rl.Black)
	//sketch.AddLayer("blinds", NewBlindsLayer(field))
	//sketch.AddLayer("moses", imageLayer)
	sketch.AddLayer("field", &FieldLayer{field: field, loColor: rl.NewColor(0, 0, 0, 0), hiColor: fieldColor, dirty: true})

	actorColor := color.RGBA { R: 10, G: 58, B: 59, A: 25 }
	fmt.Printf("actor color = %v\n", actorColor)

	contourLayer := NewContourLayer(rng, field, actorColor, -math.Pi/3.0, 12.0 * math.Pi)
	sketch.AddLayer("contours", contourLayer)
	//sketch.AddLayer("sierpinski-arrowhead", sierpinskiLayer)
	//	aurora := NewImageLayer("/home/d/Dropbox/photos/Events/2025/Aurora/Photo Nov 11 2025, 9 52 03 PM.jpg")
	//	sketch.AddLayer("aurora", aurora)
	//	cave := NewImageLayer("/home/d/Dropbox/photos/Events/2025/ Chelsea and James visit Lindell/Photo Nov 29 2025, 5 26 40 PM (29).jpg")
	//	sketch.AddLayer("cave", cave)

	ports := MakePorts()
	ports["sierpinskiArrowAngle"] =
		Sine{
			Amp:  5,
			Freq: 0.1,
			Bias: 60,
		}

	ports["sierpinskiArrowDepth"] =
		Const{
			V: 6,
		}

	ports["sierpinskiArrowLength"] =
		Const{
			V: 8000,
		}

	for !rl.WindowShouldClose() {

		// begin drawing
		t := time.Since(env.Time).Seconds()

		env.Time = time.Now()
		env.Ports = ports.Eval(t)

		sketch.Update(env)

		gui.SetStyle(gui.DEFAULT, gui.BACKGROUND_COLOR, 0x181818FF)
		gui.SetStyle(gui.DEFAULT, gui.BASE_COLOR_NORMAL, 0x2A2A2AFF)
		gui.SetStyle(gui.DEFAULT, gui.BASE_COLOR_FOCUSED, 0x3A3A3AFF)
		gui.SetStyle(gui.DEFAULT, gui.BASE_COLOR_PRESSED, 0x4A4A4AFF)
		gui.SetStyle(gui.DEFAULT, gui.TEXT_COLOR_NORMAL, 0xE0E0E0FF)
		gui.SetStyle(gui.DEFAULT, gui.TEXT_COLOR_FOCUSED, 0xFFFFFFFF)
		gui.SetStyle(gui.DEFAULT, gui.BORDER_COLOR_NORMAL, 0x404040FF)

		/**
		 *	MAIN DRAWING
		 */
		sg.BeginDrawing()

		env.Window.Begin()
		env.Window.Background(rl.GetColor(uint(gui.GetStyle(gui.DEFAULT, gui.BACKGROUND_COLOR))))
		env.Window.End()

		sketch.Draw(env)

		margin := 10
		y := float32(margin)
		minX := float32(margin)
		maxX := float32(env.Layout.Controls.X + env.Layout.Controls.Width - 20)
		sliderWidth := maxX - 0 - 20
		controlRowHeight := 20
		controlRect := env.Layout.Controls
		c := env.Controls
		c.Begin()
		//rl.ClearBackground(rl.GetColor(uint(gui.GetStyle(gui.DEFAULT, gui.BACKGROUND_COLOR))))
		for _, layerTools := range sketch.layerToolsOrdered {

			config := layerTools.config

			//layerTools.texture.Texture

			gui.Label(rl.Rectangle{X: minX, Y: y, Width: controlRect.Width, Height: 24}, layerTools.name)

			y += float32(controlRowHeight + 10)

			config.visible = gui.Toggle(rl.Rectangle{X: minX, Y: y, Width: 16, Height: 16}, "A", config.visible)
			config.a = uint8(gui.Slider(rl.Rectangle{X: minX + 20, Y: y, Width: sliderWidth, Height: 16}, "", "", float32(config.a), 0, 255))

			y += float32(controlRowHeight)

			config.rVisible = gui.Toggle(rl.Rectangle{X: minX, Y: y, Width: 16, Height: 16}, "R", config.rVisible)
			config.r = uint8(gui.Slider(rl.Rectangle{X: minX + 20, Y: y, Width: sliderWidth, Height: 16}, "", "", float32(config.r), 0, 255))

			y += float32(controlRowHeight)

			config.gVisible = gui.Toggle(rl.Rectangle{X: minX, Y: y, Width: 16, Height: 16}, "G", config.gVisible)
			config.g = uint8(gui.Slider(rl.Rectangle{X: minX + 20, Y: y, Width: sliderWidth, Height: 16}, "", "", float32(config.g), 0, 255))

			y += float32(controlRowHeight)

			config.bVisible = gui.Toggle(rl.Rectangle{X: minX, Y: y, Width: 16, Height: 16}, "B", config.bVisible)
			config.b = uint8(gui.Slider(rl.Rectangle{X: minX + 20, Y: y, Width: sliderWidth, Height: 16}, "", "", float32(config.b), 0, 255))

			y += float32(controlRowHeight + 10)
		}

		c.End()

		sg.EndDrawing()

		if rl.IsKeyDown(rl.KeySpace) {
			capture := sketch.Capture(env)
			if _, err := env.Storage.Save(capture); err != nil {
				log.Printf("Error saving snapshot: %v\n", err)
			}
		}

		for ch := rl.GetCharPressed(); ch != 0; ch = rl.GetCharPressed() {
			c := rune(ch)
			if c == 'c' {
				sketch.ResetCamera(env)
			} else if c >= '1' && c <= '9' {
				zoom := 1 << int(ch-'0')
				sketch.cam.Zoom = float32(zoom)
			}
		}

		//rl.EndMode2D()

		// HUD
	}

	rl.CloseWindow()
}
