package window_maker

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

// Import necessary DLLs
var (
	user32                         = syscall.NewLazyDLL("user32.dll")
	gdi32                          = syscall.NewLazyDLL("gdi32.dll")
	kernel32                       = syscall.NewLazyDLL("kernel32.dll")
	procRegisterClassEx            = user32.NewProc("RegisterClassExW")
	procCreateWindowEx             = user32.NewProc("CreateWindowExW")
	procDefWindowProc              = user32.NewProc("DefWindowProcW")
	procDispatchMessage            = user32.NewProc("DispatchMessageW")
	procGetMessage                 = user32.NewProc("GetMessageW")
	procTranslateMessage           = user32.NewProc("TranslateMessage")
	procShowWindow                 = user32.NewProc("ShowWindow")
	procUpdateWindow               = user32.NewProc("UpdateWindow")
	procSetLayeredWindowAttributes = user32.NewProc("SetLayeredWindowAttributes")
	procSetWindowLongPtr           = user32.NewProc("SetWindowLongPtrW")
	procGetWindowLongPtr           = user32.NewProc("GetWindowLongPtrW")
	procSetWindowPos               = user32.NewProc("SetWindowPos")
	procPostQuitMessage            = user32.NewProc("PostQuitMessage")
	procDestroyWindow              = user32.NewProc("DestroyWindow")
	procLoadCursorW                = user32.NewProc("LoadCursorW")
	procAdjustWindowRectEx         = user32.NewProc("AdjustWindowRectEx")
	procGetSystemMetrics           = user32.NewProc("GetSystemMetrics")
	procGetDC                      = user32.NewProc("GetDC")
	procReleaseDC                  = user32.NewProc("ReleaseDC")
	procInvalidateRect             = user32.NewProc("InvalidateRect")
	procBeginPaint                 = user32.NewProc("BeginPaint")
	procEndPaint                   = user32.NewProc("EndPaint")
	procCreateSolidBrush           = gdi32.NewProc("CreateSolidBrush")
	procSelectObject               = gdi32.NewProc("SelectObject")
	procDeleteObject               = gdi32.NewProc("DeleteObject")
	procRectangle                  = gdi32.NewProc("Rectangle") // Ensure this line exists
)

// Windows API constants
const (
	WS_OVERLAPPEDWINDOW = 0x00CF0000
	WS_VISIBLE          = 0x10000000
	WS_CAPTION          = 0x00C00000
	WS_SYSMENU          = 0x00080000
	WS_THICKFRAME       = 0x00040000
	WS_MINIMIZEBOX      = 0x00020000
	WS_MAXIMIZEBOX      = 0x00010000
	WS_EX_LAYERED       = 0x00080000
	LWA_ALPHA           = 0x2
	SW_SHOWNORMAL       = 1

	SWP_NOMOVE       = 0x0002
	SWP_NOSIZE       = 0x0001
	SWP_NOZORDER     = 0x0004
	SWP_FRAMECHANGED = 0x0020

	IDC_ARROW = 32512 // Standard arrow cursor identifier

	SW_SHOW = 5

	SM_CXSCREEN = 0
	SM_CYSCREEN = 1
)

// RECT structure
type RECT struct {
	Left, Top, Right, Bottom int32
}

// POINT structure
type POINT struct {
	X, Y int32
}

// WNDCLASSEX structure
type WNDCLASSEX struct {
	CbSize        uint32
	Style         uint32
	WndProc       uintptr
	CbClsExtra    int32
	CbWndExtra    int32
	HInstance     syscall.Handle
	HIcon         syscall.Handle
	HCursor       syscall.Handle
	HbrBackground syscall.Handle
	LpszMenuName  *uint16
	LpszClassName *uint16
	HIconSm       syscall.Handle
}

// MSG structure
type MSG struct {
	HWnd    uintptr
	Message uint32
	WParam  uintptr
	LParam  uintptr
	Time    uint32
	Pt      POINT
}

// Window structure represents a window
type Window struct {
	hwnd        uintptr
	title       string
	width       int
	height      int
	canResize   bool
	mu          sync.Mutex
	shouldClose bool

	// Rectangle properties
	rectX      int
	rectY      int
	rectWidth  int
	rectHeight int
	rectMu     sync.Mutex
}

// Global variables for the current window (assuming only one window)
var currentWindow *Window
var windowMutex sync.Mutex

// Define negative constants as variables to avoid uintptr overflow
var (
	GWL_STYLE   = int32(-16)
	GWL_EXSTYLE = int32(-20)
)

// windowWndProc is the window procedure that handles messages
func windowWndProc(hwnd uintptr, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case 0x0010: // WM_CLOSE
		procDestroyWindow.Call(hwnd)
		return 0
	case 0x0002: // WM_DESTROY
		procPostQuitMessage.Call(0)
		return 0
	case 0x000F: // WM_PAINT
		return handleWM_PAINT(hwnd)
	default:
		ret, _, _ := procDefWindowProc.Call(hwnd, uintptr(msg), wParam, lParam)
		return ret
	}
}

// handleWM_PAINT handles the WM_PAINT message
func handleWM_PAINT(hwnd uintptr) uintptr {
	var ps struct {
		Hdc           uintptr
		FErase        int32
		RcPaint       RECT
		RcWindow      RECT
		Flags         uint32
		EraseBkMode   uint32
		FillAttribute uint32
	}
	ret, _, _ := procBeginPaint.Call(hwnd, uintptr(unsafe.Pointer(&ps)))
	if ret == 0 {
		return 0
	}
	hdc := ret

	// Get the current window instance
	windowMutex.Lock()
	win := currentWindow
	windowMutex.Unlock()
	if win == nil {
		procEndPaint.Call(hwnd, uintptr(unsafe.Pointer(&ps)))
		return 0
	}

	// Lock access to rectangle properties
	win.rectMu.Lock()
	x := win.rectX
	y := win.rectY
	width := win.rectWidth
	height := win.rectHeight
	win.rectMu.Unlock()

	// Create a solid brush (red color: RGB(255,0,0))
	redColor := uintptr(0x0000FF) // COLORREF is 0x00BBGGRR
	hBrush, _, _ := procCreateSolidBrush.Call(redColor)
	if hBrush == 0 {
		procEndPaint.Call(hwnd, uintptr(unsafe.Pointer(&ps)))
		return 0
	}

	// Select the brush into the device context (DC)
	oldBrush, _, _ := procSelectObject.Call(hdc, hBrush)

	// Draw the rectangle
	procRectangle.Call(hdc, uintptr(x), uintptr(y), uintptr(x+width), uintptr(y+height))

	// Restore the old brush
	procSelectObject.Call(hdc, oldBrush)

	// Delete the created brush
	procDeleteObject.Call(hBrush)

	// End painting
	procEndPaint.Call(hwnd, uintptr(unsafe.Pointer(&ps)))

	return 0
}

// NewWindow creates a new window with the specified parameters and centers it on the screen
func NewWindow(title string, width, height int, canResize bool) (*Window, error) {
	windowMutex.Lock()
	defer windowMutex.Unlock()

	if currentWindow != nil {
		return nil, fmt.Errorf("only one window is supported")
	}

	window := &Window{
		title:      title,
		width:      width,
		height:     height,
		canResize:  canResize,
		rectX:      50, // Initial rectangle position
		rectY:      50,
		rectWidth:  200, // Initial rectangle size
		rectHeight: 100,
	}

	// Set the global current window
	currentWindow = window

	// Generate a unique class name
	className := syscall.StringToUTF16Ptr(fmt.Sprintf("GoWindowClass-%p", window))

	// Load the standard arrow cursor
	cursor, _, errCursor := procLoadCursorW.Call(0, uintptr(IDC_ARROW))
	if cursor == 0 {
		return nil, fmt.Errorf("failed to load cursor: %v", errCursor)
	}

	// Define the window class
	wc := WNDCLASSEX{
		CbSize:        uint32(unsafe.Sizeof(WNDCLASSEX{})),
		Style:         0,
		WndProc:       syscall.NewCallback(windowWndProc),
		CbClsExtra:    0,
		CbWndExtra:    0,
		HInstance:     0, // NULL
		HIcon:         0, // Default icon
		HCursor:       syscall.Handle(cursor),
		HbrBackground: syscall.Handle(5), // COLOR_WINDOW + 1
		LpszMenuName:  nil,
		LpszClassName: className,
		HIconSm:       0,
	}

	// Register the window class
	ret, _, errReg := procRegisterClassEx.Call(uintptr(unsafe.Pointer(&wc)))
	if ret == 0 {
		return nil, fmt.Errorf("failed to register window class: %v", errReg)
	}

	// Define window styles
	windowStyle := WS_OVERLAPPEDWINDOW | WS_VISIBLE
	if !canResize {
		windowStyle &^= WS_THICKFRAME | WS_MAXIMIZEBOX
	}

	// Convert window title to UTF16
	titleUTF16, err := syscall.UTF16PtrFromString(title)
	if err != nil {
		return nil, fmt.Errorf("error converting title to UTF16: %v", err)
	}

	// Adjust window size based on styles
	var rect RECT
	rect.Left = 0
	rect.Top = 0
	rect.Right = int32(width)
	rect.Bottom = int32(height)

	exStyle := uint32(0)
	if canResize {
		exStyle |= WS_EX_LAYERED
	}

	retAdjust, _, errAdjust := procAdjustWindowRectEx.Call(
		uintptr(unsafe.Pointer(&rect)),
		uintptr(windowStyle),
		0, // No menu
		uintptr(exStyle),
	)
	if retAdjust == 0 {
		return nil, fmt.Errorf("failed to adjust window size: %v", errAdjust)
	}

	actualWidth := int(rect.Right - rect.Left)
	actualHeight := int(rect.Bottom - rect.Top)

	// Get screen dimensions
	screenWidth, _, _ := procGetSystemMetrics.Call(SM_CXSCREEN)
	screenHeight, _, _ := procGetSystemMetrics.Call(SM_CYSCREEN)

	// Calculate position to center the window
	x := (int(screenWidth) - actualWidth) / 2
	y := (int(screenHeight) - actualHeight) / 2

	// Create the window
	hwnd, _, errCreate := procCreateWindowEx.Call(
		uintptr(0),                          // Extended window styles
		uintptr(unsafe.Pointer(className)),  // Window class name
		uintptr(unsafe.Pointer(titleUTF16)), // Window title
		uintptr(windowStyle),                // Window styles
		uintptr(x),                          // X position
		uintptr(y),                          // Y position
		uintptr(actualWidth),                // Window width
		uintptr(actualHeight),               // Window height
		0,                                   // Parent window
		0,                                   // Menu
		0,                                   // Instance handle
		0,                                   // Additional application data
	)
	if hwnd == 0 {
		return nil, fmt.Errorf("failed to create window: %v", errCreate)
	}

	window.hwnd = hwnd

	// **Important:** Explicitly show and update the window
	procShowWindow.Call(hwnd, uintptr(SW_SHOWNORMAL))
	procUpdateWindow.Call(hwnd)

	return window, nil
}

// Show displays the window and starts the message processing loop
func (w *Window) Show() {
	// Lock the OS thread to ensure the message loop runs on a single thread
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	w.messageLoop()
}

// messageLoop processes window messages
func (w *Window) messageLoop() {
	var msg MSG
	for {
		ret, _, _ := procGetMessage.Call(
			uintptr(unsafe.Pointer(&msg)),
			0, // HWND (0: messages for any window of the current thread)
			0,
			0,
		)
		if ret == 0 { // WM_QUIT
			w.mu.Lock()
			w.shouldClose = true
			w.mu.Unlock()
			break
		} else if ret == ^uintptr(0) { // Error (-1)
			break
		} else {
			procTranslateMessage.Call(uintptr(unsafe.Pointer(&msg)))
			procDispatchMessage.Call(uintptr(unsafe.Pointer(&msg)))
		}
	}
}

// ShouldClose checks if the window should close
func (w *Window) ShouldClose() bool {
	w.mu.Lock()
	defer w.mu.Unlock()
	return w.shouldClose
}

// Close closes the window
func (w *Window) Close() {
	procDestroyWindow.Call(w.hwnd)
}

// Transparented sets the window transparency (alpha from 0.0 to 1.0)
func (w *Window) Transparented(alpha float32) error {
	w.mu.Lock()
	defer w.mu.Unlock()

	// Get the current extended window style
	currentExStyle, _, err := procGetWindowLongPtr.Call(
		w.hwnd,
		uintptr(GWL_EXSTYLE),
	)
	if currentExStyle == 0 && err != nil && err != syscall.Errno(0) {
		return fmt.Errorf("failed to get current extended window style: %v", err)
	}

	// Set the WS_EX_LAYERED flag
	newExStyle := int32(currentExStyle) | WS_EX_LAYERED
	_, _, errSet := procSetWindowLongPtr.Call(
		w.hwnd,
		uintptr(GWL_EXSTYLE),
		uintptr(newExStyle),
	)
	if errSet != nil && errSet != syscall.Errno(0) {
		return fmt.Errorf("failed to set extended window style: %v", errSet)
	}

	// Set transparency
	alphaByte := byte(alpha * 255)
	ret, _, errSetAttributes := procSetLayeredWindowAttributes.Call(
		w.hwnd,             // HWND
		0,                  // crKey
		uintptr(alphaByte), // bAlpha
		uintptr(LWA_ALPHA), // dwFlags
	)
	if ret == 0 {
		return fmt.Errorf("failed to set layered window attributes: %v", errSetAttributes)
	}

	return nil
}

// Decorated enables or disables window decorations
func (w *Window) Decorated(decorate bool) error {
	w.mu.Lock()
	defer w.mu.Unlock()

	// Get the current window style
	currentStyle, _, err := procGetWindowLongPtr.Call(
		w.hwnd,
		uintptr(GWL_STYLE),
	)
	if currentStyle == 0 && err != nil && err != syscall.Errno(0) {
		return fmt.Errorf("failed to get current window style: %v", err)
	}

	var newStyle uint32
	if decorate {
		// Re-add all window styles that were removed
		newStyle = uint32(currentStyle) | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
	} else {
		// Remove window decoration styles
		newStyle = uint32(currentStyle) &^ (WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
	}

	_, _, errSet := procSetWindowLongPtr.Call(
		w.hwnd,
		uintptr(GWL_STYLE),
		uintptr(newStyle),
	)
	if errSet != nil && errSet != syscall.Errno(0) {
		return fmt.Errorf("failed to set window style: %v", errSet)
	}

	// Update the window style to apply changes
	_, _, _ = procSetWindowPos.Call(
		w.hwnd,
		0,
		0,
		0,
		0,
		0,
		uintptr(SWP_NOZORDER|SWP_FRAMECHANGED),
	)

	return nil
}

// Invalidate redraws the client area of the window
func (w *Window) Invalidate() error {
	ret, _, err := procInvalidateRect.Call(
		w.hwnd,
		0, // RECT (0: entire client area)
		1, // TRUE: clear the area
	)
	if ret == 0 {
		return fmt.Errorf("failed to invalidate window: %v", err)
	}
	return nil
}

// SetRectangle sets the position and size of the rectangle to be drawn
func (w *Window) SetRectangle(x, y, width, height int) {
	w.rectMu.Lock()
	w.rectX = x
	w.rectY = y
	w.rectWidth = width
	w.rectHeight = height
	w.rectMu.Unlock()

	// Request window redraw
	err := w.Invalidate()
	if err != nil {
		fmt.Printf("Error invalidating window: %v\n", err)
	}
}


fatal error: all goroutines are asleep - deadlock!

goroutine 1 [sync.Mutex.Lock, locked to thread]:
sync.runtime_SemacquireMutex(0x0?, 0x58?, 0xc0001075c8?)
        C:/Users/79685/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.6.windows-amd64/src/runtime/sema.go:77 +0x25
sync.(*Mutex).lockSlow(0xe22c68)

        C:/Users/79685/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.6.windows-amd64/src/sync/mutex.go:171 +0x15d
sync.(*Mutex).Lock(...)

        C:/Users/79685/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.6.windows-amd64/src/sync/mutex.go:90
wallpeper/window_maker.handleWM_PAINT(0x110298)

        D:/JBProjects/GOLAND/wallpeper/window_maker/window_maker.go:169 +0xb9
wallpeper/window_maker.windowWndProc(0x110298?, 0x3?, 0x0?, 0x1af0370?)
        D:/JBProjects/GOLAND/wallpeper/window_maker/window_maker.go:144 +0xd0
syscall.SyscallN(0x7ff90e04c4f0?, {0xc000088020?, 0x1?, 0xcccb40?})
        C:/Users/79685/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.6.windows-amd64/src/runtime/syscall_windows.go:544 +0x107
syscall.(*Proc).Call(0xd97200?, {0xc000088020?, 0xce9280?, 0x1?})

        C:/Users/79685/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.6.windows-amd64/src/syscall/dll_windows.go:166 +0x1c
syscall.(*LazyProc).Call(0xd97200, {0xc000088020, 0x1, 0x1})

        C:/Users/79685/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.6.windows-amd64/src/syscall/dll_windows.go:286 +0x4a
wallpeper/window_maker.NewWindow({0xcfe9cc, 0xf}, 0x320, 0x258, 0x1)
        D:/JBProjects/GOLAND/wallpeper/window_maker/window_maker.go:333 +0x933  
main.main()
        D:/JBProjects/GOLAND/wallpeper/main.go:12 +0x32