// Copyright 2016 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

package walk

import (
	"strconv"

	"github.com/lxn/win"
)

type Slider struct {
	WidgetBase
	valueChangedPublisher EventPublisher
	layoutFlags           LayoutFlags
	tracking              bool
	persistent            bool
}

type SliderCfg struct {
	Orientation    Orientation
	ToolTipsHidden bool
}

func NewSlider(parent Container) (*Slider, error) {
	return NewSliderWithOrientation(parent, Horizontal)
}

func NewSliderWithOrientation(parent Container, orientation Orientation) (*Slider, error) {
	return NewSliderWithCfg(parent, &SliderCfg{Orientation: orientation})
}

func NewSliderWithCfg(parent Container, cfg *SliderCfg) (*Slider, error) {
	sl := new(Slider)

	var style uint32 = win.WS_TABSTOP | win.WS_VISIBLE
	if cfg.Orientation == Vertical {
		style |= win.TBS_VERT
		sl.layoutFlags = ShrinkableVert | GrowableVert
	} else {
		sl.layoutFlags = ShrinkableHorz | GrowableHorz
	}
	if !cfg.ToolTipsHidden {
		style |= win.TBS_TOOLTIPS
	}

	if err := InitWidget(
		sl,
		parent,
		"msctls_trackbar32",
		style,
		0); err != nil {
		return nil, err
	}

	sl.SetBackground(nullBrushSingleton)

	sl.GraphicsEffects().Add(InteractionEffect)
	sl.GraphicsEffects().Add(FocusEffect)

	sl.MustRegisterProperty("Value", NewProperty(
		func() interface{} {
			return sl.Value()
		},
		func(v interface{}) error {
			sl.SetValue(assertIntOr(v, 0))
			return nil
		},
		sl.valueChangedPublisher.Event()))

	return sl, nil
}

func (sl *Slider) MinValue() int {
	return int(sl.SendMessage(win.TBM_GETRANGEMIN, 0, 0))
}

func (sl *Slider) MaxValue() int {
	return int(sl.SendMessage(win.TBM_GETRANGEMAX, 0, 0))
}

func (sl *Slider) SetRange(min, max int) {
	sl.SendMessage(win.TBM_SETRANGEMIN, 0, uintptr(min))
	sl.SendMessage(win.TBM_SETRANGEMAX, 1, uintptr(max))
}

func (sl *Slider) Value() int {
	return int(sl.SendMessage(win.TBM_GETPOS, 0, 0))
}

func (sl *Slider) SetValue(value int) {
	sl.SendMessage(win.TBM_SETPOS, 1, uintptr(value))
	sl.valueChangedPublisher.Publish()
}

// ValueChanged returns an Event that can be used to track changes to Value.
func (sl *Slider) ValueChanged() *Event {
	return sl.valueChangedPublisher.Event()
}

func (sl *Slider) Persistent() bool {
	return sl.persistent
}

func (sl *Slider) SetPersistent(value bool) {
	sl.persistent = value
}

func (sl *Slider) SaveState() error {
	return sl.WriteState(strconv.Itoa(sl.Value()))
}

func (sl *Slider) RestoreState() error {
	s, err := sl.ReadState()
	if err != nil {
		return err
	}

	value, err := strconv.Atoi(s)
	if err != nil {
		return err
	}

	sl.SetValue(value)

	return nil
}

func (sl *Slider) LineSize() int {
	return int(sl.SendMessage(win.TBM_GETLINESIZE, 0, 0))
}

func (sl *Slider) SetLineSize(lineSize int) {
	sl.SendMessage(win.TBM_SETLINESIZE, 0, uintptr(lineSize))
}

func (sl *Slider) PageSize() int {
	return int(sl.SendMessage(win.TBM_GETPAGESIZE, 0, 0))
}

func (sl *Slider) SetPageSize(pageSize int) {
	sl.SendMessage(win.TBM_SETPAGESIZE, 0, uintptr(pageSize))
}

func (sl *Slider) Tracking() bool {
	return sl.tracking
}

func (sl *Slider) SetTracking(tracking bool) {
	sl.tracking = tracking
}

func (sl *Slider) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case win.WM_HSCROLL, win.WM_VSCROLL:
		switch win.LOWORD(uint32(wParam)) {
		case win.TB_THUMBPOSITION, win.TB_ENDTRACK:
			sl.valueChangedPublisher.Publish()

		case win.TB_THUMBTRACK:
			if sl.tracking {
				sl.valueChangedPublisher.Publish()
			}
		}
		return 0
	}
	return sl.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
}

func (*Slider) NeedsWmSize() bool {
	return true
}

func (sl *Slider) CreateLayoutItem(ctx *LayoutContext) LayoutItem {
	return &sliderLayoutItem{
		layoutFlags: sl.layoutFlags,
		idealSize:   sl.dialogBaseUnitsToPixels(Size{15, 15}),
	}
}

type sliderLayoutItem struct {
	LayoutItemBase
	layoutFlags LayoutFlags
	idealSize   Size // in native pixels
}

func (li *sliderLayoutItem) LayoutFlags() LayoutFlags {
	return li.layoutFlags
}

func (li *sliderLayoutItem) IdealSize() Size {
	return li.idealSize
}

func (li *sliderLayoutItem) MinSize() Size {
	return li.idealSize
}
