#include "Animation.h"
#include <stdlib.h>

struct AnimationEventNode {
	AnimationEventNode* next;

	u32 startFrame;
	u32 endFrame;
	PixelRenderer renderCallback;
	void* privateData;
};

AnimationEventNode*
AnimationEventNode_new(u32 startFrame, u32 endFrame, PixelRenderer renderCallback, void* privateData) {
	AnimationEventNode* aen = (AnimationEventNode*)malloc(sizeof(AnimationEventNode));
	aen->next = NULL;
	aen->startFrame = startFrame;
	aen->endFrame = endFrame;
	aen->renderCallback = renderCallback;
	aen->privateData = privateData;

	return aen;
}

Animation
Animation_new(u32 width, u32 height, u32 frameCount) {
	return (Animation){
		.width       = width,
		.height      = height,
		.frameCount  = frameCount,
		.duplicateFrames = 0,
		.frameBuffer = RGBImage_new(width, height),
		.events      = NULL,
	};
}

void
Animation_free(Animation anim) {
	RGBImage_free(anim.frameBuffer);
	while (anim.events != NULL) {
		AnimationEventNode* toFree = anim.events;
		anim.events = anim.events->next;
		free(toFree);
	}
}

void
Animation_delete(Animation* anim) {
	Animation_free(*anim);
	anim->width = anim->height = anim->frameCount = 0;
	RGBImage_delete(&anim->frameBuffer);
}

void
Animation_pushEvent(Animation* anim, u32 startFrame, u32 endFrame, PixelRenderer renderer, void* privateData) {
	if (anim->events == NULL) {
		anim->events = AnimationEventNode_new(startFrame, endFrame, renderer, privateData);
		return;
	}

	AnimationEventNode* prevNode = NULL, *currNode = anim->events;
	while (currNode != NULL) {
		if (startFrame < currNode->startFrame) {
			prevNode->next = AnimationEventNode_new(startFrame, endFrame, renderer, privateData);
			prevNode->next->next = currNode;
			return;
		}

		prevNode = currNode;
		currNode = currNode->next;
	}

	prevNode->next = AnimationEventNode_new(startFrame, endFrame, renderer, privateData);
}

void
Animation_render(Animation* anim) {
	for (int currFrame = 0; currFrame < anim->frameCount; ++currFrame) {
		if (anim->events != NULL)
			for (i32 r = 0; r < anim->frameBuffer.width; ++r)
			for (i32 c = 0; c < anim->frameBuffer.height; ++c) {
				for (AnimationEventNode* aen = anim->events; aen != NULL && aen->startFrame <= currFrame; aen = aen->next) {
					ARGB* pixel = RGBImage_at(anim->frameBuffer, r, c);
					ARGB newPixel = aen->renderCallback(anim, currFrame, *pixel, r, c, aen->privateData);
					ARGB_merge(pixel, newPixel);
				}

				AnimationEventNode* prevNode = NULL, *currNode = anim->events;
				while (currNode != NULL && currNode->startFrame <= currFrame) {
					if (currNode->endFrame <= currFrame) {
						if (prevNode != NULL)
							prevNode->next = currNode->next;
						else
							anim->events = NULL;
						free(currNode);
					}
					else {
						prevNode = currNode;
						currNode = currNode->next;
					}
				}
			}

		ppm6_write(stdout, anim->frameBuffer);

		for (u32 duplicates = 0; duplicates < anim->duplicateFrames; ++duplicates)
			ppm6_write(stdout, anim->frameBuffer);
	}
}
