/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * This file has been modified from the original.
 * 
 * The original file can be found at:
 *		https://code.google.com/p/replicaisland/
 */

package com.replica.core.components;


import com.replica.core.BaseObject;
import com.replica.core.GameObject;
import com.replica.core.PhasedObjectManager;
import com.replica.core.graphics.AnimationFrame;
import com.replica.core.graphics.DrawableBitmap;
import com.replica.core.graphics.DrawableFactory;
import com.replica.core.graphics.SpriteAnimation;

/**
 * Provides an interface for controlling a sprite with animations. Manages a
 * list of animations and provides a drawable surface with the correct animation
 * frame to a render component each frame. Also manages horizontal and vertical
 * flipping.
 */
public class SpriteComponent extends GameComponent {

	private PhasedObjectManager mAnimations;
	private float mAnimationTime;
	private int mCurrentAnimationIndex;
	private int mWidth;
	private int mHeight;
	private float mOpacity;
	private RenderComponent mRenderComponent;
	private DynamicCollisionComponent mCollisionComponent;
	private boolean mVisible;
	private SpriteAnimation mCurrentAnimation;
	private boolean mAnimationsDirty;
	boolean mActive;

	public SpriteComponent(int width, int height) {
		super();
		mAnimations = new PhasedObjectManager();
		reset();
		mWidth = width;
		mHeight = height;
		setPhase(ComponentPhases.PRE_DRAW.ordinal());
	}

	public SpriteComponent() {
		super();
		mAnimations = new PhasedObjectManager();
		reset();
		setPhase(ComponentPhases.PRE_DRAW.ordinal());
	}

	@Override
	public void reset() {
		mWidth = 0;
		mHeight = 0;
		mVisible = true;
		mCurrentAnimationIndex = -1;
		mAnimations.removeAll();
		mAnimations.commitUpdates();
		mAnimationTime = 0.0f;
		mRenderComponent = null;
		mCollisionComponent = null;
		mCurrentAnimation = null;
		mOpacity = 1.0f;
		mAnimationsDirty = false;
		mActive = true;
	}

	@Override
	public void update(float timeDelta, BaseObject parent) {
		
		if (!mActive) {
			return;
		}
		
		
		mAnimationTime += timeDelta;
		final PhasedObjectManager animations = mAnimations;
		final int currentAnimIndex = mCurrentAnimationIndex;

		if (mAnimationsDirty) {
			animations.commitUpdates();
			mAnimationsDirty = false;
		}
		boolean validFrameAvailable = false;
		if (animations.getCount() > 0 && currentAnimIndex != -1) {
			SpriteAnimation currentAnimation = mCurrentAnimation;

			if (currentAnimation == null && currentAnimIndex != -1) {
				currentAnimation = findAnimation(currentAnimIndex);
				if (currentAnimation == null) {
					// We were asked to play an animation that doesn't exist.
					// Revert to our
					// default animation.
					// TODO: throw an assert here?
					mCurrentAnimation = (SpriteAnimation) animations.get(0);
					currentAnimation = mCurrentAnimation;
				} else {
					mCurrentAnimation = currentAnimation;
				}
			}
			
			GameObject parentObject = (GameObject) parent;
			AnimationFrame currentFrame = currentAnimation
					.getFrame(mAnimationTime);
			if (currentFrame != null) {
				validFrameAvailable = true;
				final RenderComponent render = mRenderComponent;
				if (render != null) {
					
					final DrawableFactory factory = sSystemRegistry.drawableFactory;
					if (mVisible && currentFrame.texture != null
							&& factory != null) {
						// Fire and forget. Allocate a new bitmap for this
						// animation frame, set it up, and
						// pass it off to the render component for drawing.
						DrawableBitmap bitmap = factory
								.allocateDrawableBitmap();
						bitmap.setWidth(mWidth);
						bitmap.setHeight(mHeight);
						bitmap.setOpacity(mOpacity);
						if (currentFrame.mCrop != null) {
							final int[] crop = currentFrame.mCrop;
							bitmap.setCrop(crop[0], crop[1], crop[2], crop[3]);
						} else {
							updateFlip(bitmap,
									parentObject.facingDirection.x < 0.0f,
									parentObject.facingDirection.y < 0.0f);
						}
						bitmap.setTexture(currentFrame.texture);
						render.setDrawable(bitmap);
					} else {
						render.setDrawable(null);
					}
				}

				if (mCollisionComponent != null) {
					mCollisionComponent.setCollisionVolumes(
							currentFrame.attackVolumes,
							currentFrame.vulnerabilityVolumes,
							null == currentFrame.mCrop);
				}
			}
		}

		if (!validFrameAvailable) {
			
			
			
			// No current frame = draw nothing!
			if (mRenderComponent != null) {
				mRenderComponent.setDrawable(null);
			}
			if (mCollisionComponent != null) {
				mCollisionComponent.setCollisionVolumes(null, null, false);
			}
		}
	}

	public final void playAnimation(int index) {
		if (mCurrentAnimationIndex != index) {
			mAnimationTime = 0;
			mCurrentAnimationIndex = index;
			mCurrentAnimation = null;
		}
	}

	public final SpriteAnimation findAnimation(int index) {
		return (SpriteAnimation) mAnimations.find(index);
	}

	public final void addAnimation(SpriteAnimation anim) {
		mAnimations.add(anim);
		mAnimationsDirty = true;
	}

	public final boolean animationFinished() {
		boolean result = false;
		if (mCurrentAnimation != null && !mCurrentAnimation.getLoop()
				&& mAnimationTime > mCurrentAnimation.getLength()) {
			result = true;
		}
		return result;
	}

	public final float getWidth() {
		return mWidth;
	}

	public final float getHeight() {
		return mHeight;
	}

	public final void setSize(int width, int height) {
		mWidth = width;
		mHeight = height;
	}

	protected final void updateFlip(DrawableBitmap bitmap, boolean horzFlip,
			boolean vertFlip) {
		bitmap.setFlip(horzFlip, vertFlip);
	}

	public final void setRenderComponent(RenderComponent component) {
		mRenderComponent = component;
	}

	public final void setCollisionComponent(DynamicCollisionComponent component) {
		mCollisionComponent = component;
	}

	public final boolean getVisible() {
		return mVisible;
	}

	public final void setVisible(boolean visible) {
		mVisible = visible;
	}

	public final float getCurrentAnimationTime() {
		return mAnimationTime;
	}

	public final void setCurrentAnimationTime(float time) {
		mAnimationTime = time;
	}

	public final void setOpacity(float opacity) {
		mOpacity = opacity;
	}

	public final int getCurrentAnimation() {
		return mCurrentAnimationIndex;
	}

	public final int getAnimationCount() {
		return mAnimations.getConcreteCount();
	}
	
	public final void setActive(boolean active) {
		mActive = active;
		if (!active) {
			if (mRenderComponent != null) {
				mRenderComponent.setDrawable(null);
			}
			if (mCollisionComponent != null) {
				mCollisionComponent.setCollisionVolumes(null, null, false);
			}
		}
	}
}
