// Copyright 2001-2003, Danger, Inc.  All Rights Reserved.
// This file is subject to the Danger, Inc. Sample Code License,
// which is provided in the file SAMPLE_CODE_LICENSE.
// Copies are also available from http://developer.danger.com/
package com.danger.rocks;

import danger.app.*;
import danger.audio.*;
import danger.system.*;
import danger.ui.*;
import danger.util.*;


/**
 * DOCUMENT ME!
 *
 * @author $author$
 * @version $Revision$
 */
public class HeroObject
{
	//~ Static fields/initializers ---------------------------------------------

	private static final int[] YELLOW = { 40, 240, 120 };
	private static final int[] RED   = { 0, 240, 120 };
	private static final int[] GREEN = { 80, 240, 120 };
	private static final int FIXED_TWO_THRIDS = Fixed.fromReal(0, 2, 3);
	private static int SPEAK_OFFSET_X   = 11;
	private static int SPEAK_OFFSET_Y   = 4;
	private static int DAMAGE_LED_TIMER = 30;

	//~ Instance fields --------------------------------------------------------

	private int mSpeakMsg   = -1;
	private int mSpeakTimer = 0;

	// data
	boolean		 mThrustOn;
	int			 mRotateDirection = 0; // -1 left, 0 is off, 1 is right
	int			 mDamage;
	int			 mShield;
	int			 mFrameCount;
	boolean		 mDead;
	int			 mDrawMode;
	int			 mModeTimer		  = 0;
	int			 mLEDTimer		  = 1;
	int			 mStartX;
	int			 mStartY;
	HeroImage    mHeroImage		  = null;
	MotionObject mMotion		  = null;
	Rect		 mImageRect		  = new Rect();
	Rect		 mBoundRect		  = new Rect();

	//~ Constructors -----------------------------------------------------------

	/**
	 * Creates a new HeroObject object.
	 *
	 * @param bounds DOCUMENT ME!
	 */
	public HeroObject(Rect bounds)
	{
		int x;
		int y;

		mHeroImage     = new HeroImage();

		mDrawMode	   = drawmode.BLINK;
		mModeTimer     = 20;
		x			   = (bounds.getWidth() / 2) + bounds.left;
		y			   = (bounds.getHeight() / 2) + bounds.top;
		mStartX		   = x;
		mStartY		   = y;
		mMotion		   = new MotionObject(x, y, 0, 0, bounds,
				HeroImage.TOTAL_HERO_FRAMES, 0, mHeroImage.getSizeX(),
				mHeroImage.getSizeY());
	}

	//~ Methods ----------------------------------------------------------------

	/**
	 * DOCUMENT ME!
	 */
	public void newLife()
	{
		mDamage = 0;
		Hardware.setLEDHSV(0, 0, 0);
		mDead	    = false;
		mShield     = 0;
	}

	/**
	 * DOCUMENT ME!
	 */
	public void kill()
	{
		mDead = true;
		stopThrust();
	}

	/**
	 * DOCUMENT ME!
	 */
	public void reset()
	{
		mDrawMode	   = drawmode.INVISIBLE;
		mModeTimer     = 20;
		mLEDTimer	   = 1;
		mMotion.setAngle(0);
		mMotion.setSpeed(0);
		mMotion.setX(mStartX);
		mMotion.setY(mStartY);
		stopThrust();
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param inPen DOCUMENT ME!
	 */
	public void draw(Pen inPen)
	{
		inPen.setColor(Color.BLACK);

		if (mHeroImage != null)
		{
			boolean draw = true;
			Rect    r = getRect();

			switch (mDrawMode)
			{
				case drawmode.NORMAL:
					draw = true;

					break;

				case drawmode.DEAD:

					// draw game over
					//		inPen.DrawText(mMotion.GetX() - 20, mMotion.GetY(), RockStrings.kGameOverMan);
					Hardware.setLEDHSV(0, 0, 0);
					draw = false;

					break;

				case drawmode.INVISIBLE:
					draw = false;

					break;

				case drawmode.BLINK:

					if ((mModeTimer & 2) == 0)
					{
						draw = false;
					}

					break;

				case drawmode.SPIN_CRASH:
					break;
			}

			if (draw == true)
			{
				mHeroImage.draw(inPen, mMotion.getX(), mMotion.getY(),
					mMotion.getCurrentFrame());
			}

			if (mShield > 0)
			{
				r.inset(-5);

				if (Screen.isColor())
				{
					int rc = (mFrameCount << 4) & 0xff;
					int gc = (~((mFrameCount << 2) & 0xff)) + 255;
					int bc = (mFrameCount << 5) & 0xff;
					inPen.setColor(Color.pack(rc, gc, bc));
					inPen.drawOval(r);
					inPen.setColor(Color.BLACK);
				}
				else
				{
					inPen.drawOval(r);
				}

				mShield--;
			}

			if (mLEDTimer > 0)
			{
				mLEDTimer--;

				if (mLEDTimer < (DAMAGE_LED_TIMER / 2))
				{
					setDamageLED();
				}
			}
			else
			{
				mLEDTimer = DAMAGE_LED_TIMER;
				Hardware.setLEDHSV(0, 0, 0);
			}

			if (mSpeakTimer > 0)
			{
				mSpeakTimer--;

				if (mSpeakMsg != -1)
				{
					inPen.drawText(mMotion.getX() - SPEAK_OFFSET_X,
						mMotion.getY() - SPEAK_OFFSET_Y,
						RockStrings.sHeroSpeak[mSpeakMsg]);
				}
			}

			if (rocks.mDEBUG == true)
			{
				if (r.left > r.right)
				{
					int a = r.left;
					r.left	    = r.right;
					r.right     = a;
				}

				if (r.bottom < r.top)
				{
					int a = r.bottom;
					r.bottom     = r.top;
					r.top		 = a;
				}

				inPen.drawRect(r);
			}
		}
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @return DOCUMENT ME!
	 */
	public Rect getRect()
	{
		mBoundRect = mHeroImage.getRectForHero(mMotion.getCurrentFrame());
		mBoundRect.inset(3);
		mBoundRect.translate(mMotion.getX(), mMotion.getY());

		return mBoundRect;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @return DOCUMENT ME!
	 */
	public Rect getImageRect()
	{
		mImageRect = mHeroImage.getRectForHero(mMotion.getCurrentFrame());
		mImageRect.inset(-5);
		mImageRect.translate(mMotion.getX(), mMotion.getY());

		return mImageRect;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @return DOCUMENT ME!
	 */
	public boolean isDead()
	{
		if (mDrawMode == drawmode.DEAD)
		{
			return true;
		}

		return false;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @return DOCUMENT ME!
	 */
	public boolean isActive()
	{
		if (mDrawMode == drawmode.NORMAL)
		{
			return true;
		}

		return false;
	}

	private void setDamageLED()
	{
		switch (mDamage)
		{
			default:
				Hardware.setLEDHSV(0, 0, 0);

				break;

			case 25:
				Hardware.setLEDHSV(GREEN[0], GREEN[1], GREEN[2]);

				break;

			case 50:
				Hardware.setLEDHSV(YELLOW[0], YELLOW[1], YELLOW[2]);

				break;

			case 75:
				Hardware.setLEDHSV(RED[0], RED[1], RED[2]);

				break;
		}
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @return DOCUMENT ME!
	 */
	public boolean sustainDamage()
	{
		mDamage += 25;

		if (mDamage > 99)
		{
			return false;
		}

		mShield = 6;

		return true;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @return DOCUMENT ME!
	 */
	public boolean hit()
	{
		boolean hit = false;

		if (mDrawMode == drawmode.NORMAL)
		{ // only hurt if we're normal
			mDrawMode	   = drawmode.SPIN_CRASH;
			mModeTimer     = 30;
			hit			   = true;
			mHeroImage.setCrash(true);

			mSpeakTimer = 28;
			mSpeakMsg++;

			if (RockStrings.sHeroSpeak[mSpeakMsg] == null)
			{
				mSpeakMsg = 0;
			}
		}

		return hit;
	}

	private boolean safeMode()
	{
		if (mDrawMode != drawmode.NORMAL)
		{
			if (mModeTimer < 3)
			{
				mModeTimer = 3;
			}

			return false;
		}

		return true;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param missile DOCUMENT ME!
	 *
	 * @return DOCUMENT ME!
	 */
	public boolean collidesWith(MissileObject missile)
	{
		Rect rect = getRect();
		int  x = missile.mMotion.getX();
		int  y = missile.mMotion.getY();

		if (missile.mOrigin != this)
		{
			return rect.contains(x, y);
		}

		return false;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param rock DOCUMENT ME!
	 *
	 * @return DOCUMENT ME!
	 */
	public boolean collidesWith(RockObject rock)
	{
		Rect rockrect = rock.getRect();

		Rect me = getRect();
		me.inset(5);

		boolean hit = me.intersects(rockrect);

		if (rock.isCrashed() == true)
		{
			hit = false;
		}

		if (hit)
		{
			return safeMode();
		}

		return false;
	}

	/**
	 * DOCUMENT ME!
	 *
	 * @param w DOCUMENT ME!
	 */
	public void update(RockWindow w)
	{
		mFrameCount++;

		// invalidate old position
		//w.InvalidateThisRect(getImageRect());
		mMotion.Update();

		// invalidate new position
		//w.InvalidateThisRect(getImageRect());
		switch (mDrawMode)
		{
			case drawmode.NORMAL:
				break;

			case drawmode.INVISIBLE:
				mModeTimer--;

				if (mModeTimer == 0)
				{
					mDrawMode	   = drawmode.BLINK;
					mModeTimer     = 20;
					mHeroImage.setCrash(false);
				}

				break;

			case drawmode.BLINK:
				mModeTimer--;

				if (mModeTimer == 0)
				{
					mDrawMode = drawmode.NORMAL;
				}

				break;

			case drawmode.SPIN_CRASH:
				mModeTimer--;

				if (mModeTimer != 0)
				{
					mMotion.setAngle(mMotion.getAngle() + 10);
				}
				else
				{
					if (mDead == false)
					{
						reset();
					}
					else
					{
						mDrawMode = drawmode.DEAD;
					}
				}

				break;
		}

		if (mThrustOn)
		{
			if ((mFrameCount & 1) == 0)
			{
				mMotion.addSpeed(FIXED_TWO_THRIDS);
			}
		}

		rotate(mRotateDirection);
	}

	// direction > 0 is right, < 0 is left
	public void startRotate(int direction)
	{
		if (direction > 0)
		{
			mRotateDirection = 1;
		}
		else
		{
			mRotateDirection = -1;
		}
	}

	/**
	 * DOCUMENT ME!
	 */
	public void stopRotate()
	{
		mRotateDirection = 0;
	}

	// direction > 0 is right, < 0 is left
	public void rotate(int direction)
	{
		if (direction > 0)
		{
			// rotate right
			mMotion.addAngle(HeroImage.HERO_ANGLE_PER_FRAME);
		}

		if (direction < 0)
		{
			// rotate left
			mMotion.addAngle(-HeroImage.HERO_ANGLE_PER_FRAME);
		}
	}

	/**
	 * DOCUMENT ME!
	 */
	public void startThrust()
	{
		if (mDead == false)
		{
			mHeroImage.setThrust(true);
			RockAudio.startThrust();
			mThrustOn = true;
		}
	}

	/**
	 * DOCUMENT ME!
	 */
	public void stopThrust()
	{
		mHeroImage.setThrust(false);
		RockAudio.stopThrust();
		mThrustOn = false;
	}

	//~ Inner Interfaces -------------------------------------------------------

	interface drawmode
	{
		//~ Static fields/initializers -----------------------------------------

		public static final int NORMAL     = 0;
		public static final int SPIN_CRASH = 1;
		public static final int BLINK	   = 2;
		public static final int INVISIBLE  = 3;
		public static final int DEAD	   = 4;
	}
}
