package jp.sourceforge.nicoro;

import static jp.sourceforge.nicoro.Log.LOG_TAG;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.impl.client.DefaultHttpClient;

import jp.sourceforge.nicoro.AbstractNicoroPlayer.MessageData;
import jp.sourceforge.nicoro.NicoroAPIManager.ParseGetFLVJikkyo;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.TextView;

public class NicoroJikkyoPlayer extends Activity
implements MessageView.Callback {
	private static final boolean DEBUG_LOGD = Release.IS_DEBUG && true;

	private static final int MSG_ID_MESSAGE_DRAW_MESSAGE = 0;
	private static final int MSG_ID_MESSAGE_ADDED_MESSAGE = 1;
	private static final int MSG_ID_MESSAGE_OCCURRED_ERROR = 2;
	
	public static final String INTENT_NAME_COOKIE = "COOKIE";
	public static final String INTENT_NAME_JIKKYO_NUMBER = "JIKKYO_NUMBER";
	public static final String INTENT_NAME_MESSAGE_URL = "MESSAGE_URL";
	public static final String INTENT_NAME_MESSAGE_PORT = "MESSAGE_PORT";
	public static final String INTENT_NAME_THREAD_ID = "THREAD_ID";
	public static final String INTENT_NAME_USER_ID = "USER_ID";
	public static final String INTENT_NAME_HTTP_PORT = "HTTP_PORT";

	private static final int ANIM_TIME_PLAYER_CONTROLLER_IN = 400;
	private static final int ANIM_TIME_PLAYER_CONTROLLER_OUT = 400;
	
	private SurfaceView mCameraPreview;
	private Camera mCamera;
	private MessageView mMessageView;
	private TextView mChannelName;
	
	private ViewGroup mPlayerController;
	private TranslateAnimation mAnimControllerIn;
	private TranslateAnimation mAnimControllerOut;
	private Button mButtonCameraOnOff;
	
	private LiveMessageLoader mLiveMessageLoader;
	
    private Matrix mMatrixMessage = new Matrix();
    private Random mRandom = new Random();
	private MessageData mMessageData = new MessageData();
	private long mStartTime;
    private Paint mPaintText = new Paint();
	
    private SharedPreferences mSharedPreferences;
    
	private GestureDetector mGestureDetector;
	private class CustomGestureListener extends GestureDetector.SimpleOnGestureListener {
		private int mDisplayWidth;
		private int mDisplayHeight;
		
		public CustomGestureListener() {
			DisplayMetrics metrics = new DisplayMetrics();
			getWindowManager().getDefaultDisplay().getMetrics(metrics);
			mDisplayWidth = metrics.widthPixels;
			mDisplayHeight = metrics.heightPixels;
		}

		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2,
				float velocityX, float velocityY) {
			final float absVelocityX = Math.abs(velocityX);
			final float absVelocityY = Math.abs(velocityY);
			if (absVelocityY >= absVelocityX) {
				// Controller操作
				if (velocityY >= 0.0f) {
					if (isPlayerControllerShown()) {
						hidePlayerController();
					}
				} else {
					if (!isPlayerControllerShown()) {
						showPlayerController();
					}
				}
			}
			return true;
		}

		@Override
		public boolean onSingleTapUp(MotionEvent e) {
			float x = e.getX();
			float y = e.getY();
	    	if (isPlayerControllerShown()) {
	    		if (y < ((float) (mDisplayHeight * 2)) / 3.0f) {
	    			hidePlayerController();
					return true;
	    		} else {
	    			return false;
	    		}
	    	} else {
	    		showPlayerController();
				return true;
	    	}
		}
	}
    
	private Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if (mHandler == null) {
				if (DEBUG_LOGD) {
					Log.d(LOG_TAG, Log.buf().append("Activity was destroyed. ignore message=")
							.append(msg.toString()).toString());
				}
				return;
			}
			switch (msg.what) {
			case MSG_ID_MESSAGE_DRAW_MESSAGE:
				if (!isFinishing()) {
					mMessageView.invalidate();
					
					Message message = mHandler.obtainMessage(
							MSG_ID_MESSAGE_DRAW_MESSAGE);
					mHandler.sendMessageDelayed(message, 33L);
				}
				break;
			case MSG_ID_MESSAGE_ADDED_MESSAGE:
				if (!hasMessages(MSG_ID_MESSAGE_DRAW_MESSAGE)) {
					mHandler.sendEmptyMessageDelayed(MSG_ID_MESSAGE_DRAW_MESSAGE, 33L);
				}
				break;
			case MSG_ID_MESSAGE_OCCURRED_ERROR:
				Util.showErrorDialog(NicoroJikkyoPlayer.this,
						(String) msg.obj, true);
				break;
			default:
				super.handleMessage(msg);
				break;
			}
		}
	};
	
	private class GetFLVTask extends AsyncTask<String, Void, ParseGetFLVJikkyo> {
		public void executeWrapper(String jikkyoNumber, String cookieUserSession,
				String userAgent) {
			execute(jikkyoNumber, cookieUserSession, userAgent);
		}
		
		@Override
		protected ParseGetFLVJikkyo doInBackground(String... params) {
			assert params.length == 3;
			
			final String jikkyoNumber = params[0];
			final String cookieUserSession = params[1];
			final String userAgent = params[2];

			ParseGetFLVJikkyo parseGetFLVJikkyo = null;
			try {
				DefaultHttpClient httpClient = Util.createHttpClient();
				httpClient.getCookieStore().clear();
				parseGetFLVJikkyo = new ParseGetFLVJikkyo();
				parseGetFLVJikkyo.initialize(httpClient,
						jikkyoNumber,
						cookieUserSession, userAgent);
			} catch (ClientProtocolException e) {
				Log.d(LOG_TAG, e.getMessage(), e);
			} catch (IOException e) {
				Log.d(LOG_TAG, e.getMessage(), e);
			}

			return parseGetFLVJikkyo;
		}
		
		@Override
		protected void onPostExecute(ParseGetFLVJikkyo result) {
			if (result == null) {
				return;
			}
			ParseGetFLVJikkyo parseGetFLVJikkyo = result;
			
			String cookieUserSession = mSharedPreferences.getString(
					NicoroConfig.COOKIE_USER_SESSION, null);
			final String messageUrl = parseGetFLVJikkyo.get("ms");
			final String messagePort = parseGetFLVJikkyo.get("ms_port");
			final String threadId = parseGetFLVJikkyo.get("thread_id");
			final String userId = parseGetFLVJikkyo.get("user_id");
			
			mLiveMessageLoader = new LiveMessageLoader(messageUrl,
	    			messagePort,
//	    			httpPort,
	    			threadId, cookieUserSession, userId,
	    			getApplicationContext());
			mLiveMessageLoader.setEventListener(new LiveMessageLoader.EventListener() {
				@Override
				public void onAddedMessage(LiveMessageLoader loader) {
					if (!mHandler.hasMessages(MSG_ID_MESSAGE_ADDED_MESSAGE)) {
						mHandler.sendEmptyMessage(MSG_ID_MESSAGE_ADDED_MESSAGE);
					}
				}
				@Override
				public void onFinished(LiveMessageLoader loader) {
					// nothing
				}
				@Override
				public void onOccurredError(LiveMessageLoader loader,
						String errorMessage) {
					if (mHandler != null) {
						Message message = mHandler.obtainMessage(MSG_ID_MESSAGE_OCCURRED_ERROR, errorMessage);
						mHandler.sendMessage(message);
					}
				}
			});
			mLiveMessageLoader.startLoad();
			
			final String channelName = parseGetFLVJikkyo.get("channel_name");
			if (channelName != null) {
				mChannelName.setText(channelName);
			}
		}
	}
	private GetFLVTask mGetFLVTask;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
        if (DEBUG_LOGD) {
        	Log.d(LOG_TAG, "NicoroJikkyoPlayer onCreate");
        }
		
		setContentView(R.layout.nicoro_jikkyoplayer);
		mCameraPreview = (SurfaceView) findViewById(R.id.camera);
		SurfaceHolder holder = mCameraPreview.getHolder();
		holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		holder.addCallback(new SurfaceHolder.Callback() {
			@Override
			public void surfaceChanged(SurfaceHolder holder, int format,
					int width, int height) {
		        if (DEBUG_LOGD) {
		        	Log.d(LOG_TAG, Log.buf().append("surfaceChanged format=")
		        			.append(format).append(" width=").append(width)
		        			.append(" height=").append(height).toString());
		        }

				if (mCamera != null) {
					mCamera.stopPreview();
					Camera.Parameters params = mCamera.getParameters();
//					params.setPreviewFormat(format);
					params.setPreviewFormat(PixelFormat.JPEG);
					params.setPreviewSize(width, height);
//					params.setPreviewSize(640, 480);
					mCamera.setParameters(params);
					mCamera.startPreview();
				}
			}
			@Override
			public void surfaceCreated(SurfaceHolder holder) {
				if (mCamera != null) {
//					mCamera.startPreview();
					try {
						mCamera.setPreviewDisplay(holder);
					} catch (IOException e) {
						Log.e(LOG_TAG, e.getMessage(), e);
					}
				}
			}
			@Override
			public void surfaceDestroyed(SurfaceHolder holder) {
				if (mCamera != null) {
					mCamera.stopPreview();
				}
			}
		});
		
		mCamera = Camera.open();
//		try {
//			mCamera.setPreviewDisplay(holder);
//		} catch (IOException e) {
//			Log.e(LOG_TAG, e.getMessage(), e);
//		}

		mChannelName = (TextView) findViewById(R.id.channel_name);
		
		mMessageView = (MessageView) findViewById(R.id.message_view);
		mMessageView.setCallback(this);
		
		mMessageData.mChatsWait = null;
		mMessageData.mChatsRunningNaka = 
			new LinkedList<MessageChat>();
		mMessageData.mChatsRunningShita = 
			new LinkedList<MessageChat>();
		mMessageData.mChatsRunningUe = 
			new LinkedList<MessageChat>();
		
        mPlayerController = (ViewGroup) findViewById(R.id.player_controller);
        mButtonCameraOnOff = (Button) findViewById(R.id.button_camera_onoff);
        mButtonCameraOnOff.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				if (mCameraPreview.getVisibility() == View.VISIBLE) {
					mCameraPreview.setVisibility(View.INVISIBLE);
					mCamera.stopPreview();
				} else {
					mCameraPreview.setVisibility(View.VISIBLE);
					mCamera.startPreview();
				}
	        	setButtonCameraOnOffText();
			}
        });
        setButtonCameraOnOffText();
		
        mGestureDetector = new GestureDetector(getApplicationContext(),
        		new CustomGestureListener());
        
		mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(
				getApplicationContext());
        Intent intent = getIntent();
        mGetFLVTask = new GetFLVTask();
		final String jikkyoNumber = intent.getStringExtra(
				NicoroJikkyoPlayer.INTENT_NAME_JIKKYO_NUMBER);
		final String cookieUserSession = mSharedPreferences.getString(
				NicoroConfig.COOKIE_USER_SESSION, null);
		final String userAgent = mSharedPreferences.getString(
				NicoroConfig.USER_AGENT, null);
        mGetFLVTask.executeWrapper(jikkyoNumber, cookieUserSession,
        		userAgent);
        
		final boolean showHintToast = mSharedPreferences.getBoolean(
				getString(R.string.pref_key_show_hint_toast), true);
		if (showHintToast) {
			Util.showInfoToast(getApplicationContext(),
					R.string.toast_explain_player_ctrl);
		}
        
		mStartTime = SystemClock.elapsedRealtime();
	}
	
	@Override
	protected void onDestroy() {
		mGetFLVTask.cancel(true);
    	if (mLiveMessageLoader != null) {
    		mLiveMessageLoader.finish();
    		mLiveMessageLoader = null;
    	}
		
		super.onDestroy();
		mCamera.release();
		mCamera = null;
	}
	
	@Override
	protected void onResume() {
		super.onResume();
	}
	
	@Override
	protected void onPause() {
		super.onPause();
	}

	@Override
    public boolean onTouchEvent(MotionEvent event) {
		if (mGestureDetector.onTouchEvent(event)) {
			return true;
		}
		return super.onTouchEvent(event);
    }
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		// 1.6向けなのでonKeyDownでback key監視
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			if (isPlayerControllerShown()) {
				hidePlayerController();
				return true;
			}
		} else if (keyCode == KeyEvent.KEYCODE_MENU) {
			switchPlayerControllerDisplay();
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}
	
	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		return super.onKeyUp(keyCode, event);
	}
	
    @Override
    protected void finalize() throws Throwable {
    	try {
    		super.finalize();
    	} finally {
        	if (mLiveMessageLoader != null) {
        		mLiveMessageLoader.finish();
        	}
    	}
    }
	
	private static LiveMessageLoader createLiveMessageLoader(Intent intent, Context context,
			String userSession) {
		LiveMessageLoader liveMessageLoader = null;
		String cookie = intent.getStringExtra(
				NicoroJikkyoPlayer.INTENT_NAME_COOKIE);
		String jikkyoNumber = intent.getStringExtra(
				NicoroJikkyoPlayer.INTENT_NAME_JIKKYO_NUMBER);
		String messageUrl = intent.getStringExtra(
				NicoroJikkyoPlayer.INTENT_NAME_MESSAGE_URL);
		String messagePort = intent.getStringExtra(
				NicoroJikkyoPlayer.INTENT_NAME_MESSAGE_PORT);
		String threadId = intent.getStringExtra(
				NicoroJikkyoPlayer.INTENT_NAME_THREAD_ID);
		String userId = intent.getStringExtra(
				NicoroJikkyoPlayer.INTENT_NAME_USER_ID);
		String httpPort = intent.getStringExtra(
				NicoroJikkyoPlayer.INTENT_NAME_HTTP_PORT);
		
    	liveMessageLoader = new LiveMessageLoader(messageUrl,
    			messagePort,
//    			httpPort,
    			threadId, userSession, userId,
    			context);
		
		return liveMessageLoader;
	}
	
	@Override
	public void drawMessageView(MessageView messageView, Canvas canvas) {
		canvas.drawColor(0x00000000);
		if (mLiveMessageLoader == null) {
			return;
		}
		
		// vposは現在時間基準
		final int vpos = (int) (SystemClock.elapsedRealtime() - mStartTime) / 10;
		
		final MessageData messageData = mMessageData;
		final List<MessageChat> chatsRunningNaka = messageData.mChatsRunningNaka;
		final List<MessageChat> chatsRunningShita = messageData.mChatsRunningShita;
		final List<MessageChat> chatsRunningUe = messageData.mChatsRunningUe;
		final int videoWidth = messageView.getWidth();
		final int videoHeight = messageView.getHeight();
		
    	mMatrixMessage.reset();
    	final float scaleY = (float) videoHeight / (float) AbstractNicoroPlayer.REAL_PLAYER_HEIGHT_PX_4_3;
    	mMatrixMessage.setScale(
//    			(float) videoWidth / (float) AbstractNicoroPlayer.REAL_PLAYER_WIDTH_PX,
    			scaleY,
    			scaleY);
    	// TODO 適当に横座標ずらす
    	final float translateX = ((float) videoWidth / scaleY - (float) AbstractNicoroPlayer.REAL_PLAYER_WIDTH_PX) / 2.0f;
    	mMatrixMessage.postTranslate(translateX, 0.0f);
    	canvas.setMatrix(mMatrixMessage);
    	
    	for (Iterator<MessageChat> it = chatsRunningNaka.iterator(); it.hasNext(); ) {
    		MessageChat chat = it.next();
    		if (vpos > chat.vpos + MessageChat.DISPLAY_TIME_VPOS_NAKA) {
    			it.remove();
    			continue;
    		}
    	}
    	for (Iterator<MessageChat> it = chatsRunningShita.iterator(); it.hasNext(); ) {
    		MessageChat chat = it.next();
    		if (vpos > chat.vpos + MessageChat.DISPLAY_TIME_VPOS_SHITA) {
    			it.remove();
    			continue;
    		}
    	}
    	for (Iterator<MessageChat> it = chatsRunningUe.iterator(); it.hasNext(); ) {
    		MessageChat chat = it.next();
    		if (vpos > chat.vpos + MessageChat.DISPLAY_TIME_VPOS_UE) {
    			it.remove();
    			continue;
    		}
    	}
    	
    	
    	while (true) {
    		MessageChat chat = mLiveMessageLoader.poll();
    		if (chat == null) {
    			break;
    		}
    		
    		// 再生側で取り出した時刻で上書き
    		chat.vpos = vpos;
    		
			chat.computeWidth(mPaintText);
			chat.computeSpeed();
			chat.computeLineHeight(mPaintText);
			if (chat.getPos() == MessageChat.POS_NAKA) {
				int nextY = chat.computeNakaNextY(vpos, chatsRunningNaka, mRandom);
				chat.setY(nextY, mPaintText);
				
				chat.addNakaOrder(chatsRunningNaka);
			} else if (chat.getPos() == MessageChat.POS_SHITA) {
				int nextY = chat.computeShitaNextY(chatsRunningShita, mRandom);
				chat.setY(nextY, mPaintText);

				chat.addShitaOrder(chatsRunningShita);
			} else if (chat.getPos() == MessageChat.POS_UE) {
				int nextY = chat.computeUeNextY(chatsRunningUe, mRandom);
				chat.setY(nextY, mPaintText);

				chat.addUeOrder(chatsRunningUe);
			} else {
				assert false;
			}
			
    		if (DEBUG_LOGD) {
    			Log.d(LOG_TAG, Log.buf().append("cur vpos=").append(vpos)
    					.append(" vpos=").append(chat.vpos)
    					.append(" chat=").append(chat.getText()).toString());
    		}
    	}
		
    	for (Iterator<MessageChat> it = chatsRunningNaka.iterator(); it.hasNext(); ) {
    		MessageChat chat = it.next();
    		chat.draw(vpos, canvas, mPaintText);
    	}
		
    	for (Iterator<MessageChat> it = chatsRunningShita.iterator(); it.hasNext(); ) {
    		MessageChat chat = it.next();
    		chat.draw(vpos, canvas, mPaintText);
    	}
		
    	for (Iterator<MessageChat> it = chatsRunningUe.iterator(); it.hasNext(); ) {
    		MessageChat chat = it.next();
    		chat.draw(vpos, canvas, mPaintText);
    	}
	}

    void switchPlayerControllerDisplay() {
    	if (isPlayerControllerShown()) {
    		hidePlayerController();
    	} else {
    		showPlayerController();
    	}
    }
    
    private boolean isPlayerControllerShown() {
    	final int visibility = mPlayerController.getVisibility();
    	if (visibility == View.VISIBLE
    			&& (mAnimControllerOut == null
    					|| mPlayerController.getAnimation() != mAnimControllerOut)) {
    		return true;
    	} else {
    		return false;
    	}
    }
    private void showPlayerController() {
        if (mAnimControllerIn == null) {
        	int controllerHeight = mPlayerController.getHeight();
        	mAnimControllerIn = new TranslateAnimation(
        			0.0f, 0.0f, controllerHeight, 0.0f);
        	mAnimControllerIn.setDuration(ANIM_TIME_PLAYER_CONTROLLER_IN);
        	mAnimControllerIn.setAnimationListener(new Animation.AnimationListener() {
				@Override
				public void onAnimationEnd(Animation animation) {
					mPlayerController.clearAnimation();
				}
				@Override
				public void onAnimationRepeat(Animation animation) {
				}
				@Override
				public void onAnimationStart(Animation animation) {
				}
        	});
        }
		mPlayerController.setVisibility(View.VISIBLE);
		mPlayerController.startAnimation(mAnimControllerIn);
    }
    private void hidePlayerController() {
        if (mAnimControllerOut == null) {
        	int controllerHeight = mPlayerController.getHeight();
        	mAnimControllerOut = new TranslateAnimation(
        			0.0f, 0.0f, 0.0f, controllerHeight);
        	mAnimControllerOut.setDuration(ANIM_TIME_PLAYER_CONTROLLER_OUT);
        	mAnimControllerOut.setAnimationListener(new Animation.AnimationListener() {
				@Override
				public void onAnimationEnd(Animation animation) {
					mPlayerController.setVisibility(View.INVISIBLE);
					mPlayerController.clearAnimation();
				}
				@Override
				public void onAnimationRepeat(Animation animation) {
				}
				@Override
				public void onAnimationStart(Animation animation) {
				}
        	});
        }
		mPlayerController.startAnimation(mAnimControllerOut);
    }

	private void setButtonCameraOnOffText() {
		if (mCameraPreview.getVisibility() == View.VISIBLE) {
			mButtonCameraOnOff.setText(R.string.button_camera_off);
		} else {
			mButtonCameraOnOff.setText(R.string.button_camera_on);
		}
	}
}
