package main

import (
	"fmt"
	"log"

	"opslag.de/schobers/krampus19/gut"

	"opslag.de/schobers/geom"

	"opslag.de/schobers/allg5"
	"opslag.de/schobers/krampus19/alui"
)

const margin = 8

type displayModeControl struct {
	alui.StackPanel

	label alui.Label

	modes   []string
	current int
}

func newDisplayModeControl(ctx *Context) *displayModeControl {
	var modes []string
	fmtDisplayMode := func(width, height int) string { return fmt.Sprintf("%d x %d", width, height) }
	containsDisplayMode := func(mode string) bool {
		for _, m := range modes {
			if m == mode {
				return true
			}
		}
		return false
	}

	displayMode := ctx.Settings.Video.DisplayMode
	if displayMode == "" {
		displayMode = fmtDisplayMode(ctx.DisplaySize.X, ctx.DisplaySize.Y)
	}
	var current int
	for _, m := range allg5.DisplayModes() {
		mode := fmtDisplayMode(m.Width, m.Height)
		if containsDisplayMode(mode) {
			continue
		}
		if mode == displayMode {
			current = len(modes)
		}
		modes = append(modes, mode)
	}

	c := &displayModeControl{modes: modes, current: current}
	c.Orientation = alui.OrientationHorizontal
	c.label.TextAlign = allg5.AlignCenter
	c.selectMode(0)

	c.AddChild(newSpriteButton(ctx, "ui", "angle-left", func() { c.selectMode(-1) }))
	c.AddChild(&c.label)
	c.AddChild(newSpriteButton(ctx, "ui", "angle-right", func() { c.selectMode(1) }))
	return c
}

func (c *displayModeControl) selectMode(delta int) {
	c.current = (c.current + delta + len(c.modes)) % len(c.modes)
	c.label.Text = c.Mode()
}

func (c *displayModeControl) Mode() string { return c.modes[c.current] }

type checkBox struct {
	alui.Button

	ctx *Context

	Selected bool
}

func newCheckBox(ctx *Context) *checkBox {
	b := &checkBox{ctx: ctx}
	b.OnClick = func() {
		b.Selected = !b.Selected
	}
	return b
}

func (b *checkBox) DesiredSize(ctx *alui.Context) geom.PointF32 {
	return b.ctx.SpriteDrawer.Size("ui", "check-square").Add2D(2*margin, 2*margin)
}

func (b *checkBox) Render(ctx *alui.Context, bounds geom.RectangleF32) {
	tint := b.ctx.Palette.Primary
	if b.Over {
		tint = b.ctx.Palette.Dark
		ctx.Cursor = allg5.MouseCursorLink
	}
	var part = "square"
	if b.Selected {
		part = "check-square"
	}
	b.ctx.SpriteDrawer.Draw("ui", part, bounds.Center(), DrawSpriteOptions{Tint: &tint})
}

type spriteButton struct {
	alui.Button

	ctx *Context

	Sprite string
	Part   string
}

func newSpriteButton(ctx *Context, sprite, part string, onClick func()) *spriteButton {
	b := &spriteButton{ctx: ctx, Sprite: sprite, Part: part}
	b.OnClick = onClick
	return b
}

func (b *spriteButton) DesiredSize(ctx *alui.Context) geom.PointF32 {
	return b.ctx.SpriteDrawer.Size(b.Sprite, b.Part).Add2D(2*margin, 2*margin)
}

func (b *spriteButton) Render(ctx *alui.Context, bounds geom.RectangleF32) {
	tint := b.ctx.Palette.Primary
	if b.Over {
		tint = b.ctx.Palette.Dark
		ctx.Cursor = allg5.MouseCursorLink
	}
	b.ctx.SpriteDrawer.Draw(b.Sprite, b.Part, bounds.Center(), DrawSpriteOptions{Tint: &tint})
}

type selectKeyControl struct {
	alui.Label

	Key allg5.Key

	WaitingForInput bool
}

func newSelectKeyControl(key allg5.Key) *selectKeyControl {
	c := &selectKeyControl{Key: key}
	c.TextAlign = allg5.AlignCenter
	return c
}

func (c *selectKeyControl) Handle(e allg5.Event) {
	c.Label.Handle(e)

	switch e := e.(type) {
	case *allg5.MouseButtonDownEvent:
		if c.Over && e.Button == allg5.MouseButtonLeft {
			c.WaitingForInput = true
		}
	case *allg5.KeyDownEvent:
		if c.WaitingForInput {
			if e.KeyCode != allg5.KeyEscape {
				c.Key = e.KeyCode
			}
			c.WaitingForInput = false
		}
	}
}

func (c *selectKeyControl) Render(ctx *alui.Context, bounds geom.RectangleF32) {
	if c.WaitingForInput {
		c.Text = "_"
	} else {
		c.Text = gut.KeyToString(c.Key)
	}
	if c.Over {
		ctx.Cursor = allg5.MouseCursorLink
	}
	c.Label.Render(ctx, bounds)
}

type settingsHeader struct {
	alui.Label
}

func newSettingsHeader(label string) alui.Control {
	header := &settingsHeader{}
	header.Font = "header"
	header.Text = label
	return alui.NewMargins(header, 3*margin, 0, 2*margin)
}

type settingsRow struct {
	alui.StackPanel

	label *alui.Label
	edit  alui.Control
}

func newSettingRow(label string, edit alui.Control) *settingsRow {
	row := &settingsRow{}
	row.Orientation = alui.OrientationHorizontal
	row.label = &alui.Label{Text: label}
	row.edit = edit
	row.Children = append(row.Children, row.label, row.edit)
	return row
}

func (r *settingsRow) Layout(ctx *alui.Context, bounds geom.RectangleF32) {
	label := bounds
	label.Max.X = label.Min.X + .5*label.Dx()
	r.label.Layout(ctx, label)

	width := r.edit.DesiredSize(ctx).X
	edit := bounds
	if geom.IsNaN32(width) || width > label.Dx() {
		edit.Min.X = label.Max.X
	} else {
		margin := (label.Dx() - width) * .5
		edit.Min.X = label.Max.X + margin
		edit.Max.X -= margin
	}
	r.edit.Layout(ctx, edit)
}

type wideButton struct {
	alui.Button
}

func newWideButton(label string, onClick func()) *wideButton {
	b := &wideButton{}
	b.Text = label
	b.TextAlign = allg5.AlignCenter
	b.OnClick = onClick
	return b
}

func (b *wideButton) DesiredSize(ctx *alui.Context) geom.PointF32 {
	size := b.Button.DesiredSize(ctx)
	size.X += 2 * margin
	return size
}

type changeSettings struct {
	alui.Column

	ctx *Context

	rows   []*settingsRow
	active int
}

func (s *changeSettings) addRow(row ...*settingsRow) {
	for _, row := range row {
		s.AddChild(row)
		s.rows = append(s.rows, row)
	}
}

func (s *changeSettings) Enter(ctx *Context) error {
	s.Init()
	s.ctx = ctx

	keyLeft := newSelectKeyControl(s.ctx.Settings.Controls.MoveLeft)
	keyUp := newSelectKeyControl(s.ctx.Settings.Controls.MoveUp)
	keyRight := newSelectKeyControl(s.ctx.Settings.Controls.MoveRight)
	keyDown := newSelectKeyControl(s.ctx.Settings.Controls.MoveDown)

	s.AddChild(newSettingsHeader("Controls"))
	s.addRow(
		newSettingRow("Key left", keyLeft),
		newSettingRow("Key up", keyUp),
		newSettingRow("Key right", keyRight),
		newSettingRow("Key down", keyDown),
	)
	s.AddChild(newSettingsHeader("Video"))

	displayMode := newDisplayModeControl(ctx)
	windowed := newCheckBox(ctx)
	windowed.Selected = s.ctx.Settings.Video.Windowed

	s.addRow(
		newSettingRow("Windowed", windowed),
		newSettingRow("Resolution (fullscreen)", displayMode),
	)
	buttons := &alui.StackPanel{Orientation: alui.OrientationHorizontal}
	buttons.AddChild(
		newWideButton("Cancel", func() { s.ctx.Navigation.ShowMainMenu() }),
		newWideButton("OK", func() {
			var controls = controls{MoveLeft: keyLeft.Key, MoveUp: keyUp.Key, MoveRight: keyRight.Key, MoveDown: keyDown.Key}
			var video = video{Windowed: windowed.Selected, DisplayMode: displayMode.Mode()}
			s.ctx.Settings = settings{Controls: controls, Video: video}
			err := s.ctx.Settings.StoreDefault()
			if err != nil {
				log.Printf("User settings are not stored: %v", err)
			} else {
				log.Printf("Stored new settings.")
			}

			s.ctx.Navigation.ShowMainMenu()
		}),
	)
	s.AddChild(alui.Center(buttons))
	return nil
}

func (s *changeSettings) Handle(e allg5.Event) {
	s.Column.Handle(e)

	switch e := e.(type) {
	case *allg5.KeyDownEvent:
		switch e.KeyCode {
		case allg5.KeyEscape:
			s.ctx.Navigation.ShowMainMenu()
		}
	}
}

func (s *changeSettings) Leave() {
}
