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

#pragma once
#include "Video/VgaMode.h"
#include "common/cdefs.h"

namespace kilipili
{

extern VgaMode		   vga_mode;			// VGAMode in use
extern uint32		   cc_per_scanline;		// cc per logical scanline (scaled by vss)
extern uint32		   cc_per_frame;		//
extern uint			   cc_per_px;			// cpu clock cycles per pixel
extern uint			   cc_per_us;			// cpu clock cycles per microsecond
extern volatile bool   in_vblank;			// set while in vblank (set and reset ~2 scanlines early)
extern volatile int	   line_at_frame_start; // rolling line number at start of current frame
extern volatile uint32 time_us_at_frame_start;
extern volatile uint32 time_cc_at_frame_start;
extern volatile int	   current_frame;

extern uint32 time_cc_32() noexcept; // equivalent to time_us_32() but for cpu clock cycles

/** class VideoBackend implements the hardware part of the video controller.

	Settings in boards/yourboard.h:
	required:
	- define how many gpio pins are used for each color:
		#define VIDEO_COLOR_PIN_BASE  2
		#define VIDEO_COLOR_PIN_COUNT 12
		#define VIDEO_PIXEL_RSHIFT    0u
		#define VIDEO_PIXEL_GSHIFT    4u
		#define VIDEO_PIXEL_BSHIFT    8u
		#define VIDEO_PIXEL_RCOUNT    4
		#define VIDEO_PIXEL_GCOUNT    4
		#define VIDEO_PIXEL_BCOUNT    4
	- define where the hsync and vsync pins are:	
		#define VIDEO_SYNC_PIN_BASE   14		
	optional:	
	- define which clock signals are required and where they are:
		#define VIDEO_CLOCK_ENABLE   false
		#define VIDEO_DEN_ENABLE     false
		#define VIDEO_CLOCK_POLARITY 0
		#define VIDEO_DEN_POLARITY   0
		#define VIDEO_CLOCK_PIN_BASE 0
		
	Possible options in CMakeLists.txt:
	- VIDEO_OPTIMISTIC_A1W8 = ON|OFF 
		The scanline render function for colormode `a1w8` (truecolor attributes 
		with 1 bit / pixel in the bitmap and 8 pixel wide attributes) can use a variant
		which optimizes based on the screen contents. This allows highest video resolution
		up to 1280*768 to be displayed in this mode. But if the screen contents becomes too 
		colorful, then the video display will fail. (rolling screen contents displayed as 
		the pixel supply can't catch up.) Because the test, whether a pixel octet can be 
		optimized or not takes time, the overall time for the bad cases takes even longer 
		than non-optimized. Specifically 1024*768 is also not guaranteed to display colorful 
		contents properly, while 1024*768 can *just* be displayed without optimization 
		regardless of the screen contents.
	- VIDEO_SUPPORT_200x150_A1W8
	- VIDEO_SUPPORT_400x300_A1W8
		See ScanlineRenderer.h
	- VIDEO_MAX_SYSCLOCK_MHz
		Upper limit for the system clock set by start_video_backend()
	- VIDEO_MAX_SCANLINE_BUFFERS
		Max. size of the rolling scanline buffer. See ScanlineBuffer.h

	General capabilities and drawbacks:
	  - The supply of pixel data is not part of the backend. The backend displays pixels from a 
	    rolling scanline buffer which is filled in by the video frontend. this makes the backend 
	    very stable and less likely to crash. 
	  - In most cases a scanline buffer with only 2 lines is enough.
	  - The video backend can produce a VGA video signal for almost any screen resolution. 
	    Limitations generally come from available RAM and CPU processing power for the pixel supply,
	    which is not part of the video backend.
	  - It uses 3 DMA channels and 2 state machines in PIO1 and a shared interrupt on DMA_IRQ_1.
	  - The system clock must be a multiple of the pixel clock. (min. times 2)
	  - The system clock must be a multiple of 1 MHz.
	  - Currently there is no event/interrupt per scanline which makes waiting with wfe() impossible. 
	
	Interesting implementation details:
	  -	The sync signals are emitted by a single DMA which is reprogrammed 4 times per frame.
	  -	The screenlines are emitted by a nested DMA which is started once and never touched and 
		synchronized again. The DMA displays pixels from a rolling (circular) scanline buffer.
	  -	There is no interrupt per scanline: elimination of this interrupt was required to make 
		screen resolution 1024*768 work. Together with the fact, that the pixels are read from
		a circular buffer, there is no way to tell where the display currently is other than 
		very precise counting. This is the reason why the system clock must be a multiple of 1 MHz.
	  	The video frontend must know which row is currently actually displayed in order to
		synchronize it filling the scanline buffer, not lagging behind and not overwriting pixels 
		before they are displayed.
*/

/**
	Initialize the hardware and claim the DMA channels and state machines
	Do not call directly! Only called by the frontend. 
*/
extern void initialize_video_backend() noexcept; // panics

/**
	Start video display in the requested resolution found in VgaMode.
	Pixels will be display from the cyclic scanline_buffer, 
	starting at scanline_buffer[0] for the first scanline in the first frame.
	Do not call directly! Only called by the frontend. 
*/
extern void start_video_backend(const VgaMode&, uint32 min_sys_clock = 0) throws;

/**
	Stop video display, releasing all resources.
	Actually does not stop_video_output video output but outputs a black screen.
	The scanline_buffer can now be purged and initialized for the new video mode to come.
	Do not call directly! Only called by the frontend. 
*/
extern void stop_video_backend() noexcept;


} // namespace kilipili
