/* gcc -std=c99 *.c -lm && ./a.out | mpv --no-correct-pts --container-fps-override=30 -
 * gcc -std=c99 *.c -lm && ./a.out | ffmpeg -r 30 -i - output.mp4
 */

#include "Animation.h"
#include "AnimationObject.h"
#include "AnimationMove.h"
#include "AnimationInterpolate.h"
#include <math.h>

double
pointDistance(i32 rowA, i32 colA, i32 rowB, i32 colB) {
	return sqrt(pow(rowA - rowB, 2) + pow(colA - colB, 2));
}

void
HorSegmentByPoints(
	i32 rowA, i32 colA, i32 rowB, i32 colB,
	struct AO_HorSegment* segment,
	struct AM_Spin* rotate,
	struct AM_Linear* translate)
{
	segment->left = 0;
	segment->right = pointDistance(rowA, colA, rowB, colB);

	rotate->theta = atan2(rowB - rowA, colB - colA);

	translate->startRow = rowA;
	translate->startCol = colA;

	rotate->callback = AO_HorSegment;
	rotate->priv = segment;
	translate->callback = AM_Spin;
	translate->priv = rotate;
}

int
main() {
	const u32 FPS = 60;
	Animation anim = Animation_new(512, 512, FPS * 5);

	struct AO_Square background = {
		.width = anim.width, .height = anim.height,
		.color = 0xFFF0F0F0,
	};
	Animation_pushEvent(&anim, 0, anim.frameCount, AO_Square, &background);

	/* Root node */
	struct AO_Circle rootNode_ = {
		.radius = 60, .color = 0xFFFF0000,
	};
	struct AM_Linear rootNode_linear = {
		.callback = AO_Circle, .priv = &rootNode_,

		.startRow = anim.height / 4, .startCol = anim.width / 2,
	};

	struct AM_Linear rootNode = rootNode_linear;

	/* Child node */
	struct AO_Circle childNode_ = {
		.radius = 60, .color = 0xFF00FF00,
	};

	struct AM_Linear childNode_left = {
		.callback = AO_Circle, .priv = &childNode_,

		.startRow = anim.height * 3 / 4, .startCol = anim.width / 4,
	};
	struct AM_Linear childNode_right = {
		.callback = AO_Circle, .priv = &childNode_,

		.startRow = anim.height * 3 / 4, .startCol = anim.width * 3 / 4,
	};

	struct AM_Linear childNode = childNode_right;

	/* Root Node --- Child Node */
	struct AO_HorSegment link_ = {
		.color = 0xFFFF00FF, .width = 10,
	};

	struct AM_Spin link_spin_left = {};
	struct AM_Linear link_translate_left = {};
	HorSegmentByPoints(
		rootNode_linear.startRow, rootNode_linear.startCol,
		childNode_left.startRow, childNode_left.startCol,
		&link_, &link_spin_left, &link_translate_left);

	struct AM_Spin link_spin_right = {};
	struct AM_Linear link_translate_right = {};
	HorSegmentByPoints(
		rootNode_linear.startRow, rootNode_linear.startCol,
		childNode_right.startRow, childNode_right.startCol,
		&link_, &link_spin_right, &link_translate_right);

	struct AM_Spin link_spin = link_spin_right;
	struct AM_Linear link_translate = link_translate_right;
	link_translate.priv = &link_spin;

	Animation_pushEvent(&anim, 0, anim.frameCount, AM_Linear, &link_translate);
	Animation_pushEvent(&anim, 0, anim.frameCount, AM_Linear, &rootNode);
	Animation_pushEvent(&anim, 0, anim.frameCount, AM_Linear, &childNode);

	/* Movement */

	struct AM_Linear temp1;
	struct A_Interpolate childNode_moveToLeft = {
		.initialValueTemp = &temp1, .initialValueTempSize = sizeof(temp1),

		.interpolate = AM_LinearInterpolate,
		.value = &childNode, .goalValue = &childNode_left,
	};
	struct AM_Spin temp11;
	struct A_Interpolate segment_spin_moveToLeft = {
		.initialValueTemp = &temp11, .initialValueTempSize = sizeof(temp11),

		.interpolate = AM_SpinInterpolate,
		.value = &link_spin, .goalValue = &link_spin_left,
	};
	struct AM_Linear temp12;
	struct A_Interpolate segment_translate_moveToLeft = {
		.initialValueTemp = &temp12, .initialValueTempSize = sizeof(temp12),

		.interpolate = AM_LinearInterpolate,
		.value = &link_translate, .goalValue = &link_translate_left,
	};

	struct AM_Linear temp2;
	struct A_Interpolate childNode_moveToRight = {
		.initialValueTemp = &temp2, .initialValueTempSize = sizeof(temp2),

		.interpolate = AM_LinearInterpolate,
		.value = &childNode, .goalValue = &childNode_right,
	};
	struct AM_Spin temp21;
	struct A_Interpolate segment_spin_moveToRight = {
		.initialValueTemp = &temp21, .initialValueTempSize = sizeof(temp21),

		.interpolate = AM_SpinInterpolate,
		.value = &link_spin, .goalValue = &link_spin_right,
	};
	struct AM_Linear temp22;
	struct A_Interpolate segment_translate_moveToRight = {
		.initialValueTemp = &temp22, .initialValueTempSize = sizeof(temp22),

		.interpolate = AM_LinearInterpolate,
		.value = &link_translate, .goalValue = &link_translate_right,
	};

	/* Move child node from right to left */

	childNode_moveToLeft.startFrame = segment_spin_moveToLeft.startFrame = segment_translate_moveToLeft.startFrame
		= anim.frameCount / 3 - FPS;
	childNode_moveToLeft.endFrame = segment_spin_moveToLeft.endFrame = segment_translate_moveToLeft.endFrame
		= anim.frameCount / 3;
	Animation_pushEvent(&anim, childNode_moveToLeft.startFrame, childNode_moveToLeft.endFrame, A_Interpolate, &childNode_moveToLeft);
	Animation_pushEvent(&anim, childNode_moveToLeft.startFrame, childNode_moveToLeft.endFrame, A_Interpolate, &segment_spin_moveToLeft);
	Animation_pushEvent(&anim, childNode_moveToLeft.startFrame, childNode_moveToLeft.endFrame, A_Interpolate, &segment_translate_moveToLeft);

	/* Move child node from left to right */

	childNode_moveToRight.startFrame = segment_spin_moveToRight.startFrame = segment_translate_moveToRight.startFrame
		= anim.frameCount * 2 / 3 - FPS;
	childNode_moveToRight.endFrame = segment_spin_moveToRight.endFrame = segment_translate_moveToRight.endFrame
		= anim.frameCount * 2 / 3;
	Animation_pushEvent(&anim, childNode_moveToRight.startFrame, childNode_moveToRight.endFrame, A_Interpolate, &childNode_moveToRight);
	Animation_pushEvent(&anim, childNode_moveToRight.startFrame, childNode_moveToRight.endFrame, A_Interpolate, &segment_spin_moveToRight);
	Animation_pushEvent(&anim, childNode_moveToRight.startFrame, childNode_moveToRight.endFrame, A_Interpolate, &segment_translate_moveToRight);

	Animation_render(&anim);

	Animation_delete(&anim);

	return 0;
}
