/*
 * 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 android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;

public class GameSurfaceView extends SurfaceView implements SurfaceHolder.Callback, SensorEventListener  {

	private GameThread mThread;
	private Context mContext;
	private Activity mActivity;
	private boolean mThreadIsRunning = false;
	
	private Sensor mAccelerometer;
	private SensorManager mSensorManager;
	private PowerManager mPowerManager;
	private WindowManager mWindowManager;
	private Display mDisplay;
	private WakeLock mWakeLock;
	private float mSensorX[] = new float[10];
	private float mSensorY[] = new float[10];
	
	private AirAnimation mAirAnimation;
	private SeaAnimation mSeaAnimation;
	private LandAnimation mLandAnimation;
	
	/**Screen metrics**/
	public static float mScreenWidth;
	public static float mScreenHeight;
	
    public static final int LAND_LEVEL = 1;
    public static final int SEA_LEVEL  = 2;
    public static final int AIR_LEVEL  = 3;
    public int levelPlaying = 0;
    public boolean gamePaused = false;
	
	public GameSurfaceView(Context context, AttributeSet attrs) {
		super(context, attrs);
		
		mContext = context;
		mActivity = (Activity) context;

        SurfaceHolder holder = getHolder();
        holder.addCallback(this);
        
        mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
		mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        mDisplay = mWindowManager.getDefaultDisplay();
        mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass().getName());

        init();
        
//		create the game thread, to be started in surfaceCreated
        mThread = new GameThread(holder, mContext, new Handler() {
        	@Override
            public void handleMessage(Message m) {
            }
        });

        setFocusable(true); 
    }
	
	private void init() {		
		mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		DisplayMetrics metrics = new DisplayMetrics();
        mActivity.getWindowManager().getDefaultDisplay().getMetrics(metrics);        
        mScreenWidth = metrics.widthPixels;
        mScreenHeight = metrics.heightPixels;
		mSeaAnimation = new SeaAnimation();
		mAirAnimation = new AirAnimation();
		mLandAnimation = new LandAnimation();
                
        for (int i = 0; i < mSensorX.length; i++) {
        	mSensorX[i] = 0.0f;
        }
        for (int i = 0; i < mSensorY.length; i++) {
        	mSensorY[i] = 0.0f;
        }      
    }
	
	public void initAir() {
		mAirAnimation.init(mContext);
	}
	
	public void initSea() {
		mSeaAnimation.init(mContext);
	}
	
	public void initLand() {
		mLandAnimation.init(mContext);
	}
	
	public void pauseAnimation() {
		gamePaused = true;
	}
	
	public void restartAirAnimation() {
		mAirAnimation.startAnimation();
    	gamePaused = false;
	}
	
	public void restartSeaAnimation() {
		mSeaAnimation.startAnimation();
    	gamePaused = false;
	}
	
	public void restartLandAnimation() {
		mLandAnimation.startAnimation();
    	gamePaused = false;
	}
	
	public void stopAirAnimation() {
		mAirAnimation.cleanAir();
	}
	
	public void stopSeaAnimation() {
		mSeaAnimation.cleanSea();
	}
	
	public void stopLandAnimation() {
		mLandAnimation.cleanLand();
	}
	
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		mWakeLock.acquire();
        mThread.setRunning(true);
		mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
        if (!mThreadIsRunning){
        	mThread.start();
        }
    }

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		boolean retry = true;
        mThread.setRunning(false);
        mSensorManager.unregisterListener(this);
        mWakeLock.release();

        while (retry) {
            try {
                mThread.join();
                retry = false;
                setThreadIsRunning(false);
            } catch (InterruptedException e) {
            	e.printStackTrace();
            }
        }	
	}
	
	private boolean getThreadIsRunning() {
		return mThreadIsRunning;
	}
	
	private void setThreadIsRunning(boolean is) {
		mThreadIsRunning = is;
	}
	
	public GameThread getThread(int level) {
		levelPlaying = level;
        return mThread;
    }
	
	public Activity getPlayActivity() {
		return mActivity;
	}
	
	public void setPlayActivity(Activity act) {
		mActivity = act;
	}
	
	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) { }

	@Override
	public void onSensorChanged(SensorEvent event) {
		if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER){
		    return;
	    }

		switch (mDisplay.getRotation()) {
            case Surface.ROTATION_0:
                addSensorValue(event.values[0],-event.values[1]);
            	break;
            case Surface.ROTATION_90:
            	addSensorValue(-event.values[1],event.values[0]);
            	break;
            case Surface.ROTATION_180:
            	addSensorValue(-event.values[0],-event.values[1]);
            	break;
            case Surface.ROTATION_270:
                addSensorValue(event.values[1],event.values[0]); 
            	break;
        }
	}
	
	private void addSensorValue(float x, float y) {
		for (int i=0; i < mSensorX.length - 1; i++){
			mSensorX[i] = mSensorX[i+1];
			mSensorY[i] = mSensorY[i+1];
		}
		mSensorX[mSensorX.length - 1] = x;
		mSensorY[mSensorX.length - 1] = y;
	}

	public class GameThread extends Thread {
		
		private SurfaceHolder mSurfaceHolder;
		private Handler mHandler;
        private Canvas mCanvas;
        private boolean mRun = false;
                         
		public GameThread(SurfaceHolder surfaceHolder, Context context, Handler handler) {
            mSurfaceHolder = surfaceHolder;
            mHandler = handler;
        }
		
		@Override
        public void run() {
			while (mRun) {
				if (!gamePaused) {
	            	try {
	                    mCanvas = mSurfaceHolder.lockCanvas(null);
	                    synchronized (mSurfaceHolder) {
	                    	switch (levelPlaying) {
	                		case GameSurfaceView.LAND_LEVEL:   
	            				mLandAnimation.mGameTimer = System.currentTimeMillis();
	            				mLandAnimation.updatePhysics(mSensorX);
	            				mLandAnimation.doDraw(mCanvas);
		                        break;
	                		case GameSurfaceView.SEA_LEVEL:   
	            				mSeaAnimation.mGameTimer = System.currentTimeMillis();
		                        mSeaAnimation.updatePhysics(mSensorX, mSensorY);
		                        mSeaAnimation.doDraw(mCanvas);
		                        break;
	                		case GameSurfaceView.AIR_LEVEL:   
	            				mAirAnimation.mGameTimer = System.currentTimeMillis();
		                        mAirAnimation.updatePhysics(mSensorX, mSensorY);
		                        mAirAnimation.doDraw(mCanvas);
		                        break;
	                    	}
	                    }
	                } finally {
	                    if (mCanvas != null) {
	                        mSurfaceHolder.unlockCanvasAndPost(mCanvas);
	                    }
	                }
				}
            }
        }
		
		public void setRunning(boolean b) {
            mRun = b;
        }		
	}
}