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

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

#include <exec/memory.h>

#include "/Shared/SH_view.h"
#include "/Shared/SH_timer.h"
#include "TM_const.h"
#include "TM_structures.h"
#include "TM_animation.h"
#include "TM_render.h"

#include "/tools.h"

IMPORT LONG dPalette[256*3];
IMPORT ULONG tmpPalette[256*3+2];
ULONG *origPalette;
LONG FadeFlag;

LONG TM_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 = AllocVec((ONumber+1) * sizeof(ANIM_OBJECT),MEMF_PUBLIC|MEMF_CLEAR)) == NULL)
		return DA_MEMORY_PROBLEM;

	CurrentAnimCommand = AnimList;

	Time2Go = 0;
	OverfillTime = 0;

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

		XEvent();

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

		next_seq = FALSE;

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

			if(AnimTime < Time2Go)
			{
				switch(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, origPalette, (LONG)AnimTime);
						break;

					case FADE_FROM_WHITE:
						WaitTOF();
						LoadRGB32(DView->VP, tmpPalette);
						FadeInSupport2(dPalette, tmpPalette, 0xffffffffL, 0xffffffffL, 0xffffffffL, (LONG)AnimTime);
						break;

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


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

						ControlObject(&Objects[i]);
					}

				State = RenderFrame(DView, Scene);

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

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

					case FADE_FROM_WHITE:
						WaitTOF();
						LoadRGB32(DView->VP, origPalette);
						break;

					case FADE_TO_WHITE:
						MemSet(&tmpPalette[1], 255, ((tmpPalette[0]>>16)*3)*4);
						tmpPalette[256*3+1] = 0L;
						WaitTOF();
						LoadRGB32(DView->VP, tmpPalette);
						break;
				}

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

						ControlObject(&Objects[i]);
					}

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


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

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

	CurrentObject = 1;
	FadeFlag = NO_FADE;

	for(;;)
	{
		switch((*AnimCommand)->Command)
		{
			case AC_OBJECT:
				CurrentObject = (*AnimCommand)->Parameter +1;
				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_FADE_FROM_BLACK:
				origPalette = (ULONG *)((*AnimCommand)->Parameter);
				tmpPalette[0] = origPalette[0];
				MemSet(&tmpPalette[1], 0, ((tmpPalette[0]>>16)*3+1)*4);
				FadeFlag = FADE_FROM_BLACK;
				break;

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

			case AC_FADE_FROM_WHITE:
				origPalette = (ULONG *)((*AnimCommand)->Parameter);
				tmpPalette[0] = origPalette[0];
				MemSet(&tmpPalette[1], 255, ((tmpPalette[0]>>16)*3)*4);
				tmpPalette[256*3+1] = 0L;
				FadeFlag = FADE_FROM_WHITE;
				break;

			case AC_FADE_TO_WHITE:
				origPalette = (ULONG *)((*AnimCommand)->Parameter);
				CopyMem(origPalette, tmpPalette, ((origPalette[0]>>16)*3+2)*4);
				FadeFlag = FADE_TO_WHITE;
				break;

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

				for(i=0; i<ONumber+1; 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;
					}

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

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

					case FADE_FROM_WHITE:
						FadeInSupport1(dPalette, origPalette, 0xffffffffL, 0xffffffffL, 0xffffffffL, (LONG)Time);
						break;

					case FADE_TO_WHITE:
						FadeOutSupport1(dPalette, origPalette, 0xffffffffL, 0xffffffffL, 0xffffffffL, (LONG)Time);
						break;
				}

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

			case AC_QUIT:
				return DA_NORMAL_TERMINATION;

			default:
				return DA_ILLEGAL_COMMAND;
		}

		(*AnimCommand)++;
	}
}

VOID ControlObject(OBJECT *Object)
{
						Object->RotX %= ANGLES_NUMBER;
						while(Object->RotX < 0)
							Object->RotX += ANGLES_NUMBER;

						Object->RotY %= ANGLES_NUMBER;
						while(Object->RotY < 0)
							Object->RotY += ANGLES_NUMBER;

						Object->RotZ %= ANGLES_NUMBER;
						while(Object->RotZ < 0)
							Object->RotZ += ANGLES_NUMBER;

}