package jp.sourceforge.nicoro;

import static jp.sourceforge.nicoro.Log.LOG_TAG;
import static jp.sourceforge.nicoro.AbstractNicoroPlayer.REAL_PLAYER_WIDTH_PX_4_3;
import static jp.sourceforge.nicoro.AbstractNicoroPlayer.REAL_PLAYER_HEIGHT_PX_4_3;

import java.util.Iterator;
import java.util.List;
import java.util.Random;

import jp.sourceforge.nicoro.AbstractNicoroPlayer.MessageData;
import jp.sourceforge.nicoro.nicoscript.NicoScriptDefault;
import jp.sourceforge.nicoro.nicoscript.NicoScriptGyaku;

import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;

public class MessageChatController {
	private static final boolean DEBUG_LOGV = Release.IS_DEBUG && false;
	private static final boolean DEBUG_LOGD = Release.IS_DEBUG && false;

    private Paint mPaintText = new Paint();
    private Random mRandom = new Random();
    private Matrix mMatrixMessage = new Matrix();

    private NicoScriptDefault mCommandDefault;
    private NicoScriptGyaku mCommandGyaku;

    private int mDuration = -1;

    public void setAntiAlias(boolean messageAntialias) {
        mPaintText.setAntiAlias(messageAntialias);
    }

    /**
     *
     * @param duration 動画の長さ（10ms単位）
     */
    public void setDuration(int duration) {
    	mDuration = duration;
    }

    /**
     * コメント描画
     * @param canvas
     * @param messageData コメント
     * @param messageDataFork 投稿者コメント
     * @param vpos 時間（10ms単位）
     * @param videoWidth
     * @param videoHeight
     * @param messageDisable コメント表示／非表示
     */
    public void drawMessage(Canvas canvas,
    		MessageData messageData,
    		MessageData messageDataFork,
    		int vpos, int videoWidth, int videoHeight,
    		boolean messageDisable) {
		setMatrixToCanvas(canvas, videoWidth, videoHeight);

//    	if (DEBUG_LOGV) {
//		Log.v(LOG_TAG, Log.buf().append("drawMessage: vpos=")
//				.append(vpos).toString());
//	}

		drawMessageCommon(canvas, messageDataFork, vpos, messageDisable);

    	if (mDuration > 0 && vpos + 500 > mDuration) {
    		// 動画終了間際では残り全てのコメント表示開始
	    	for (Iterator<MessageChat> it = messageData.mChatsWait.iterator(); it.hasNext(); ) {
	    		MessageChat chat = it.next();
        		// 現在時刻で上書き
        		chat.setVpos(vpos + 200);
	    	}
    	}
		drawMessageCommon(canvas, messageData, vpos, messageDisable);
    }

    /**
     * 生放送／実況向けコメント描画
     * @param canvas
     * @param messageData
     * @param messageDataFork
     * @param vpos
     * @param videoWidth
     * @param videoHeight
     * @param messageDisable
     * @param liveMessageLoader
     */
    public void drawMessageForLive(Canvas canvas,
    		MessageData messageData,
    		MessageData messageDataFork,
    		int vpos, int videoWidth, int videoHeight,
    		boolean messageDisable, LiveMessageLoader liveMessageLoader) {
		if (liveMessageLoader == null) {
			return;
		}

    	// TODO 投稿者コメント

    	setMatrixToCanvas(canvas, videoWidth, videoHeight);

		final List<MessageChat> chatsWait = messageData.mChatsWait;
    	assert chatsWait != null;

    	while (true) {
    		MessageChat chat = liveMessageLoader.poll();
    		if (chat == null) {
    			break;
    		}

    		// TODO 再生側で取り出した時刻で上書き
//    		chat.setVpos(vpos + 100);
    		chat.setVpos(vpos + 200);
    		chatsWait.add(chat);
    	}

		drawMessageCommon(canvas, messageData, vpos, messageDisable);
    }

    private void drawMessageCommon(Canvas canvas, MessageData messageData,
    		int vpos, boolean messageDisable) {
		final List<MessageChat> chatsWait = messageData.mChatsWait;

    	assert chatsWait != null;
    	assert messageData.mChatsRunningNaka != null;
    	assert messageData.mChatsRunningShita != null;
    	assert messageData.mChatsRunningUe != null;

    	// 表示が終わったコメントを非表示に
    	removeMessageChatByPassage(messageData, vpos);

    	// 方向設定更新
    	for (MessageChat chat : messageData.mChatsRunningNaka) {
    		chat.setDir(this);
    	}

    	// 時間が来たコメントの表示開始
    	for (Iterator<MessageChat> it = chatsWait.iterator(); it.hasNext(); ) {
    		MessageChat chat = it.next();
    		if (chat.isDisplayedTiming(vpos)) {
    			it.remove();
				addMessageChat(chat, messageData, vpos);
    		} else {
    		    // ソート済みのためタイミング外のものが来た時点でループ抜ける
    		    break;
    		}
    	}

    	// コメントの表示
    	if (!messageDisable) {
    		drawMessageChatMain(messageData, vpos, canvas);
    	}
    }

    private void removeMessageChatByPassage(
    		MessageData messageData, int vpos) {
		final List<MessageChat> chatsRunningNaka = messageData.mChatsRunningNaka;
		final List<MessageChat> chatsRunningShita = messageData.mChatsRunningShita;
		final List<MessageChat> chatsRunningUe = messageData.mChatsRunningUe;

    	for (Iterator<MessageChat> it = chatsRunningNaka.iterator(); it.hasNext(); ) {
    		MessageChat chat = it.next();
    		if (chat.isRemovedTiming(vpos)) {
    			chat.prepareRemove(this);
    			it.remove();
    	    	if (DEBUG_LOGV) {
    	    		Log.v(LOG_TAG, Log.buf().append("remove: ")
    	    				.append(chat.toString()).toString());
    	    	}
    			continue;
    		}
    	}
    	for (Iterator<MessageChat> it = chatsRunningShita.iterator(); it.hasNext(); ) {
    		MessageChat chat = it.next();
    		if (chat.isRemovedTiming(vpos)) {
    			chat.prepareRemove(this);
    			it.remove();
    	    	if (DEBUG_LOGV) {
    	    		Log.v(LOG_TAG, Log.buf().append("remove: ")
    	    				.append(chat.toString()).toString());
    	    	}
    			continue;
    		}
    	}
    	for (Iterator<MessageChat> it = chatsRunningUe.iterator(); it.hasNext(); ) {
    		MessageChat chat = it.next();
    		if (chat.isRemovedTiming(vpos)) {
    			chat.prepareRemove(this);
    			it.remove();
    	    	if (DEBUG_LOGV) {
    	    		Log.v(LOG_TAG, Log.buf().append("remove: ")
    	    				.append(chat.toString()).toString());
    	    	}
    			continue;
    		}
    	}
    }

    private void addMessageChat(
    		MessageChat chat, MessageData messageData, int vpos) {
		final List<MessageChat> chatsRunningNaka = messageData.mChatsRunningNaka;
		final List<MessageChat> chatsRunningShita = messageData.mChatsRunningShita;
		final List<MessageChat> chatsRunningUe = messageData.mChatsRunningUe;
		final Paint paintText = mPaintText;
		final Random random = mRandom;

		chat.prepareAdd(this, paintText, vpos);
		if (chat.getPos() == MessageChat.POS_NAKA) {
			int nextY = chat.computeNakaNextY(vpos, chatsRunningNaka, random);
			chat.setY(nextY, paintText);

			chat.addNakaOrder(chatsRunningNaka);
		} else if (chat.getPos() == MessageChat.POS_SHITA) {
			int nextY = chat.computeShitaNextY(chatsRunningShita, random);
			chat.setY(nextY, paintText);

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

			chat.addUeOrder(chatsRunningUe);
		} else {
			assert false;
		}

		if (DEBUG_LOGD) {
			Log.d(LOG_TAG, Log.buf().append("current-vpos=").append(vpos)
					.append(" vpos=").append(chat.getVpos())
					.append(" chat=").append(chat.getText()).toString());
		}
    }

    private void drawMessageChatMain(
    		MessageData messageData, int vpos, Canvas canvas) {
		final List<MessageChat> chatsRunningNaka = messageData.mChatsRunningNaka;
		final List<MessageChat> chatsRunningShita = messageData.mChatsRunningShita;
		final List<MessageChat> chatsRunningUe = messageData.mChatsRunningUe;
    	final Paint paintText = mPaintText;

    	for (MessageChat chat : chatsRunningNaka) {
    		chat.draw(vpos, canvas, paintText, this);
    	}

        for (MessageChat chat : chatsRunningShita) {
    		chat.draw(vpos, canvas, paintText, this);
    	}

        for (MessageChat chat : chatsRunningUe) {
    		chat.draw(vpos, canvas, paintText, this);
    	}
    }

    private void setMatrixToCanvas(Canvas canvas,
    		int videoWidth, int videoHeight) {
		final Matrix matrixMessage = mMatrixMessage;

		matrixMessage.reset();
    	final float scaleY = (float) videoHeight / (float) REAL_PLAYER_HEIGHT_PX_4_3;
//    	final float scaleX = (float) videoWidth / (float) REAL_PLAYER_WIDTH_PX_4_3;
    	final float scaleX = scaleY;
    	matrixMessage.setScale(
    			scaleX,
    			scaleY);
    	// TODO 適当に横座標ずらす
    	final float translateX = ((float) videoWidth / scaleX
    			- (float) REAL_PLAYER_WIDTH_PX_4_3) / 2.0f;
    	matrixMessage.postTranslate(translateX, 0.0f);
    	canvas.setMatrix(matrixMessage);
//    	canvas.clipRect(0, 0, videoWidth, videoHeight);
    }

    public void setCommandDefault(NicoScriptDefault messageChat) {
    	mCommandDefault = messageChat;
    }
    public void clearCommandDefault(NicoScriptDefault messageChat) {
    	if (mCommandDefault == messageChat) {
    		mCommandDefault = null;
    	}
    }

    public int getDefaultColor() {
    	if (mCommandDefault == null) {
    		return MessageChat.COLOR_WHITE;
    	} else {
    		return mCommandDefault.getColor();
    	}
    }

    public int getDefaultFontSize() {
    	if (mCommandDefault == null) {
    		return MessageChat.FONTSIZE_PX_MEDIUM;
    	} else {
    		return mCommandDefault.getFontSize();
    	}
    }

    public int getDefaultPos() {
    	if (mCommandDefault == null) {
    		return MessageChat.POS_NAKA;
    	} else {
    		return mCommandDefault.getPos();
    	}
    }

    public void setCommandGyaku(NicoScriptGyaku messageChat) {
    	mCommandGyaku = messageChat;
    }
    public void clearCommandGyaku(NicoScriptGyaku messageChat) {
    	if (mCommandGyaku == messageChat) {
    		mCommandGyaku = null;
    	}
    }

    public int getDir(boolean isFork) {
    	if (mCommandGyaku == null) {
    		return MessageChat.DIR_NORMAL;
    	} else {
    		return mCommandGyaku.getDir(isFork);
    	}
    }
}
