package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"
	"unsafe"

	"github.com/getlantern/systray"
	"golang.org/x/sys/windows/registry"
)

// Define required structures for OS version check
const (
	VER_MAJORVERSION = 0x001
	VER_MINORVERSION = 0x002
	VER_BUILDNUMBER  = 0x004
)

type RTL_OSVERSIONINFOW struct {
	OSVersionInfoSize uint32
	MajorVersion      uint32
	MinorVersion      uint32
	BuildNumber       uint32
	PlatformId        uint32
	CSDVersion        [128]uint16
}

// isWindows11OrNewer checks if the OS is Windows 11 (Build >= 22000) or later.
func isWindows11OrNewer() bool {
	ntdll := syscall.NewLazyDLL("ntdll.dll")
	rtlGetVersion := ntdll.NewProc("RtlGetVersion")

	var info RTL_OSVERSIONINFOW
	info.OSVersionInfoSize = uint32(unsafe.Sizeof(info))

	ret, _, _ := rtlGetVersion.Call(uintptr(unsafe.Pointer(&info)))

	// Check if the call was successful (NTSTATUS 0)
	if ret != 0 {
		return false // Assume older or error
	}

	// Windows 11 is generally defined by Build Number >= 22000
	return info.MajorVersion >= 10 && info.BuildNumber >= 22000
}


// Set Windows dark/light mode via registry
func setMode(light bool) {
	key, _ := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Themes\Personalize`, registry.SET_VALUE)
	defer key.Close()
	val := uint32(0)
	if light {
		val = 1
	}
	key.SetDWordValue("AppsUseLightTheme", val)
	key.SetDWordValue("SystemUsesLightTheme", val)
}

// Restart Explorer to fully apply theme
func restartExplorer() {
	exec.Command("taskkill", "/f", "/im", "explorer.exe").Run()
	time.Sleep(5 * time.Second)
	exec.Command("explorer.exe").Start()
}

// Get current theme mode from registry
func getCurrentMode() string {
	key, _ := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Themes\Personalize`, registry.QUERY_VALUE)
	defer key.Close()
	val, _, _ := key.GetIntegerValue("AppsUseLightTheme")
	if val == 1 {
		return "light"
	}
	return "dark"
}

// Find image in same dir with any supported extension
func findImageFile(name string) string {
	exePath, _ := os.Executable()
	dir := filepath.Dir(exePath)
	matches, _ := filepath.Glob(filepath.Join(dir, name+".*"))
	for _, f := range matches {
		ext := strings.ToLower(filepath.Ext(f))
		if ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp" || ext == ".webp" {
			return f
		}
	}
	return ""
}

// Get current wallpaper path from registry
func getCurrentWallpaper() string {
	key, _ := registry.OpenKey(registry.CURRENT_USER, `Control Panel\Desktop`, registry.QUERY_VALUE)
	defer key.Close()
	val, _, _ := key.GetStringValue("Wallpaper")
	return val
}

// Set desktop wallpaper via Windows API
func setWallpaper(path string) {
	user32 := syscall.NewLazyDLL("user32.dll")
	sysParam := user32.NewProc("SystemParametersInfoW")
	p, _ := syscall.UTF16PtrFromString(path)
	sysParam.Call(0x0014, 0, uintptr(unsafe.Pointer(p)), uintptr(0x01|0x02))
}

// Main background loop
func backgroundLoop() {
	lightImg := findImageFile("light")
	darkImg := findImageFile("dark")
	lastMode := getCurrentMode()
	
	// Determine if explorer restart is needed once at startup
	needsExplorerRestart := isWindows11OrNewer()
	if !needsExplorerRestart {
		fmt.Println("Running on Windows 10 (or older), skipping explorer restart.")
	} else {
		fmt.Println("Running on Windows 11 (or newer), explorer restart enabled.")
	}


	for {
		hour := time.Now().Hour()
		newMode := "dark"
		var targetImg string

		if hour >= 6 && hour < 18 {
			newMode = "light"
			targetImg = lightImg
		} else {
			targetImg = darkImg
		}

		if newMode != lastMode {
			setMode(newMode == "light")
			
			// Conditional Explorer Restart ( Windows 10 or 11)
			if needsExplorerRestart {
				restartExplorer()
			}

			lastMode = newMode
		}

		if targetImg != "" && getCurrentWallpaper() != targetImg {
			setWallpaper(targetImg)
		}

		time.Sleep(60 * time.Second)
	}
}

// Load icon from local directory (icon.ico)
func getIconBytes() []byte {
	exePath, _ := os.Executable()
	iconPath := filepath.Join(filepath.Dir(exePath), "icon.ico")
	data, err := ioutil.ReadFile(iconPath)
	if err != nil {
		fmt.Println("icon.ico not found or unreadable.")
		return nil
	}
	return data
}

// Systray startup
func onReady() {
	icon := getIconBytes()
	if icon != nil {
		systray.SetIcon(icon)
	}
	systray.SetTitle("Auto Theme")
	systray.SetTooltip("Auto Theme + Wallpaper Switcher")

	mQuit := systray.AddMenuItem("Quit", "Exit the program")

	go func() {
		go backgroundLoop()
		<-mQuit.ClickedCh
		systray.Quit()
	}()
}

func onExit() {}

func main() {
	systray.Run(onReady, onExit)
}
