// 8 february 2014

package ui

import (
	"fmt"
	"syscall"
	"unsafe"
	"sync"
)

const (
	stdWndClassFormat = "gouiwnd%X"
)

var (
	curWndClassNum uintptr
	curWndClassNumLock sync.Mutex
)

var (
	defWindowProc = user32.NewProc("DefWindowProcW")
)

func stdWndProc(s *sysData) func(hwnd _HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) _LRESULT {
	return func(hwnd _HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) _LRESULT {
		switch uMsg {
		case _WM_COMMAND:
			id := _HMENU(wParam.LOWORD())
			s.childrenLock.Lock()
			ss := s.children[id]
			s.childrenLock.Unlock()
			switch ss.ctype {
			case c_button:
				if wParam.HIWORD() == _BN_CLICKED {
					ss.signal()
				}
			}
			return 0
		case _WM_GETMINMAXINFO:
			mm := lParam.MINMAXINFO()
			// ... minimum size
			_ = mm
			return 0
		case _WM_SIZE:
			if s.resize != nil {
				var r _RECT

				r1, _, err := _getClientRect.Call(
					uintptr(hwnd),
					uintptr(unsafe.Pointer(&r)))
				if r1 == 0 {
					panic("GetClientRect failed: " + err.Error())
				}
				// top-left corner is (0,0) so no need for winheight
				s.resizes = s.resizes[0:0]		// set len to 0 without changing cap
				s.resize(int(r.Left), int(r.Top), int(r.Right), int(r.Bottom), &s.resizes)
				// TODO use the Defer movement functions here?
				for _, s := range s.resizes {
					err = s.sysData.setRect(s.x, s.y, s.width, s.height, 0)
					if err != nil {
						panic("child resize failed: " + err.Error())
					}
				}
			}
			return 0
		case _WM_CLOSE:
			s.signal()
			return 0
		default:
			r1, _, _ := defWindowProc.Call(
				uintptr(hwnd),
				uintptr(uMsg),
				uintptr(wParam),
				uintptr(lParam))
			return _LRESULT(r1)
		}
		panic(fmt.Sprintf("stdWndProc message %d did not return: internal bug in ui library", uMsg))
	}
}

type _WNDCLASS struct {
	style				uint32
	lpfnWndProc		uintptr
	cbClsExtra		int32		// originally int
	cbWndExtra		int32		// originally int
	hInstance			_HANDLE
	hIcon			_HANDLE
	hCursor			_HANDLE
	hbrBackground	_HBRUSH
	lpszMenuName	*uint16
	lpszClassName		uintptr
}

var (
	icon, cursor _HANDLE
)

var (
	_registerClass = user32.NewProc("RegisterClassW")
)

func registerStdWndClass(s *sysData) (newClassName string, err error) {
	curWndClassNumLock.Lock()
	newClassName = fmt.Sprintf(stdWndClassFormat, curWndClassNum)
	curWndClassNum++
	curWndClassNumLock.Unlock()

	wc := &_WNDCLASS{
		lpszClassName:	uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(newClassName))),
		lpfnWndProc:		syscall.NewCallback(stdWndProc(s)),
		hInstance:		hInstance,
		hIcon:			icon,
		hCursor:			cursor,
		hbrBackground:	_HBRUSH(_COLOR_BTNFACE + 1),
	}

	ret := make(chan uiret)
	defer close(ret)
	uitask <- &uimsg{
		call:		_registerClass,
		p:		[]uintptr{uintptr(unsafe.Pointer(wc))},
		ret:		ret,
	}
	r := <-ret
	if r.ret == 0 {		// failure
		return "", r.err
	}
	return newClassName, nil
}

func initWndClassInfo() (err error) {
	const (
		_IDI_APPLICATION = 32512
		_IDC_ARROW = 32512
	)

	r1, _, err := user32.NewProc("LoadIconW").Call(
		uintptr(_NULL),
		uintptr(_IDI_APPLICATION))
	if r1 == 0 {		// failure
		return fmt.Errorf("error getting window icon: %v", err)
	}
	icon = _HANDLE(r1)

	r1, _, err = user32.NewProc("LoadCursorW").Call(
		uintptr(_NULL),
		uintptr(_IDC_ARROW))
	if r1 == 0 {		// failure
		return fmt.Errorf("error getting window cursor: %v", err)
	}
	cursor = _HANDLE(r1)

	return nil
}