package main

import "core:os"
import "core:fmt"

import "./console"
import "../vendor/evdev"

print_debug :: proc(
	con: ^console.Console,
	x, y: int,
	type: evdev.EventType,
	code: u16,
	value: uint,
	stick_x, stick_y: int,
	is_down: bool,
) {
	con->write_at(console.CSI + "K", x, y)
	con->write_at(
		fmt.tprintf(
			console.FORMAT_RGB + "%s",
			38, // Foreground
			2, // RGB mode
			255,
			0,
			0,
			fmt.tprintf("Type: %s - Code: %03d - Value: %03d", type, code, value),
		),
		x,
		y,
	)
	con->write_at(console.CSI + "K", x, y + 1)
	con->write_at(
		fmt.tprintf(
			console.FORMAT_RGB + "%s",
			38, // Foreground
			2, // RGB mode
			255,
			0,
			0,
			fmt.tprintf("LStick: %d v %d", stick_x, stick_y),
		),
		x,
		y + 1,
	)
	con->write_at(console.CSI + "K", x, y + 2)
	if type == .Key {
		con->write_at(
			fmt.tprintf(
				console.FORMAT_RGB + "%s",
				38, // Foreground
				2, // RGB mode
				255,
				0,
				0,
				fmt.tprintf("%s(%d) => %t", evdev.Button(code), code, is_down),
			),
			x,
			y + 2,
		)
	}

	con->write(console.RESET_FORMAT)
}

draw_stick :: proc(con: ^console.Console, x, y: int, stick_x, stick_y: int, button: bool) {
	con->write_at(" /---\\", x, y)
	con->write_at("/     \\", x, y + 1)
	con->write_at("|     |", x, y + 2)
	con->write_at("|     |", x, y + 3)
	con->write_at("|     |", x, y + 4)
	con->write_at("\\     /", x, y + 5)
	con->write_at(" \\---/", x, y + 6)
	x_offset := int((f32(stick_x) / 32000) * 2)
	y_offset := int((f32(stick_y) / 32000) * 2)

	if button {
		con->write(
			fmt.tprintf(
				console.FORMAT_RGB,
				48, // Background
				2, // RGB mode
				66,
				66,
				66,
			),
		)
	} else {
		con->write(console.RESET_FORMAT)
	}

	con->write_at(".+.", x + 2 + x_offset, y + 2 + y_offset)
	con->write_at("+++", x + 2 + x_offset, y + 3 + y_offset)
	con->write_at(".+.", x + 2 + x_offset, y + 4 + y_offset)

	con->write(console.RESET_FORMAT)
}

draw_facebuttons :: proc(con: ^console.Console, x, y: int, but_a, but_b, but_x, but_y: bool) {
	if but_y {
		con->write(
			fmt.tprintf(
				console.FORMAT_RGB,
				48, // Background
				2, // RGB mode
				66,
				66,
				66,
			),
		)
	} else {
		con->write(console.RESET_FORMAT)
	}
	con->write_at("( Y )", x + 3, y)
	if but_x {
		con->write(
			fmt.tprintf(
				console.FORMAT_RGB,
				48, // Background
				2, // RGB mode
				66,
				66,
				66,
			),
		)
	} else {
		con->write(console.RESET_FORMAT)
	}
	con->write_at("( X )", x, y + 1)
	if but_b {
		con->write(
			fmt.tprintf(
				console.FORMAT_RGB,
				48, // Background
				2, // RGB mode
				66,
				66,
				66,
			),
		)
	} else {
		con->write(console.RESET_FORMAT)
	}
	con->write_at("( B )", x + 6, y + 1)
	if but_a {
		con->write(
			fmt.tprintf(
				console.FORMAT_RGB,
				48, // Background
				2, // RGB mode
				66,
				66,
				66,
			),
		)
	} else {
		con->write(console.RESET_FORMAT)
	}
	con->write_at("( A )", x + 3, y + 2)

	con->write(console.RESET_FORMAT)
}

draw_dpad :: proc(con: ^console.Console, x, y: int, dpad_x, dpad_y: int) {
	if dpad_y < 0 {
		con->write(
			fmt.tprintf(
				console.FORMAT_RGB,
				48, // Background
				2, // RGB mode
				66,
				66,
				66,
			),
		)
	} else {
		con->write(console.RESET_FORMAT)
	}
	con->write_at("[ ^ ]", x + 3, y)
	if dpad_x < 0 {
		con->write(
			fmt.tprintf(
				console.FORMAT_RGB,
				48, // Background
				2, // RGB mode
				66,
				66,
				66,
			),
		)
	} else {
		con->write(console.RESET_FORMAT)
	}
	con->write_at("[ < ]", x, y + 1)
	if dpad_x > 0 {
		con->write(
			fmt.tprintf(
				console.FORMAT_RGB,
				48, // Background
				2, // RGB mode
				66,
				66,
				66,
			),
		)
	} else {
		con->write(console.RESET_FORMAT)
	}
	con->write_at("[ > ]", x + 6, y + 1)
	if dpad_y > 0 {
		con->write(
			fmt.tprintf(
				console.FORMAT_RGB,
				48, // Background
				2, // RGB mode
				66,
				66,
				66,
			),
		)
	} else {
		con->write(console.RESET_FORMAT)
	}
	con->write_at("[ v ]", x + 3, y + 2)

	con->write(console.RESET_FORMAT)
}

draw_shoulder :: proc(con: ^console.Console, x, y: int, trigger: int, shoulder: bool) {
	if shoulder {
		con->write_at("*", x, y)
	} else {
		con->write_at(".", x, y)
	}
	con->write_at("^", x, y + 1)
	steps := int((f32(trigger) / 1024) * 4)
	for _, i in 0 ..< 4 {
		if i <= steps {
			con->write_at("#", x, y + 5 - i)
		} else {
			con->write_at(".", x, y + 5 - i)
		}
	}
	con->write_at("v", x, y + 6)
}

main :: proc() {
	if len(os.args) < 2 {
		panic("no file")
	}

	con := console.create()
	defer con->destroy()

	// width, height := con->get_size()

	lstick_x, lstick_y: int
	lstick_but, rstick_but: bool
	rstick_x, rstick_y: int
	but_a, but_b, but_x, but_y: bool
	dpad_x, dpad_y: int
	ltrigger, rtrigger: int
	lshoulder, rshoulder: bool
	// select/start/power button

	last_type: evdev.EventType
	last_code: u16
	last_value: uint
	last_is_down: bool

	filepath := os.args[1]

	fd: os.Handle
	errno: os.Errno
	fd, errno = os.open(filepath, os.O_RDWR)
	if errno != os.ERROR_NONE {
		panic("could not open device")
	}
	defer os.close(fd)

	con->clear()
	for errno == os.ERROR_NONE {
		print_debug(&con, 1, 1, last_type, last_code, last_value, lstick_x, lstick_y, last_is_down)

		draw_stick(&con, 3, 9, lstick_x, lstick_y, lstick_but)
		draw_stick(&con, 21, 12, rstick_x, rstick_y, rstick_but)
		draw_dpad(&con, 3, 16, dpad_x, dpad_y)
		draw_facebuttons(&con, 17, 9, but_a, but_b, but_x, but_y)
		draw_shoulder(&con, 2, 9, ltrigger, lshoulder)
		draw_shoulder(&con, 29, 9, rtrigger, rshoulder)

		event: evdev.InputEvent
		_, errno = os.read_ptr(fd, &event, size_of(event))
		//fmt.printf("%#v\n", event)
		if event.type != .Syn {
			last_type = event.type
			last_code = event.code
			last_value = event.value
		}

		if event.type == .Key {
			if event.code == u16(evdev.Button.Start) {
				break
			} else {
				buffer := evdev.get_key(fd)
				is_down := true if buffer[event.code / 8] & (1 << (event.code % 8)) > 0 else false
				last_is_down = is_down
				#partial switch evdev.Button(event.code) {
				case .A:
					but_a = is_down
				case .B:
					but_b = is_down
				case .X:
					but_x = is_down
				case .Y:
					but_y = is_down
				case .Thumbl:
					lstick_but = is_down
				case .Thumbr:
					rstick_but = is_down
				case .Tl:
					lshoulder = is_down
				case .Tr:
					rshoulder = is_down
				case .Select:
				case .Start:
				}
			}
		} else if event.type == .Abs {
			axis := evdev.AbsAxis(event.code)
			absinfo := evdev.get_abs_axis(fd, axis)
			value := int(absinfo.value)
			#partial switch axis {
			case .X:
				lstick_x = value
			case .Y:
				lstick_y = value
			case .Rx:
				rstick_x = value
			case .Ry:
				rstick_y = value
			case .Hat0X:
				dpad_x = value
			case .Hat0Y:
				dpad_y = value
			case .Z:
				ltrigger = value
			case .Rz:
				rtrigger = value
			}
		}
	}
}