.{math, memory} := @use("../../stn/src/lib.hb");
.{dt_get} := @use("../../dt_api/src/lib.hb");
.{Color, Image} := @use("lib.hb");
.{Vec2} := math

ctx := @as(Context, idk)

Context := struct {
	fb: ^Color,
	bb: ^Color,
	buf: ^Color,
	width: int,
	height: int,
	pixels: int,
	double_buffer: bool,
}

init := fn(): void {
	width := dt_get(int, "framebuffer/fb0/width\0")
	height := dt_get(int, "framebuffer/fb0/height\0")
	pixels := width * height
	back_buffer := memory.alloc(Color, pixels * @bitcast(@sizeof(Color)))
	ctx = Context.{
		fb: dt_get(^Color, "framebuffer/fb0/ptr\0"),
		bb: back_buffer,
		buf: back_buffer,
		width,
		height,
		pixels,
		double_buffer: true,
	}
	return
}

doublebuffer := fn(enable: bool): void {
	if enable {
		ctx.buf = ctx.bb
	} else {
		ctx.buf = ctx.fb
	}
	ctx.double_buffer = enable
	return
}

clear := fn(color: Color): void {
	return @inline(memory.set, Color, &color, ctx.buf, @bitcast(ctx.pixels))
}

sync := fn(): void {
	return @inline(memory.copy, Color, ctx.buf, ctx.fb, @bitcast(ctx.pixels))
}

width := fn(): int {
	return ctx.width
}

height := fn(): int {
	return ctx.height
}

screenidx := fn(x: int, y: int): int {
	return x + ctx.width * y
}

put_pixel := fn(pos: Vec2(int), color: Color): void {
	*(ctx.buf + @inline(screenidx, pos.x, pos.y)) = color
	return
}

put_filled_rect := fn(pos: Vec2(int), tr: Vec2(int), color: Color): void {
	start_idx := @inline(screenidx, pos.x, pos.y)
	end_idx := @inline(screenidx, pos.x, pos.y + tr.y)

	loop if start_idx >= end_idx break else {
		@inline(memory.set, Color, &color, ctx.buf + start_idx, @bitcast(tr.x))
		start_idx += ctx.width
	}

	return
}

put_rect := fn(pos: Vec2(int), tr: Vec2(int), color: Color): void {
	start_idx := @inline(screenidx, pos.x, pos.y)
	end_idx := @inline(screenidx, pos.x, pos.y + tr.y)
	right_start_idx := @inline(screenidx, pos.x + tr.x, pos.y)

	loop if start_idx > end_idx break else {
		*(ctx.buf + start_idx) = color;
		*(ctx.buf + right_start_idx) = color
		start_idx += ctx.width
		right_start_idx += ctx.width
	}

	@inline(memory.set, Color, &color, ctx.buf + @inline(screenidx, pos.x, pos.y), @bitcast(tr.x + 1))
	@inline(memory.set, Color, &color, ctx.buf + @inline(screenidx, pos.x, pos.y + tr.y), @bitcast(tr.x + 1))

	return
}

put_line_low := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void {
	dx := p1.x - p0.x
	dy := p1.y - p0.y
	yi := 1
	if dy < 0 {
		yi = -1
		dy = -dy
	}
	D := 2 * dy - dx
	y := p0.y
	x := p0.x
	loop if x == p1.x break else {
		*(ctx.buf + @inline(screenidx, x, y)) = color
		if D > 0 {
			y += yi
			D += 2 * (dy - dx)
		} else {
			D += 2 * dy
		}
		x += 1
	}
	return
}

put_line_high := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void {
	dx := p1.x - p0.x
	dy := p1.y - p0.y
	xi := 1
	if dy < 0 {
		xi = -1
		dx = -dx
	}
	D := 2 * dx - dy
	x := p0.x
	y := p0.y
	loop if y == p1.y break else {
		*(ctx.buf + @inline(screenidx, x, y)) = color
		if D > 0 {
			x += xi
			D += 2 * (dx - dy)
		} else {
			D += 2 * dx
		}
		y += 1
	}
	return
}

put_line := fn(p0: Vec2(int), p1: Vec2(int), color: Color): void {
	if math.abs(int, p1.y - p0.y) < math.abs(int, p1.x - p0.x) {
		if p0.x > p1.x {
			@inline(put_line_low, p1, p0, color)
		} else {
			@inline(put_line_low, p0, p1, color)
		}
	} else {
		if p0.y > p1.y {
			@inline(put_line_high, p1, p0, color)
		} else {
			@inline(put_line_high, p0, p1, color)
		}
	}
	return
}

set_height := fn(new: int): void {
	return
}

set_width := fn(new: int): void {
	return
}

dimensions := fn(): Vec2(int) {
	return .(ctx.width, ctx.height)
}

set_dimensions := fn(new: Vec2(int)): void {
	return
}

put_image := fn(image: Image, pos: Vec2(int)): void {
	// y := 0
	// loop if y == image.height break else {
	// 	@inline(memory.copy, Color, image.buf + y * image.width, ctx.buf + @inline(screenidx, pos.x, pos.y + image.height - y), @intcast(image.width))
	// 	y += 1
	// }
	// return

	start_idx := @inline(screenidx, pos.x, pos.y)
	end_idx := @inline(screenidx, pos.x, pos.y + image.height)
	cursor := image.width * image.height

	loop if start_idx >= end_idx break else {
		@inline(memory.copy, Color, image.buf + cursor, ctx.buf + start_idx, @intcast(image.width))
		start_idx += ctx.width
		cursor -= image.width
	}
	return
}

// peony-made
put_trirect := fn(pos: Vec2(int), size: Vec2(int), color0: Color, color1: Color): void {
	step := Vec2(int).(1, 1)
	if size.x < 0 {
		step.x = -1
	}
	if size.y < 0 {
		step.y = size.y / size.x
	}

	start_y := pos.y
	target := pos + size

	loop if pos.x == target.x break else {
		put_vline(pos.x, pos.y, target.y, color0)
		@inline(put_vline, pos.x, pos.y, start_y, color1)
		pos += step
	}

	return
}

// peony-made
put_vline := fn(x: int, y0: int, y1: int, color: Color): void {
	if y1 < y0 {
		tmp := y0
		y0 = y1
		y1 = tmp
	}
	y := y0

	loop if y == y1 break else {
		*(ctx.buf + @inline(screenidx, x, y)) = color
		y += 1
	}

	return
}

// peony-made
put_hline := fn(y: int, x0: int, x1: int, color: Color): void {
	if x1 < x0 {
		tmp := x0
		x0 = x1
		x1 = tmp
	}
	x := x0

	loop if x == x1 break else {
		*(ctx.buf + @inline(screenidx, x, y)) = color
		x += 1
	}

	return
}