package numeric

import (
	"galaxy/client/widget/validator"
	"galaxy/util"
	"strconv"
	"strings"
	"unicode/utf8"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/driver/mobile"
	"fyne.io/fyne/v2/widget"
)

type FloatEntry struct {
	widget.Entry
	origin    float64
	MaxValue  float64
	maxSize   uint
	validator fyne.StringValidator
	Valid     bool
}

type IntEntry struct {
	widget.Entry
	origin    uint
	MaxValue  uint
	maxSize   uint
	validator fyne.StringValidator
	Valid     bool
}

func NewFloatEntry(maxSize uint, onChanged func(string)) *FloatEntry {
	e := &FloatEntry{maxSize: maxSize, validator: validator.FloatEntryValidator}
	e.ExtendBaseWidget(e)
	e.Entry.Scroll = fyne.ScrollNone
	e.Entry.TextStyle = fyne.TextStyle{Monospace: true}
	// e.Validator = validator.FloatEntryValidator
	// e.AlwaysShowValidationError = true
	e.Entry.ActionItem = nil
	e.SetOrigin(0)
	e.Validate()
	e.Entry.OnChanged = onChanged
	return e
}

func NewIntEntry(maxSize uint, onChanged func(string)) *IntEntry {
	e := &IntEntry{maxSize: maxSize, validator: validator.IntEntryValidator}
	e.ExtendBaseWidget(e)
	e.Entry.Scroll = fyne.ScrollNone
	e.Entry.TextStyle = fyne.TextStyle{Monospace: true}
	// e.Validator = validator.IntEntryValidator
	// e.AlwaysShowValidationError = true
	e.Entry.ActionItem = nil
	e.SetOrigin(0)
	e.Validate()
	e.Entry.OnChanged = onChanged
	return e
}

func (e *FloatEntry) CreateRenderer() fyne.WidgetRenderer {
	r := e.Entry.CreateRenderer()
	return r
}

func (e *FloatEntry) TypedRune(r rune) {
	if !((r >= '0' && r <= '9') || r == '.') {
		return
	}
	if !lengthBelowLimit(e.Entry.Text, e.maxSize) && e.Entry.SelectedText() == "" {
		return
	}
	if r == '.' && strings.Contains(e.Entry.Text, ".") {
		return
	}
	e.Entry.TypedRune(r)
}

func (e *FloatEntry) TypedShortcut(shortcut fyne.Shortcut) {
	paste, ok := shortcut.(*fyne.ShortcutPaste)
	if !ok {
		e.Entry.TypedShortcut(shortcut)
		return
	}

	content := paste.Clipboard.Content()
	if _, err := strconv.ParseFloat(content, 64); err == nil {
		e.Entry.TypedShortcut(shortcut)
	}
}

func (e *FloatEntry) Keyboard() mobile.KeyboardType {
	return mobile.NumberKeyboard
}

func (e *FloatEntry) SetOrigin(v float64) {
	if v < 0 {
		return
	}
	e.origin = v
	e.Reset()
}

func (e *FloatEntry) Reset() {
	e.SetValue(e.origin)
}

func (e *FloatEntry) Clear() {
	onChanged := e.Entry.OnChanged
	e.Entry.OnChanged = nil
	e.Entry.SetText("")
	e.Entry.OnChanged = onChanged
}

func (e *FloatEntry) SetValue(v float64) {
	if v < 0 {
		return
	}

	e.Entry.SetText(strconv.FormatFloat(util.Fixed3(v), 'f', -1, 64))
}

func (e *FloatEntry) Value() (float64, bool) {
	if v, err := validator.ParseFloat(e.Entry.Text); err != nil {
		return 0, false
	} else {
		return v, true
	}
}

func (e *FloatEntry) Overriden() bool {
	if v, ok := e.Value(); !ok {
		return false
	} else {
		return util.Fixed3(v) != util.Fixed3(e.origin)
	}
}

func (e *FloatEntry) Validate() {
	if e.validator == nil {
		return
	}
	err := e.validator(e.Entry.Text)
	e.Valid = err == nil
}

func (e *IntEntry) TypedRune(r rune) {
	if r >= '0' && r <= '9' {
		if lengthBelowLimit(e.Entry.Text, e.maxSize) || e.Entry.SelectedText() != "" {
			e.Entry.TypedRune(r)
		}
	}
}

func (e *IntEntry) TypedShortcut(shortcut fyne.Shortcut) {
	paste, ok := shortcut.(*fyne.ShortcutPaste)
	if !ok {
		e.Entry.TypedShortcut(shortcut)
		return
	}

	content := paste.Clipboard.Content()
	if _, err := strconv.ParseInt(content, 10, 64); err == nil {
		e.Entry.TypedShortcut(shortcut)
	}
}

func (e *IntEntry) Keyboard() mobile.KeyboardType {
	return mobile.NumberKeyboard
}

func (e *IntEntry) SetOrigin(v int) {
	if v < 0 {
		return
	}
	e.origin = uint(v)
	e.Reset()
}

func (e *IntEntry) Reset() {
	e.SetValue(int(e.origin))
}

func (e *IntEntry) SetValue(v int) {
	if v < 0 {
		return
	}
	e.Entry.SetText(strconv.Itoa(v))
}

func (e *IntEntry) Value() (int, bool) {
	if v, err := validator.ParseInt(e.Entry.Text); err != nil {
		return 0, false
	} else {
		return v, true
	}
}

func (e *IntEntry) Overriden() bool {
	if v, ok := e.Value(); !ok {
		return false
	} else {
		return v != int(e.origin)
	}
}

func (e *IntEntry) Validate() {
	if e.validator == nil {
		return
	}
	err := e.validator(e.Entry.Text)
	e.Valid = err == nil
}

func lengthBelowLimit(s string, max uint) bool {
	return utf8.RuneCountInString(s) < int(max)
}
