#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

#include <exec/types.h>
#include <devices/timer.h>
#include <proto/graphics.h>

#include "/Shared/EE_view.h"
#include "/Shared/EE_timer.h"
#include "EM_structures.h"
#include "EM_animation.h"

IMPORT LONG dPalette[256*3];
IMPORT ULONG tmpPalette[256*3+2];
ULONG *EM_origPalette;
LONG EM_FadeFlag;

LONG EM_LogoPosX, EM_LogoPosY;
DOUBLE EM_LogoCPosX, EM_LogoCPosY;
DOUBLE EM_LogoDPosX, EM_LogoDPosY;
DOUBLE EM_LogoEPosX, EM_LogoEPosY;


LONG EM_DoAnim(DVIEW *DView, SCENE *Scene, ANIM_COMMAND *AnimList,
							 LONG (*RenderFrame)(DVIEW *DView, SCENE *Scene))
{
OBJECT *Objects;
LONG ONumber;
ANIM_OBJECT *AnimObjects;
LONG State;
ANIM_COMMAND *CurrentAnimCommand;
DOUBLE Time2Go, AnimTime, OverfillTime;
LONG i;
BOOL next_seq;

	Objects = Scene->Objects;
	ONumber = Scene->ONumber;

	if((AnimObjects = calloc(ONumber, sizeof(ANIM_OBJECT))) == NULL)
		return DA_MEMORY_PROBLEM;

	CurrentAnimCommand = AnimList;

	Time2Go = 0;
	AnimTime = 0;
	OverfillTime = 0;

	for(;;)
	{
		State = EM_InterpreteAnimList(&CurrentAnimCommand, AnimObjects,
																		 ONumber, &Time2Go);

		XEvent();

		if(State != IAL_OK)
		{
			free(AnimObjects);
			return State;
		}

		next_seq = FALSE;

		do
		{
			AnimTime = (DOUBLE)GetCurrentXTime() + OverfillTime;

			if(AnimTime < Time2Go)
			{
				switch(EM_FadeFlag)
				{
					case FADE_FROM_BLACK:
						WaitTOF();
						LoadRGB32(DView->VP, tmpPalette);
						FadeInSupport2(dPalette, tmpPalette, 0L, 0L, 0L, (LONG)AnimTime);
						break;

					case FADE_TO_BLACK:
						WaitTOF();
						LoadRGB32(DView->VP, tmpPalette);
						FadeOutSupport2(dPalette, tmpPalette, EM_origPalette, (LONG)AnimTime);
						break;
				}

				for(i=0; i<ONumber; i++)
					if(AnimObjects[i].Flags & AOF_ACTIVE)
					{
						Objects[i].PosX = (LONG)(AnimObjects[i].cPosX + AnimObjects[i].dPosX * AnimTime);
						Objects[i].PosY = (LONG)(AnimObjects[i].cPosY + AnimObjects[i].dPosY * AnimTime);
						Objects[i].PosZ = (LONG)(AnimObjects[i].cPosZ + AnimObjects[i].dPosZ * AnimTime);
						Objects[i].RotX = (LONG)(AnimObjects[i].cRotX + AnimObjects[i].dRotX * AnimTime);
						Objects[i].RotY = (LONG)(AnimObjects[i].cRotY + AnimObjects[i].dRotY * AnimTime);
						Objects[i].RotZ = (LONG)(AnimObjects[i].cRotZ + AnimObjects[i].dRotZ * AnimTime);
						Objects[i].Flags |= OF_NEWPOS;

						Objects[i].RotX %= ANGLES_NUMBER;
						while(Objects[i].RotX < 0)
							Objects[i].RotX += ANGLES_NUMBER;

						Objects[i].RotY %= ANGLES_NUMBER;
						while(Objects[i].RotY < 0)
							Objects[i].RotY += ANGLES_NUMBER;

						Objects[i].RotZ %= ANGLES_NUMBER;
						while(Objects[i].RotZ < 0)
							Objects[i].RotZ += ANGLES_NUMBER;
					}

				EM_LogoPosX = (LONG)(EM_LogoCPosX + EM_LogoDPosX * AnimTime);
				EM_LogoPosY = (LONG)(EM_LogoCPosY + EM_LogoDPosY * AnimTime);

				State = RenderFrame(DView, Scene);

				if(State != IAL_OK)
				{
					free(AnimObjects);
					return State;
				}
			}
			else
			{
				switch(EM_FadeFlag)
				{
					case FADE_FROM_BLACK:
						WaitTOF();
						LoadRGB32(DView->VP, EM_origPalette);
						break;

					case FADE_TO_BLACK:
						memset(&tmpPalette[1], 0, ((tmpPalette[0]>>16)*3+1)*4);
						WaitTOF();
						LoadRGB32(DView->VP, tmpPalette);
						break;
				}

				for(i=0; i<ONumber; i++)
					if(AnimObjects[i].Flags & AOF_ACTIVE)
					{
						Objects[i].PosX = (LONG)(AnimObjects[i].cPosX = AnimObjects[i].ePosX);
						Objects[i].PosY = (LONG)(AnimObjects[i].cPosY = AnimObjects[i].ePosY);
						Objects[i].PosZ = (LONG)(AnimObjects[i].cPosZ = AnimObjects[i].ePosZ);
						Objects[i].RotX = (LONG)(AnimObjects[i].cRotX = AnimObjects[i].eRotX);
						Objects[i].RotY = (LONG)(AnimObjects[i].cRotY = AnimObjects[i].eRotY);
						Objects[i].RotZ = (LONG)(AnimObjects[i].cRotZ = AnimObjects[i].eRotZ);
						Objects[i].Flags |= OF_NEWPOS;

						Objects[i].RotX %= ANGLES_NUMBER;
						while(Objects[i].RotX < 0)
							Objects[i].RotX += ANGLES_NUMBER;

						Objects[i].RotY %= ANGLES_NUMBER;
						while(Objects[i].RotY < 0)
							Objects[i].RotY += ANGLES_NUMBER;

						Objects[i].RotZ %= ANGLES_NUMBER;
						while(Objects[i].RotZ < 0)
							Objects[i].RotZ += ANGLES_NUMBER;
					}

				EM_LogoPosX = (LONG)(EM_LogoCPosX = EM_LogoEPosX);
				EM_LogoPosY = (LONG)(EM_LogoCPosY = EM_LogoEPosY);

				OverfillTime = AnimTime - Time2Go;
				next_seq = TRUE;
			}
		}
		while(!next_seq);
	}
}


LONG EM_InterpreteAnimList(ANIM_COMMAND **AnimCommand, ANIM_OBJECT *AnimObjects,
													 LONG ONumber, DOUBLE *Time2Go)
{
LONG CurrentObject;
LONG i;
DOUBLE Time;

	for(i=0; i<ONumber; i++)
		AnimObjects[i].Flags &= (~AOF_ACTIVE);

	CurrentObject = 0;
	EM_FadeFlag = NO_FADE;

	EM_LogoDPosX = 0;
	EM_LogoDPosY = 0;

	for(;;)
	{
		switch((*AnimCommand)->Command)
		{
			case AC_OBJECT:
				CurrentObject = (*AnimCommand)->Parameter;
				AnimObjects[CurrentObject].dPosX = 0;
				AnimObjects[CurrentObject].dPosY = 0;
				AnimObjects[CurrentObject].dPosZ = 0;
				AnimObjects[CurrentObject].dRotX = 0;
				AnimObjects[CurrentObject].dRotY = 0;
				AnimObjects[CurrentObject].dRotZ = 0;
				AnimObjects[CurrentObject].Flags |= AOF_ACTIVE;
				break;

			case AC_MOVEX:
				AnimObjects[CurrentObject].ePosX = (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].dPosX = (DOUBLE)((*AnimCommand)->Parameter)-
																					 AnimObjects[CurrentObject].cPosX;
				break;

			case AC_MOVEY:
				AnimObjects[CurrentObject].ePosY = (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].dPosY = (DOUBLE)((*AnimCommand)->Parameter)-
																					 AnimObjects[CurrentObject].cPosY;
				break;

			case AC_MOVEZ:
				AnimObjects[CurrentObject].ePosZ = (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].dPosZ = (DOUBLE)((*AnimCommand)->Parameter)-
																					 AnimObjects[CurrentObject].cPosZ;
				break;

			case AC_RMOVEX:
				AnimObjects[CurrentObject].ePosX += (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].dPosX = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_RMOVEY:
				AnimObjects[CurrentObject].ePosY += (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].dPosY = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_RMOVEZ:
				AnimObjects[CurrentObject].ePosZ += (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].dPosZ = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_ROTATIONX:
				AnimObjects[CurrentObject].eRotX = (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].dRotX = (DOUBLE)((*AnimCommand)->Parameter)-
																					 AnimObjects[CurrentObject].cRotX;
				break;

			case AC_ROTATIONY:
				AnimObjects[CurrentObject].eRotY = (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].dRotY = (DOUBLE)((*AnimCommand)->Parameter)-
																					 AnimObjects[CurrentObject].cRotY;
				break;

			case AC_ROTATIONZ:
				AnimObjects[CurrentObject].eRotZ = (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].dRotZ = (DOUBLE)((*AnimCommand)->Parameter)-
																					 AnimObjects[CurrentObject].cRotZ;
				break;

			case AC_RROTATIONX:
				AnimObjects[CurrentObject].eRotX += (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].dRotX = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_RROTATIONY:
				AnimObjects[CurrentObject].eRotY += (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].dRotY = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_RROTATIONZ:
				AnimObjects[CurrentObject].eRotZ += (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].dRotZ = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_SETPOSX:
				AnimObjects[CurrentObject].ePosX = (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].cPosX = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_SETPOSY:
				AnimObjects[CurrentObject].ePosY = (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].cPosY = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_SETPOSZ:
				AnimObjects[CurrentObject].ePosZ = (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].cPosZ = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_SETROTX:
				AnimObjects[CurrentObject].eRotX = (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].cRotX = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_SETROTY:
				AnimObjects[CurrentObject].eRotY = (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].cRotY = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_SETROTZ:
				AnimObjects[CurrentObject].eRotZ = (DOUBLE)((*AnimCommand)->Parameter);
				AnimObjects[CurrentObject].cRotZ = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_LMOVEX:
				EM_LogoEPosX = (DOUBLE)((*AnimCommand)->Parameter);
				EM_LogoDPosX = (DOUBLE)((*AnimCommand)->Parameter) - EM_LogoCPosX;
				break;

			case AC_LMOVEY:
				EM_LogoEPosY = (DOUBLE)((*AnimCommand)->Parameter);
				EM_LogoDPosY = (DOUBLE)((*AnimCommand)->Parameter) - EM_LogoCPosY;
				break;

			case AC_LRMOVEX:
				EM_LogoEPosX += (DOUBLE)((*AnimCommand)->Parameter);
				EM_LogoDPosX = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_LRMOVEY:
				EM_LogoEPosY += (DOUBLE)((*AnimCommand)->Parameter);
				EM_LogoDPosY = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_LSETPOSX:
				EM_LogoEPosX = (DOUBLE)((*AnimCommand)->Parameter);
				EM_LogoCPosX = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_LSETPOSY:
				EM_LogoEPosY = (DOUBLE)((*AnimCommand)->Parameter);
				EM_LogoCPosY = (DOUBLE)((*AnimCommand)->Parameter);
				break;

			case AC_FADE_FROM_BLACK:
				EM_origPalette = (ULONG *)((*AnimCommand)->Parameter);
				tmpPalette[0] = EM_origPalette[0];
				memset(&tmpPalette[1], 0, ((tmpPalette[0]>>16)*3+1)*4);
				EM_FadeFlag = FADE_FROM_BLACK;
				break;

			case AC_FADE_TO_BLACK:
				EM_origPalette = (ULONG *)((*AnimCommand)->Parameter);
				memcpy(tmpPalette, EM_origPalette, ((EM_origPalette[0]>>16)*3+2)*4);
				EM_FadeFlag = FADE_TO_BLACK;
				break;

			case AC_DO:
				Time = (DOUBLE)((*AnimCommand)->Parameter);

				for(i=0; i<ONumber; i++)
					if(AnimObjects[i].Flags & AOF_ACTIVE)
					{
						AnimObjects[i].dPosX /= Time;
						AnimObjects[i].dPosY /= Time;
						AnimObjects[i].dPosZ /= Time;
						AnimObjects[i].dRotX /= Time;
						AnimObjects[i].dRotY /= Time;
						AnimObjects[i].dRotZ /= Time;
					}

				EM_LogoDPosX /= Time;
				EM_LogoDPosY /= Time;

				switch(EM_FadeFlag)
				{
					case FADE_FROM_BLACK:
						FadeInSupport1(dPalette, EM_origPalette, 0L, 0L, 0L, (LONG)Time);
						break;

					case FADE_TO_BLACK:
						FadeOutSupport1(dPalette, EM_origPalette, 0L, 0L, 0L, (LONG)Time);
						break;

				}

				*Time2Go = (ULONG)Time;
				(*AnimCommand)++;
				return IAL_OK;

			case AC_QUIT:
				return DA_NORMAL_TERMINATION;

			default:
				return DA_ILLEGAL_COMMAND;
		}

		(*AnimCommand)++;
	}
}
