#include "../AnimationRender/AnimationRender.h"
#include "../AnimationRender/F_Draw.h"
#include "../AnimationRender/F_FrameRange.h"
#include "../AnimationRender/F_Interpolate.h"
#include "../AnimationRender/FrameCallback.h"
#include "../FrameRender/P_Checker.h"
#include "../FrameRender/P_Circle.h"
#include "../FrameRender/P_Image.h"
#include "../FrameRender/P_Square.h"
#include "../FrameRender/P_TransformCenter.h"
#include "../FrameRender/P_TransformRotate.h"
#include "../FrameRender/PixelCallback.h"
#include "../RGBImage/RGBImage.h"
#include <stdlib.h>

# define M_PI		3.14159265358979323846	/* pi */

int
main() {
	u32 width = 512;
	u32 height = 512;
	const u32 FPS = 30;
	u32 frames = FPS * 4;

	FILE* fbclc = fopen("renders/bclc.ppm", "rb");
	if (fbclc == NULL) {
		fprintf(stderr, "Couldn't open image file!\n");
		return 1;
	}
	RGBImage bclc = ppm_read(fbclc);
	fclose(fbclc);

	/* Background */
	TransformCenter tc_checker1 = { .centerX = 0, .centerY = 0, };
	TransformCenter tc_checker2 = {
		.centerX = tc_checker1.centerX + -1 * frames,
		.centerY = tc_checker1.centerY + -1 * frames,
	};
	PixelCallback* tc_checker = TransformCenter_setup(tc_checker1);

	/* Image */
	TransformCenter tc_img1 = { .centerX = 0, .centerY = 0, };
	TransformCenter tc_img2 = {
		.centerX = tc_img1.centerX + 0.5 * 100,
		.centerY = tc_img1.centerY + 0.5 * 100,
	};
	PixelCallback* tc_img = TransformCenter_setup(tc_img1);

	/* Line */
	LineSegment line1 = {
		.aX = 0, .aY = 21,
		.bX = 368, .bY = 512,
		.width = 1, .color = 0xFFF550F0,
	};
	LineSegment line2 = {
		.aX = 256, .aY = 512,
		.bX = 0, .bY = 0,
		.width = 10, .color = 0xFF50F0F5,
	};
	PixelCallback* line = LineSegment_setup(line1);

	/* Yellow square */
	TransformCenter tc_ysquare1 = { .centerX = 100, .centerY = 100, };
	TransformCenter tc_ysquare2 = {
		.centerX = tc_ysquare1.centerX + 80 * 1,
		.centerY = tc_ysquare1.centerY + 80 * 3,
	};
	PixelCallback* tc_ysquare = TransformCenter_setup(tc_ysquare1);

	TransformRotate tr_ysquare1 = { .angle = 0 },
	                tr_ysquare2 = { .angle = M_PI / 15 * 80 };
	PixelCallback* tr_ysquare = TransformRotate_setup(tr_ysquare1);

	/* White circle */
	TransformCenter tc_circle1 = { .centerX = 40, .centerY = 40, };
	TransformCenter tc_circle2 = {
		.centerX = tc_circle1.centerX + 80 * 2,
		.centerY = tc_circle1.centerY + 80 * 2,
	};
	PixelCallback* tc_circle = TransformCenter_setup(tc_circle1);

	/* Green square */
	TransformCenter tc_gsquare1 = { .centerX = width - 25, .centerY = 20, };
	TransformCenter tc_gsquare2 = {
		.centerX = tc_gsquare1.centerX + 80 * -2,
		.centerY = tc_gsquare1.centerY + 80 * 4,
	};
	PixelCallback* tc_gsquare = TransformCenter_setup(tc_gsquare1);

	/* Segment */
	TransformCenter tc_segment1 = { .centerX = 0, .centerY = 0, };
	TransformCenter tc_segment2 = {
		.centerX = tc_segment1.centerX + frames * 4,
		.centerY = tc_segment1.centerY + frames * 4,
	};

	PixelCallback* tc_segment = TransformCenter_setup(tc_segment1);
	TransformRotate tr_segment1 = { .angle = 0 },
	                tr_segment2 = { .angle = M_PI / 10 * frames };
	PixelCallback* tr_segment = TransformRotate_setup(tr_segment1);

	PixelCallback* callbacks[] = {
		tc_checker,
		Checker_setup((Checker){
			.squareSize = 32, .colorA = 0xFF000000, .colorB = 0xFFFF00FF,
		}),
		NULL,
		tc_img,
		Image_setup((Image){ .img = bclc, .scale = 0.4, }),
		NULL,
		line,
		tc_circle,
		Circle_setup((Circle){
			.radius = 20, .color = 0xFFFFFFFF,
		}),
		NULL,
		tc_ysquare,
		tr_ysquare,
		Square_setup((Square){
			.size = 20, .color = 0xFFFFFF00,
		}),
		NULL,
		tc_gsquare,
		Square_setup((Square){
			.size = 50, .color = 0xA009FAA5,
		}),
		NULL,
		tc_segment,
		tr_segment,
		LineSegment_setup((LineSegment){
			.aX = -60, .aY = 0,
			.bX = 60, .bY = 0,
			.width = 5, .color = 0x600000FF,
		}),
	};
	u32 sizeCallbacks = sizeof(callbacks) / sizeof(*callbacks);

	FrameCallback* frameCallbacks[] = {
		Interpolate_setup((Interpolate){
			.startValue = &tc_checker1, .endValue = &tc_checker2,
			.value = tc_checker->callback_self,
			.interpolate = (f_interpolate)Interpolate_TransformCenter,
		}),
		Interpolate_setup((Interpolate){
			.startValue = &tc_segment1, .endValue = &tc_segment2,
			.value = tc_segment->callback_self,
			.interpolate = (f_interpolate)Interpolate_TransformCenter,
		}),
		Interpolate_setup((Interpolate){
			.startValue = &tr_segment1, .endValue = &tr_segment2,
			.value = tr_segment->callback_self,
			.interpolate = (f_interpolate)Interpolate_TransformRotate,
		}),
		Interpolate_setup((Interpolate){
			.startValue = &line1, .endValue = &line2,
			.value = line->callback_self,
			.interpolate = (f_interpolate)Interpolate_LineSegment,
		}),
		NULL,
		FrameRange_setup((FrameRange){
			.startFrame = 0, .endFrame = 100,
		}),
		Interpolate_setup((Interpolate){
			.startValue = &tc_img1, .endValue = &tc_img2,
			.value = tc_img->callback_self,
			.interpolate = (f_interpolate)Interpolate_TransformCenter,
		}),
		NULL,
		FrameRange_setup((FrameRange){
			.startFrame = 0, .endFrame = 80,
		}),
		Interpolate_setup((Interpolate){
			.startValue = &tc_ysquare1, .endValue = &tc_ysquare2,
			.value = tc_ysquare->callback_self,
			.interpolate = (f_interpolate)Interpolate_TransformCenter,
		}),
		Interpolate_setup((Interpolate){
			.startValue = &tr_ysquare1, .endValue = &tr_ysquare2,
			.value = tr_ysquare->callback_self,
			.interpolate = (f_interpolate)Interpolate_TransformRotate,
		}),
		Interpolate_setup((Interpolate){
			.startValue = &tc_circle1, .endValue = &tc_circle2,
			.value = tc_circle->callback_self,
			.interpolate = (f_interpolate)Interpolate_TransformCenter,
		}),
		NULL,
		FrameRange_setup((FrameRange){
			.startFrame = 30, .endFrame = 110,
		}),
		Interpolate_setup((Interpolate){
			.startValue = &tc_gsquare1, .endValue = &tc_gsquare2,
			.value = tc_gsquare->callback_self,
			.interpolate = (f_interpolate)Interpolate_TransformCenter,
		}),
		/* Render */
		NULL,
		Draw_setup((Draw){
			.callbacks = (const PixelCallback**)callbacks, .sizeCallbacks = sizeCallbacks,
		}),
	};
	u32 sizeFrameCallbacks = sizeof(frameCallbacks) / sizeof(*frameCallbacks);

	RenderAnimation(width, height, 150, (const FrameCallback**)frameCallbacks, sizeFrameCallbacks);

	RGBImage_delete(&bclc);
	for (u32 i = 0; i < sizeCallbacks; ++i) {
		if (callbacks[i] == NULL) continue;
		free(callbacks[i]->callback_self);
		free(callbacks[i]);
	}
	for (u32 i = 0; i < sizeFrameCallbacks; ++i) {
		if (frameCallbacks[i] == NULL) continue;
		free(frameCallbacks[i]->callback_self);
		free(frameCallbacks[i]);
	}

	return 0;
}
