/*
 * Land Sea Air -- an interactive music game
 * demonstrating the "secret yanni" techique, with key changes
 * design, animation, audio, and code by peter "pdx" drescher
 * twittering machine
 * 
 */
package com.twittering.landseaair;

import java.util.Random;

import com.twittering.landseaair.R;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
//import android.graphics.Paint.Style;
//import android.graphics.Color;
import android.graphics.Rect;
import android.view.Gravity;
import android.widget.Toast;

public class LandAnimation {

    private Bitmap dCurrentBG;
    private Bitmap dNextBG;
    private Bitmap dBackground[] = new Bitmap[6];
    private int dBGcounter = 0;
    private static int backgroundHeight;
    private static int screenHeight;
    private int topBackgroundY = 0;
	private int bottomBackgroundY = 0;
	private Paint paintOpacity = new Paint();
	private int opacityOfBG = 0;
	private boolean changeBG = false;
    
    private Bitmap dSnake[] = new Bitmap[19];
    private Bitmap dRat[] = new Bitmap[5]; 
    private Bitmap dBlueFrog[] = new Bitmap[4];
    private Bitmap dGreenFrog[] = new Bitmap[4];

    private Random mRnd = new Random();
	FMODaudio mFMODaudio = new FMODaudio();
    private Canvas mCanvas;
    private Context mContext;
	private Activity mActivity;
	
    private static final int FPS = 30; // milliseconds
    private static final int BACKGROUND_SPEED = 2; //frames
    private static final int SNAKE_FRAME_SPEED = 3; // frames
    private static final int SNAKE_FRAMES = 18; // index, start at 0
    private static final int SNAKE_WIDTH = 120; // pixels
    private static final int RAT_RELEASE = 3000; // milliseconds
    private static final int DRUNKARDS_WALK_AMT = 50; // pixels
    private static final int RATY_SPEED = 10; // pixels
    private static final int RATX_SPEED = 3; // pixels
    private static final int FROG_WIDTH = 60; // pixels
    private static final int FROG_HEIGHT = 120; // pixels

    private float dSnakeX = 0;
    private float dSnakeY = 0;
    private int dSnakeDx = 0;
    private int dSnakeFrameCtr = 0;
	
    private int nextRatX[] = {0,0,0,0,0}; // tracking five rats
    private int dRatX[] = {0,0,0,0,0};
    private int dRatY[] = {0,0,0,0,0};
    private int dRatFrame[] = {0,0,0,0,0};
	private boolean ratSwallowed[] = {true, true, true, true, true}; //release the rats!
    
    private int dFrogX = 0; 
    private int dFrogY = 0;
    private int dFrogFrame = 0;
	private boolean frogSwallowed = true;
	private int frogColor = 0;
	
	private int frogLeapTime = 2000; //time until next leap (may vary randomly later)
	private boolean frogLeaping = false;
	private int leapState = 0;
	private int leapFrameCtr = 0;
	private static final int LEAP_START = 1;
	private static final int LEAPING    = 2;
	private static final int LEAP_STOP  = 3;
    private int leapDirection = 0;

    public long mGameTimer;
	private long dRatFrameTimer[] = new long[5];
    private long dRatTimer[] = {0,0,0,0,0};
	private long dFrogFrameTimer = 0;
    private long dFrogTimer = 0;

    public LandAnimation() { }
    
    public void init(Context context) { 
    	mContext = context;
		mActivity = (Activity) context;
    	
        dSnake[0]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake01);
        dSnake[1]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake02);
        dSnake[2]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake03);
        dSnake[3]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake04);
        dSnake[4]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake05);
        dSnake[5]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake06);
        dSnake[6]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake07);
        dSnake[7]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake08);
        dSnake[8]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake09);
        dSnake[9]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake10);
        dSnake[10] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake11);
        dSnake[11] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake12);
        dSnake[12] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake13);
        dSnake[13] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake14);
        dSnake[14] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake15);
        dSnake[15] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake16);
        dSnake[16] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake17);
        dSnake[17] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake18);
        dSnake[18] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_snake19);
    	dSnakeY = -dSnake[0].getHeight();

    	Bitmap fromRes;
		screenHeight = (int) GameSurfaceView.mScreenHeight;
        fromRes = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_bg_grass);
        dBackground[0] = Bitmap.createScaledBitmap(fromRes, (int) GameSurfaceView.mScreenWidth, screenHeight, true);
        fromRes = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_bg_moss);
        dBackground[1] = Bitmap.createScaledBitmap(fromRes, (int) GameSurfaceView.mScreenWidth, screenHeight, true);
        fromRes = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_bg_dirt);
        dBackground[2] = Bitmap.createScaledBitmap(fromRes, (int) GameSurfaceView.mScreenWidth, screenHeight, true);
        fromRes = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_bg_mud);
        dBackground[3] = Bitmap.createScaledBitmap(fromRes, (int) GameSurfaceView.mScreenWidth, screenHeight, true);
        fromRes = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_bg_sand);
        dBackground[4] = Bitmap.createScaledBitmap(fromRes, (int) GameSurfaceView.mScreenWidth, screenHeight, true);
        fromRes = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_bg_rock);
        dBackground[5] = Bitmap.createScaledBitmap(fromRes, (int) GameSurfaceView.mScreenWidth, screenHeight, true);
        
        dRat[0]       = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_blackrat01);
        dRat[1]       = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_blackrat02);
        dRat[2]       = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_blackrat03);
        dRat[3]       = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_blackrat04);
        dRat[4]       = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_blackrat05);
        dBlueFrog[0]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_bluefrog01);
        dBlueFrog[1]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_bluefrog02);
        dBlueFrog[2]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_bluefrog03);
        dBlueFrog[3]  = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_bluefrog04);
        dGreenFrog[0] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_greenfrog01);
        dGreenFrog[1] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_greenfrog02);
        dGreenFrog[2] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_greenfrog03);
        dGreenFrog[3] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.land_greenfrog04);   
        
        startAnimation();
    }
    
    public void startAnimation() {
        dCurrentBG = dBackground[0];
        dNextBG    = dBackground[1];
        backgroundHeight = dCurrentBG.getHeight();
        changeBG = false;
        dBGcounter = 0;
        topBackgroundY = 0;
    	opacityOfBG = 0;
        dFrogX = 0; dFrogY = 0; dFrogFrame = 0; frogSwallowed = true;
        frogLeaping = false;
        showKeyToast();
    }
    
	public void updatePhysics(float mSensorX[]) {
		int length = mSensorX.length;
		int i = 0;
		float totalSensorX = 0f;
		while (i < length){
			totalSensorX += mSensorX[i] * 1f;
			i += 1;
		}
		float averageSensorX = totalSensorX / length;

		//bounce off edges
		dSnakeX = dSnakeX - averageSensorX;
		if (dSnakeX >= GameSurfaceView.mScreenWidth - SNAKE_WIDTH) {
			dSnakeX  = GameSurfaceView.mScreenWidth - SNAKE_WIDTH;
		}
		if (dSnakeX < 0) {
			dSnakeX = 0;
		}
	}
	
	private void checkCollision(){
		//make a 36x36 rect at the position of the snake's head
		float offsetX;
		switch (dSnakeDx) {
		case 0:  offsetX = 0;  break;
		case 1:  offsetX = 0;  break;
		case 2:  offsetX = 0;  break;
		case 3:  offsetX = 0;  break;
		case 4:  offsetX = 1;  break;
		case 5:  offsetX = 9;  break;
		case 6:  offsetX = 22; break;
		case 7:  offsetX = 27; break;
		case 8:  offsetX = 39; break;
		case 9:  offsetX = 46; break;
		case 10: offsetX = 48; break;
		case 11: offsetX = 51; break;
		case 12: offsetX = 52; break;
		case 13: offsetX = 50; break;
		case 14: offsetX = 46; break;
		case 15: offsetX = 37; break;
		case 16: offsetX = 31; break;
		case 17: offsetX = 19; break;
		case 18: offsetX = 12; break;
		default: offsetX = 0;  break;
		}
		
		int snakeheadX = (int) ((offsetX / SNAKE_WIDTH) * dSnake[0].getWidth());
		Rect snakeheadRect = new Rect((int) dSnakeX + snakeheadX,
									  (int) dSnakeY + dSnake[0].getHeight() - 36,
									  (int) dSnakeX + snakeheadX + 36,
									  (int) dSnakeY + dSnake[0].getHeight());	

//		//test rect
//		Paint testpaint = new Paint();
//		testpaint.setColor(Color.RED);
//		testpaint.setStrokeWidth(3);
//		testpaint.setStyle(Style.STROKE);
//		mCanvas.drawRect(snakeheadRect, testpaint);
//		testpaint.setColor(Color.BLACK);
//		mCanvas.drawRect(dSnakeX, dSnakeY, dSnakeX + SNAKE_WIDTH, dSnakeY + dSnake[0].getHeight(), testpaint);

		//check if it intersects one of the  rats
		for (int i=0; i<5;i++) {
			if (!ratSwallowed[i]) {
				Rect ratRect = new Rect(dRatX[i], dRatY[i], dRatX[i] + dRat[0].getWidth(), dRatY[i] + dRat[0].getHeight());	
				if (Rect.intersects(snakeheadRect, ratRect)) {
					dRatTimer[i] = mGameTimer;
					ratSwallowed[i] = true;
					mFMODaudio.playRatSound();
				}
			}
		}
		
		//check if it intersects a frog, and that it's visible on screen
		if (!frogSwallowed) {
			Rect frogRect = new Rect(dFrogX, dFrogY, dFrogX + FROG_WIDTH, dFrogY + FROG_HEIGHT);
			if (Rect.intersects(snakeheadRect, frogRect) && screenHeight > dFrogY + FROG_HEIGHT) {
				dFrogTimer = mGameTimer;
				frogSwallowed = true;
				mFMODaudio.playFrogSound();
			}
		}
	}

	public void doDraw(Canvas canvas) {	
		mCanvas = canvas;
		drawBackground();
		drawFrog();
		drawRats();
		drawSnake();
		checkCollision();
	}
	
	private void drawBackground() {
		if (mFMODaudio.checkBGflag()) {
			changeBackground();
		}
		
		//scroll background up, wrapping from below
	    topBackgroundY -= BACKGROUND_SPEED;
	    bottomBackgroundY = topBackgroundY + backgroundHeight;
	    if (bottomBackgroundY <= 0) {
	    	bottomBackgroundY = backgroundHeight;
	    	topBackgroundY = 0;
	    }
	    
        if (changeBG) {
        	opacityOfBG += 20;
        	if (opacityOfBG > 255) {
        		opacityOfBG = 255;
        		changeBG = false;
        		dCurrentBG = dNextBG;
        	}
        	paintOpacity.setAlpha(opacityOfBG - 255);
	        mCanvas.drawBitmap(dCurrentBG, 0, topBackgroundY,    paintOpacity);
	        mCanvas.drawBitmap(dCurrentBG, 0, bottomBackgroundY, paintOpacity);	
        	paintOpacity.setAlpha(opacityOfBG);
	        mCanvas.drawBitmap(dNextBG,    0, topBackgroundY,    paintOpacity);
	        mCanvas.drawBitmap(dNextBG,    0, bottomBackgroundY, paintOpacity);	
        } else {
	        mCanvas.drawBitmap(dCurrentBG, 0, topBackgroundY, null);
	        mCanvas.drawBitmap(dCurrentBG, 0, bottomBackgroundY, null);	
        }
	}
	
	private void changeBackground() {
		dBGcounter++;
		if (dBGcounter > 5) {  //wrap the counter
			dBGcounter = 0;
		}
		dNextBG = dBackground[dBGcounter];
		changeBG = true;
		opacityOfBG = 0;
		showKeyToast();
	}

	private void showKeyToast() {
		mActivity.runOnUiThread(new Runnable() {
		    public void run() {
				CharSequence keyText;
				switch (dBGcounter) {
				case 0: keyText = "Key of C minor (James Bond)"; break;
				case 1: keyText = "Key of Bb minor (Jason Bourne)"; break;
				case 2: keyText = "Key of Ab Major (samba)"; break;
				case 3: keyText = "Key of F# minor (salsa)"; break;
				case 4: keyText = "Key of E maqam (arabic)"; break;
				case 5: keyText = "Key of D minor (rock)"; break;
		    	default:keyText = "???";break;
		    	}
				Toast keyToast = Toast.makeText(mActivity, keyText, Toast.LENGTH_SHORT);
		    	keyToast.setGravity(Gravity.TOP|Gravity.LEFT, 0, 0);
		    	keyToast.show();
		    }
		});
	}
		
	private void drawRats() {
		for (int i=0;i<5;i++) {
			drawARat(i);
		}
	}
	
	private void drawARat(int ratNumber) {
		// when a rat gets swallowed, wait an increasing amount of time per rat, then re-release
		if (ratSwallowed[ratNumber]) {
			if (mGameTimer > dRatTimer[ratNumber] + (RAT_RELEASE + (ratNumber*1000))) {
				ratSwallowed[ratNumber] = false;
				dRatY[ratNumber] = (int) (GameSurfaceView.mScreenHeight + dRat[0].getHeight());
			} else {
				return;  // don't draw a rat
			}
		}
		
    	// for each frame, get the next sprite of the current rat, and move down the Y axis
		if( mGameTimer > dRatFrameTimer[ratNumber] + FPS) {
			dRatFrameTimer[ratNumber] = mGameTimer;
	        dRatFrame[ratNumber]++;
	        if (dRatFrame[ratNumber] >= 5) {
	        	dRatFrame[ratNumber] = 0;
	        }
	        
    		dRatY[ratNumber] -= RATY_SPEED + ratNumber; // vary the speed per rat
    		if (dRatY[ratNumber] < -dRat[0].getHeight()) {
    			dRatY[ratNumber] = (int) (GameSurfaceView.mScreenHeight + dRat[0].getHeight());
    		}
    		
    		// determine the X axis using a "drunkard's walk" (brownian motion) algorithm:
    		// flip a coin to determine which direction to move a small random amount, then move to that point incrementally
    		if (dRatX[ratNumber] < nextRatX[ratNumber]) {
    			dRatX[ratNumber] += RATX_SPEED + ratNumber; // vary the speed per rat
    			if (dRatX[ratNumber] >= nextRatX[ratNumber]) {
    				dRatX[ratNumber] = nextRatX[ratNumber];
    			}
    			
    		} else
    		if (dRatX[ratNumber] > nextRatX[ratNumber]) {
    			dRatX[ratNumber] -= RATX_SPEED + ratNumber; // vary the speed per rat
    			if (dRatX[ratNumber] <= nextRatX[ratNumber]) {
    				dRatX[ratNumber] = nextRatX[ratNumber];
    			}
    			
    		} else
    		if (dRatX[ratNumber] == nextRatX[ratNumber]) {
	    		int flipCoin = mRnd.nextInt(2);
	    		if (flipCoin == 0) {
	    			nextRatX[ratNumber] += mRnd.nextInt(DRUNKARDS_WALK_AMT);
	    		} else {
	    			nextRatX[ratNumber] -= mRnd.nextInt(DRUNKARDS_WALK_AMT);
	    		}
	    		
	    		// bounce off screen edges
	    		if (nextRatX[ratNumber] > GameSurfaceView.mScreenWidth - dRat[0].getWidth()) {
	    			nextRatX[ratNumber] -= DRUNKARDS_WALK_AMT;
	    		} else if (nextRatX[ratNumber] < 0) {
	    			nextRatX[ratNumber] += DRUNKARDS_WALK_AMT;
	    		}
    		}
		}
        mCanvas.drawBitmap(dRat[dRatFrame[ratNumber]], dRatX[ratNumber], dRatY[ratNumber], null);
	}
	
	private void drawFrog() {
		// when a frog gets swallowed, wait 10 seconds, and re-release it
		if (frogSwallowed) {
			if (mGameTimer > dFrogTimer + 10000) {
				frogColor = mRnd.nextInt(2); // blue or green
				frogSwallowed = false;
				dFrogY = (int) (GameSurfaceView.mScreenHeight + FROG_HEIGHT);
				dFrogX = mRnd.nextInt((int) (GameSurfaceView.mScreenWidth - FROG_WIDTH));
			} else {
				return;  // don't draw the frog
			}
		}
		
		if ((mGameTimer > dFrogTimer + frogLeapTime) && !frogLeaping) {
			dFrogTimer = mGameTimer;
			frogLeaping = true;
			leapFrameCtr = 0;
			leapState = LEAP_START;
			leapDirection = mRnd.nextInt(3); //left, right or center
		}
		
		if (frogLeaping) {
			doLeapFrog();
		} else {		
		// move down the Y axis to stay in sync with the moving background
    		dFrogY -= BACKGROUND_SPEED;
    		if (dFrogY < -FROG_HEIGHT) {
				dFrogTimer = mGameTimer;
				dFrogY = (int) (GameSurfaceView.mScreenHeight + FROG_HEIGHT);
    		}
		}
		
    	if (frogColor == 0) {
            mCanvas.drawBitmap(dBlueFrog[dFrogFrame], dFrogX, dFrogY, null);
    	} else {
            mCanvas.drawBitmap(dGreenFrog[dFrogFrame], dFrogX, dFrogY, null);
    	}
	}

	private void doLeapFrog() {		
		//increment the frog frame while moving forward on the Y axis
		//stay on frame[3] during the leap
		//decrement the frog frame on landing
		if( mGameTimer > dFrogFrameTimer + FPS) {
			dFrogFrameTimer = mGameTimer;
			//set X-axis position
			switch (leapDirection) {
			case 0: //Leap Left
	    		dFrogX -= 5;
		        if (dFrogX < 0) { //hit left wall
		        	dFrogX = 0;
		        }
				break;
			case 1: //Leap Right
	    		dFrogX += 5;
		        if (dFrogX > (int) (GameSurfaceView.mScreenWidth - FROG_WIDTH)) { //hit right wall
		        	dFrogX = (int) (GameSurfaceView.mScreenWidth - FROG_WIDTH);
		        }
				break;
			default: //Leap Center
				break;
			}
			
			//set Y-axis position and frame
			switch (leapState) {
			case LEAP_START:
	    		dFrogY -= BACKGROUND_SPEED + 20;
		        dFrogFrame++;
		        if (dFrogFrame == 3) {
		        	leapState = LEAPING;
		        }
				break;
			case LEAPING:
	    		dFrogY -= BACKGROUND_SPEED + 20;
				leapFrameCtr++;
				if (leapFrameCtr > 5) {
		        	leapState = LEAP_STOP;
				}
				break;
			case LEAP_STOP:
		        dFrogFrame--;
		        if (dFrogFrame == 0) {
		        	frogLeaping = false;
		        }
				break;
			}
		}
	}
	
	private void drawSnake() {	
		if (dSnakeFrameCtr >= SNAKE_FRAME_SPEED) {
			dSnakeFrameCtr = 0;
			dSnakeY += 10;
			if (dSnakeY > GameSurfaceView.mScreenHeight) {
				dSnakeY = -dSnake[0].getHeight();
			}
			dSnakeDx++;
			if (dSnakeDx > SNAKE_FRAMES) {
				dSnakeDx = 0;
			}
		} else {
			dSnakeFrameCtr++;
		}
        mCanvas.drawBitmap(dSnake[dSnakeDx], dSnakeX, dSnakeY, null);
	}
	
	public void cleanLand() {
		int i;
		dCurrentBG.recycle(); dCurrentBG = null;
		dNextBG.recycle(); dNextBG = null;

		for (i=0;i<6;i++){
			dBackground[i].recycle(); dBackground[i] = null;
		}
		for (i=0;i<19;i++){
			dSnake[i].recycle(); dSnake[i] = null;
		}
		for (i=0;i<5;i++){
			dRat[i].recycle(); dRat[i] = null;
		}
		for (i=0;i<4;i++){
			dBlueFrog[i].recycle(); dBlueFrog[i] = null;
			dGreenFrog[i].recycle(); dGreenFrog[i] = null;
		}
		System.gc();
	}
}
