/*
 * PSP Software Development Kit - https://github.com/pspdev
 * -----------------------------------------------------------------------
 * Licensed under the BSD license, see LICENSE in PSPSDK root for details.
 *
 * main.c - image display plugin example
 *
 * Copyright (c) 2023 pyroesp
 *
 */
 
#include <pspkernel.h>
#include <pspdisplay.h>
#include <psptypes.h>
#include <psprtc.h>


PSP_MODULE_INFO("image display", PSP_MODULE_USER, 0, 1);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER);


#define SCREEN_W 480
#define SCREEN_H 272

#define IMAGE_W 72
#define IMAGE_H 40
#define PIXEL_PER_BYTE 8


// monochrome 1 bit per pixel image
u8 image[] = { 
	0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00,
	0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00,
	0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00,
	0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00,
	0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00,
	0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00,
	0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00,
	0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00,
	0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00,
	0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC,
	0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC,
	0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC,
	0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC,
	0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC,
	0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC,
	0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC,
	0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC,
	0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC,
	0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC,
	0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00,
	0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00,
	0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00,
	0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00,
	0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00,
	0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00,
	0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00,
	0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00,
	0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00,
	0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00
};


// main thread id
int thid;
// main running flag
int running;


// convert 0-359 degree value to u32 ABGR color
u32 color_Wheel(int degrees);


// main thread
int main_thread(SceSize args, void *argp){
	u64 tick;
	unsigned int* vram32;
	int bufferwidth, pixelformat;
	
	// wait for psp boot
	sceKernelDelayThread(10 * 1000 * 1000);
	// get psp ticks for pseudo randomness
	sceRtcGetCurrentTick(&tick);
	
	// set image start position from ticks
	s16 image_x = tick % (SCREEN_W - IMAGE_W);
	s16 image_y = tick % (SCREEN_H - IMAGE_H);
	// set image movement from start position
	s16 image_vx = (image_x & 1) ? -1 : 1;
	s16 image_vy = (image_y & 1) ? -1 : 1;
	// image color
	u32 image_color = color_Wheel((int)((tick / 1000) % 360));
	
	// main menu
	while (running){
		// check x collision
		if ((image_x + image_vx) >= (SCREEN_W - IMAGE_W) || (image_x + image_vx) <= 0){
			//change direction
			image_vx = -image_vx;
			// update tick value when changing direction
			sceRtcGetCurrentTick(&tick);
			// change color based on tick value
			image_color = color_Wheel((int)((tick / 1000) % 360));
		}
		// check y collision
		if ((image_y + image_vy) >= (SCREEN_H - IMAGE_H) || (image_y + image_vy) <= 0){
			//change direction
			image_vy = -image_vy;
			// update tick value when changing direction
			sceRtcGetCurrentTick(&tick);
			// change color based on tick value
			image_color = color_Wheel((int)((tick / 1000) % 360));
		}

		// move image
		image_x += image_vx;
		image_y += image_vy;
		
		
		// get vram
		int ret = sceDisplayGetFrameBuf((void*)&vram32, &bufferwidth, &pixelformat, PSP_DISPLAY_SETBUF_IMMEDIATE); 
		// works with both PSP_DISPLAY_SETBUF_NEXTFRAME and PSP_DISPLAY_SETBUF_IMMEDIATE
		
		// check if return value from sceDisplayGetFrameBuf is valid
		// check if vram32 is not NULL
		// check if bufferwidth is not 0
		if (ret == 0 && vram32 != NULL && bufferwidth != 0){
			// draw image
			for (int i = 0; i < IMAGE_H; i++){
				for (int j = 0; j < IMAGE_W; j++){
					// check if the pixel bit of the image at position 'j' is 1
					if (image[(i * (IMAGE_W / PIXEL_PER_BYTE)) + j / PIXEL_PER_BYTE] & (1 << (PIXEL_PER_BYTE - 1 - (j % PIXEL_PER_BYTE)))){
						// write image to vram
						vram32[(image_y + i) * bufferwidth + (image_x + j)] = image_color;
					}
				}
			}
		}
		// not checking return value and/or vram32 pointer will result in a crash when putting the PSP to sleep
		
		
		// wait for vblank and allow for callbacks
		if (sceDisplayWaitVblankStartCB() < 0)
			break; // end of VSH ?
	}

	return sceKernelExitDeleteThread(0);
}



int module_start(SceSize args, void *argp){
	running = 0;
	// create and start main thread
	thid = sceKernelCreateThread("image_display", main_thread, 0x10, 4*1024, PSP_THREAD_ATTR_USER, NULL);
	if (thid >= 0){
		running = 1;
		sceKernelStartThread(thid, args, argp);
	}
	
	return 0;
}

int module_stop(SceSize args, void *argp){
	// clean exit main thread
	if (running){
		running = 0;
		SceUInt time = 200*1000;
		int ret = sceKernelWaitThreadEnd(thid, &time);
		if (ret < 0)
			sceKernelTerminateDeleteThread(thid);
	}
	
	return 0;
}



// convert 0-359 degree value to u32 ABGR color
u32 color_Wheel(int degrees){
	u8 red = 0, green = 0, blue = 0;
	degrees = degrees % 360;
	
	if (degrees < 120){
		red = ((120 - degrees) * 255) / 120;
		green = (degrees * 255) / 120;
		blue = 0;
	}else if (degrees < 240){
		degrees -= 120;
		red = 0;
		green = ((120 - degrees) * 255) / 120;
		blue = (degrees * 255) / 120;
	}else{
		degrees -= 240;
		red = (degrees * 255) / 120;
		green = 0;
		blue = ((120 - degrees) * 255) / 120;
    }
	
	return (0xFF000000 | blue << 16 | green << 8 | red);
}