// Copyright 2010 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 (
	"unsafe"
)

import (
	"github.com/lxn/win"
)

const mainWindowWindowClass = `\o/ Walk_MainWindow_Class \o/`

func init() {
	AppendToWalkInit(func() {
		MustRegisterWindowClass(mainWindowWindowClass)
	})
}

type MainWindow struct {
	FormBase
	windowPlacement *win.WINDOWPLACEMENT
	menu            *Menu
	toolBar         *ToolBar
	statusBar       *StatusBar
}

func NewMainWindow() (*MainWindow, error) {
	return NewMainWindowWithName("")
}

func NewMainWindowWithName(name string) (*MainWindow, error) {
	mw := new(MainWindow)
	mw.SetName(name)

	if err := InitWindow(
		mw,
		nil,
		mainWindowWindowClass,
		win.WS_OVERLAPPEDWINDOW,
		win.WS_EX_CONTROLPARENT); err != nil {

		return nil, err
	}

	succeeded := false
	defer func() {
		if !succeeded {
			mw.Dispose()
		}
	}()

	mw.SetPersistent(true)

	var err error

	if mw.menu, err = newMenuBar(mw); err != nil {
		return nil, err
	}
	if !win.SetMenu(mw.hWnd, mw.menu.hMenu) {
		return nil, lastError("SetMenu")
	}

	tb, err := NewToolBar(mw)
	if err != nil {
		return nil, err
	}
	mw.SetToolBar(tb)

	if mw.statusBar, err = NewStatusBar(mw); err != nil {
		return nil, err
	}
	mw.statusBar.parent = nil
	mw.Children().Remove(mw.statusBar)
	mw.statusBar.parent = mw
	win.SetParent(mw.statusBar.hWnd, mw.hWnd)
	mw.statusBar.visibleChangedPublisher.event.Attach(func() {
		mw.SetBoundsPixels(mw.BoundsPixels())
	})

	succeeded = true

	return mw, nil
}

func (mw *MainWindow) Menu() *Menu {
	return mw.menu
}

func (mw *MainWindow) ToolBar() *ToolBar {
	return mw.toolBar
}

func (mw *MainWindow) SetToolBar(tb *ToolBar) {
	if mw.toolBar != nil {
		win.SetParent(mw.toolBar.hWnd, 0)
	}

	if tb != nil {
		parent := tb.parent
		tb.parent = nil
		parent.Children().Remove(tb)
		tb.parent = mw
		win.SetParent(tb.hWnd, mw.hWnd)
	}

	mw.toolBar = tb
}

func (mw *MainWindow) StatusBar() *StatusBar {
	return mw.statusBar
}

func (mw *MainWindow) ClientBoundsPixels() Rectangle {
	bounds := mw.FormBase.ClientBoundsPixels()

	if mw.toolBar != nil && mw.toolBar.Actions().Len() > 0 {
		tlbBounds := mw.toolBar.BoundsPixels()

		bounds.Y += tlbBounds.Height
		bounds.Height -= tlbBounds.Height
	}

	if mw.statusBar.Visible() {
		bounds.Height -= mw.statusBar.HeightPixels()
	}

	return bounds
}

func (mw *MainWindow) SetVisible(visible bool) {
	if visible {
		win.DrawMenuBar(mw.hWnd)

		mw.clientComposite.RequestLayout()
	}

	mw.FormBase.SetVisible(visible)
}

func (mw *MainWindow) applyFont(font *Font) {
	mw.FormBase.applyFont(font)

	if mw.toolBar != nil {
		mw.toolBar.applyFont(font)
	}

	if mw.statusBar != nil {
		mw.statusBar.applyFont(font)
	}
}

func (mw *MainWindow) Fullscreen() bool {
	return win.GetWindowLong(mw.hWnd, win.GWL_STYLE)&win.WS_OVERLAPPEDWINDOW == 0
}

func (mw *MainWindow) SetFullscreen(fullscreen bool) error {
	if fullscreen == mw.Fullscreen() {
		return nil
	}

	if fullscreen {
		var mi win.MONITORINFO
		mi.CbSize = uint32(unsafe.Sizeof(mi))

		if mw.windowPlacement == nil {
			mw.windowPlacement = new(win.WINDOWPLACEMENT)
		}

		if !win.GetWindowPlacement(mw.hWnd, mw.windowPlacement) {
			return lastError("GetWindowPlacement")
		}
		if !win.GetMonitorInfo(win.MonitorFromWindow(
			mw.hWnd, win.MONITOR_DEFAULTTOPRIMARY), &mi) {

			return newError("GetMonitorInfo")
		}

		if err := mw.ensureStyleBits(win.WS_OVERLAPPEDWINDOW, false); err != nil {
			return err
		}

		if r := mi.RcMonitor; !win.SetWindowPos(
			mw.hWnd, win.HWND_TOP,
			r.Left, r.Top, r.Right-r.Left, r.Bottom-r.Top,
			win.SWP_FRAMECHANGED|win.SWP_NOOWNERZORDER) {

			return lastError("SetWindowPos")
		}
	} else {
		if err := mw.ensureStyleBits(win.WS_OVERLAPPEDWINDOW, true); err != nil {
			return err
		}

		if !win.SetWindowPlacement(mw.hWnd, mw.windowPlacement) {
			return lastError("SetWindowPlacement")
		}

		if !win.SetWindowPos(mw.hWnd, 0, 0, 0, 0, 0, win.SWP_FRAMECHANGED|win.SWP_NOMOVE|
			win.SWP_NOOWNERZORDER|win.SWP_NOSIZE|win.SWP_NOZORDER) {

			return lastError("SetWindowPos")
		}
	}

	return nil
}

func (mw *MainWindow) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case win.WM_WINDOWPOSCHANGED:
		wp := (*win.WINDOWPOS)(unsafe.Pointer(lParam))

		if wp.Flags&win.SWP_NOSIZE != 0 {
			break
		}

		cb := mw.ClientBoundsPixels()

		if mw.toolBar != nil {
			mw.toolBar.SetBoundsPixels(Rectangle{0, 0, cb.Width, mw.toolBar.HeightPixels()})
		}

		mw.statusBar.SetBoundsPixels(Rectangle{0, cb.Y + cb.Height, cb.Width, mw.statusBar.HeightPixels()})

	case win.WM_INITMENUPOPUP:
		mw.menu.updateItemsWithImageForWindow(mw)
	}

	return mw.FormBase.WndProc(hwnd, msg, wParam, lParam)
}
