// Copyright (c) 2026 - 2026 kio@little-bat.de
// BSD-2-Clause license
// https://opensource.org/licenses/BSD-2-Clause

#include "MainWindow.h"
#include "common/timing.h"
#include "video/VgaMode.h"
#include "video/internal/VideoBackend.h"
#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
#include <QMouseEvent>
#include <QPainter>
#include <QPushButton>
#include <QSettings>
#include <QVector>


extern "C" void __noreturn __printflike(1, 2) panic(const char* fmt, ...);
void __noreturn			   panic(const char* format, ...)
{
	va_list va;
	va_start(va, format);
	vfprintf(stderr, format, va);
	fprintf(stderr, "\n");
	va_end(va);
	abort();
}

namespace kilipili
{
void __noreturn __printflike(1, 2) panic(const char* format, ...) // TODO: -> glue
{
	va_list va;
	va_start(va, format);
	vfprintf(stderr, format, va);
	fprintf(stderr, "\n");
	va_end(va);
	abort();
}
} // namespace kilipili


// =============================================================================


extern QImage*		qimage;
QImage*				qimage = nullptr;
static std::mutex	qimage_lock;
static QMainWindow* qmainwindow = nullptr;


namespace kilipili
{
VgaMode			vga_mode;
uint32			cc_per_px;
uint32			cc_per_scanline; //
uint32			cc_per_frame;	 //
uint			cc_per_us;		 // cpu clock cycles per microsecond
volatile bool	in_vblank;
volatile int	line_at_frame_start;
volatile uint32 time_us_at_frame_start; // timestamp of start of active screen lines (updated ~1 scanline early)
volatile uint32 time_cc_at_frame_start; // timestamp of start of active screen lines, precise to the cc
volatile int	current_frame = 0;

static constexpr uint32 nominal_clock = 900 MHz;


uint32* get_scanline(int row)
{
	assert(qimage != nullptr);
	assert(row >= 0 && row < qimage->height());
	return reinterpret_cast<uint32*>(qimage->scanLine(row));
}

uint32 time_cc_32() noexcept // equivalent to time_us_32() but for cpu clock cycles
{
	// calculated from the usec time
	// => up to cc_per_us too low ((plus time for evaluation))
a:
	uint32 cc0 = time_cc_at_frame_start;
	uint32 us0 = time_us_at_frame_start;
	if (cc0 != time_cc_at_frame_start) goto a;
	return cc0 + (time_us_32() - us0) * cc_per_us;
}

void initialize_video_backend() noexcept
{
	// nop
}

void start_video_backend(const VgaMode& vgamode, uint32 min_sys_clock)
{
	assert(qimage == nullptr);

	vga_mode		= vgamode;
	cc_per_px		= nominal_clock / vgamode.pixel_clock;
	cc_per_scanline = cc_per_px * vgamode.h_total();
	cc_per_frame	= cc_per_scanline * vgamode.v_total();
	cc_per_us		= cc_per_px * vgamode.pixel_clock / 1000000;

	in_vblank			   = false;
	current_frame		   = 0;
	line_at_frame_start	   = 0;
	time_us_at_frame_start = 0;
	time_cc_at_frame_start = 0;

	if (qmainwindow) qmainwindow->setFixedSize(vgamode.width, vgamode.height + mbar_height);
	qimage = new QImage(vgamode.width, vgamode.height, QImage::Format_RGB32);
}

void stop_video_backend() noexcept
{
	qimage_lock.lock();
	delete qimage;
	qimage = nullptr;
	qimage_lock.unlock();
}
} // namespace kilipili


MainWindow::MainWindow(int w, int h, QWidget* parent) : QMainWindow(parent)
{
	qmainwindow = this;
	setFixedSize(w, h + mbar_height); //
}

void MainWindow::paintEvent(QPaintEvent* e)
{
	using namespace kilipili;

	qimage_lock.lock();

	if (qimage == nullptr)
	{
		super::paintEvent(e);
		QPainter(this).fillRect(e->rect(), Qt::black);
	}
	else
	{
		// simulate the timing_isr():
		in_vblank			   = true;
		current_frame		   = current_frame + 1;
		line_at_frame_start	   = line_at_frame_start + vga_mode.height;
		time_us_at_frame_start = time_us_32();
		time_cc_at_frame_start = time_cc_at_frame_start + cc_per_frame;
		__sev();

		super::paintEvent(e);
		QPainter painter(this);
		painter.setRenderHint(QPainter::Antialiasing, false);
		painter.drawImage(QRect(0, mbar_height, width(), height() - mbar_height), *qimage);

		in_vblank = false;
		__sev();
	}

	qimage_lock.unlock();
	update(); // good idea?
}

void MainWindow::keyPressEvent(QKeyEvent* e)
{
	return super::keyPressEvent(e);

	//bool alt = e->modifiers() & Qt::AltModifier;
	//switch (e->key()){}
	//e->accept();
}

void MainWindow::showEvent(QShowEvent* e) { super::showEvent(e); }
void MainWindow::hideEvent(QHideEvent* e) { super::hideEvent(e); }
void MainWindow::resizeEvent(QResizeEvent* e) { super::resizeEvent(e); }
void MainWindow::keyReleaseEvent(QKeyEvent* e) { super::keyReleaseEvent(e); }
void MainWindow::focusInEvent(QFocusEvent* e) { super::focusInEvent(e); }
void MainWindow::focusOutEvent(QFocusEvent* e) { super::focusOutEvent(e); }
void MainWindow::mousePressEvent(QMouseEvent* e) { super::mousePressEvent(e); }
void MainWindow::mouseReleaseEvent(QMouseEvent* e) { super::mouseReleaseEvent(e); }
void MainWindow::mouseMoveEvent(QMouseEvent* e) { super::mouseMoveEvent(e); }
void MainWindow::wheelEvent(QWheelEvent* e) { super::wheelEvent(e); }


/*












































*/
