import { type Colordx, extend } from "@colordx/core"
import lab from "@colordx/core/plugins/lab"
import type { ImageAnalysisData } from "../analyze/analysis.ts"
import { chalkDebug } from "../utils.ts"
import * as path from "@std/path"

extend([lab])

export interface ApplicationConfig {
	latitude: number
	longitude: number
	weatherModel: string
	lightnessRange: { night: number; dawn: number; noon: number }
	chromaRange: { start: number; end: number }
	targetSkyHue: number
	numberOfWallpapers: number
	applyWallpaperCommand: string
}

export const defaultConfig: ApplicationConfig = {
	latitude: 0,
	longitude: 0,
	weatherModel: "best_match",
	lightnessRange: {
		night: 0,
		dawn: 0.3,
		noon: 0.8,
	},
	chromaRange: {
		start: 0,
		end: 1,
	},
	numberOfWallpapers: 1,
	targetSkyHue: 264,
	applyWallpaperCommand: "hyprctl hyprpaper wallpaper , %s",
}

export async function getOpenMeteoData(
	latitude: number,
	longitude: number,
	weatherModel: string = "best_match",
) {
	const openMeteoResponse = await fetch(
		"https://api.open-meteo.com/v1/forecast",
		{
			method: "POST",
			body: new URLSearchParams({
				latitude: latitude.toString(),
				longitude: longitude.toString(),
				models: weatherModel,
				current: "cloud_cover",
				minutely_15: "shortwave_radiation_instant",
				forecast_minutely_15: "1",
				past_minutely_15: "1",
			}),
		},
	)

	if (!openMeteoResponse.ok) {
		throw new Error(
			`Open-Meteo sent back an error: ${openMeteoResponse.status}`,
		)
	}

	const openMeteoData = await openMeteoResponse.json()

	const cloudCover: string = openMeteoData.current.cloud_cover

	let index = 0
	openMeteoData.minutely_15.time.forEach((time: string) => {
		if (Date.parse(time) > Date.now()) {
			// if the data point has a time greater than right now, subtract one to find the one just before the current
			// time
			index = openMeteoData.minutely_15.time.indexOf(time) - 1
		}
	})
	const shortwaveRadiation: string = openMeteoData.minutely_15.shortwave_radiation_instant[index]

	return {
		cloudCover,
		shortwaveRadiation,
	}
}

export function findMatchingImages(imagesData: ImageAnalysisData, targetColor: Colordx, numberOfImages: number) {
	const matchingImages = []

	let targetDelta = 0.06
	while (matchingImages.length < numberOfImages) {
		console.debug(`Finding matching image with delta E ${targetDelta.toFixed(2)}...`)
		for (const image of imagesData.files) {
			if (targetColor.delta(image.colour) <= targetDelta) {
				matchingImages.push(image)
			}
		}
		console.info(chalkDebug(`Couldn't find matching image with delta E ${targetDelta.toFixed(2)}! Increasing...`))
		targetDelta += 0.01
	}
	return matchingImages
}


export async function setWindowsWallpaper(tempImagePath: string, ...imagePaths: string[]) {
	interface Rect {
		left: number
		top: number
		right: number
		bottom: number
	}

	interface Vector2 {
		width: number
		height: number
	}

	const pathToUser32 = path.fromFileUrl(
		"file:///C:/Windows/System32/user32.dll",
	); // jank

	const user32 = Deno.dlopen(pathToUser32, {
		SystemParametersInfoA: {
			parameters: ["u16", "u16", "pointer", "u16"],
			result: "bool",
		},

		EnumDisplayMonitors: {
			parameters: ["pointer", "pointer", "pointer", "i32"],
			result: "bool",
		},
	});

	const monitors: Vector2[] = []

	const monitorEnum = new Deno.UnsafeCallback({
		parameters: ["pointer", "pointer", "pointer", "i32"],
		result: "bool",
	}, (_hMon, _hdc, lprcMonitor, _pData) => {
		// grab the buffer of our data...
		const buffer = Deno.UnsafePointerView.getArrayBuffer(lprcMonitor!, 16)
		// ..convert it to an Int32 array...
		const array = Array.from(new Int32Array(buffer))
		// ..then populate:            left - right                          top - bottom
		const rect = { width: Math.abs(array[2] - array[0]), height: Math.abs(array[1] - array[3]) }
		monitors.push(rect)
		
		return true
	});

	user32.symbols.EnumDisplayMonitors(null, null, monitorEnum.pointer, 0);

	// from this point on monitorsRect is populated

	console.log(monitors)

	for (let i = 0; i < monitors.length; i++) {
		const monitor = monitors[i]
		if (monitor.width >= monitor.height) {
			await $`magick ${imagePaths[i]} -resize ${monitor.width}x -format "%m" ${path.join(tempImagePath, `monitorImage${i}.png`)}`
		} else {
			await $`magick ${imagePaths[i]} -resize x${monitor.height} -format "%m" ${path.join(tempImagePath, `monitorImage${i}.png`)}`
		}
	}

	// TODO: this isn't the right order of images
	console.log(await $`magick ${path.join(tempImagePath, `monitorImage*.png`)} +append ${path.join(tempImagePath, "out.png")}`)

	const pointer = Deno.UnsafePointer.of(
		Buffer.from(
			"C:\\Users\\angelcube\\Pictures\\wallpaper\\vocaloid_morningglory_lobelia.jpg",
			"ascii",
		),
	);

	user32.symbols.SystemParametersInfoA(20, 0, pointer, 3);

	user32.close();
}