package jp.sourceforge.nicoro;

import static jp.sourceforge.nicoro.Log.LOG_TAG;
import static jp.sourceforge.nicoro.NicoroAPIManager.ECO_TYPE_HIGH;
import static jp.sourceforge.nicoro.NicoroAPIManager.ECO_TYPE_INVALID;
import static jp.sourceforge.nicoro.NicoroAPIManager.ECO_TYPE_LOW;
import static jp.sourceforge.nicoro.NicoroAPIManager.ECO_TYPE_MID;
import static jp.sourceforge.nicoro.PlayerConstants.*;

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

import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;

import java.io.IOException;
import java.lang.ref.WeakReference;

import jp.sourceforge.nicoro.NicoroAPIManager.CookieNicoHistoryAndBody;

public class NewPlayerFragmentCreator {
    private Context mContext;
    private WeakReference<Handler> mHandler;
    private SharedPreferences mSharedPreferences;

    private int mMsgIdNewPlayerFragment;
    private int mMsgIdPlayError;

    private FirstTask mFirstTask;
    private SecondTask mSecondTask;

    private DefaultHttpClient mHttpClient;

    private int mOverrideEcoType = ECO_TYPE_INVALID;

    public NewPlayerFragmentCreator(Context context, Handler handler,
            int msgIdNewPlayerFragment, int msgIdPlayError) {
        mContext = context.getApplicationContext();
        mHandler = new WeakReference<Handler>(handler);
        mSharedPreferences = Util.getDefaultSharedPreferencesMultiProcess(
                mContext);

        mMsgIdNewPlayerFragment = msgIdNewPlayerFragment;
        mMsgIdPlayError = msgIdPlayError;
    }

    public void execute(Bundle newArgs) {
        stop();
        mOverrideEcoType = ECO_TYPE_INVALID;
        if (mHttpClient == null) {
            mHttpClient = Util.createHttpClient();
        }

        String userAgent = mSharedPreferences.getString(NicoroConfig.USER_AGENT,
                null);
        mFirstTask = new FirstTask(newArgs, userAgent);
        mFirstTask.execute();
    }

    public void stop() {
        stopTaskOnly();
        releaseHttpClient();
    }

    private void stopTaskOnly() {
        if (mFirstTask != null) {
            mFirstTask.stop();
            mFirstTask = null;
        }
        if (mSecondTask != null) {
            mSecondTask.stop();
            mSecondTask = null;
        }
    }

    public boolean isRunning() {
        return mFirstTask != null || mSecondTask != null;
    }

    void releaseHttpClient() {
        if (mHttpClient != null) {
            mHttpClient.getConnectionManager().shutdown();
            mHttpClient = null;
        }
    }

    void sendErrorMessge(String errorMessage) {
        Handler handler = mHandler.get();
        if (handler != null) {
            handler.obtainMessage(mMsgIdPlayError,
                    errorMessage).sendToTarget();
        }
        releaseHttpClient();
    }

    void sendErrorMessge(Exception exception) {
        Handler handler = mHandler.get();
        if (handler != null) {
            String errorMessage;
            if (exception == null) {
                errorMessage = mContext.getResources().getString(
                        R.string.errormessage_unknown);
            } else {
                errorMessage = exception.toString();
            }
            handler.obtainMessage(mMsgIdPlayError,
                    errorMessage).sendToTarget();
        }
        releaseHttpClient();
    }

    void sendResult(Bundle result) {
        Handler handler = mHandler.get();
        if (handler != null) {
            Message msg = handler.obtainMessage(mMsgIdNewPlayerFragment);
            msg.setData(result);
            msg.sendToTarget();
        }
        releaseHttpClient();
    }

    /**
     * UIスレッド上から呼び出すこと
     * @param args 新しいAbstractPlayerFragmentに渡すパラメータ
     * @return true:リトライ開始した false:リトライしない
     */
    boolean retry(Bundle args) {
        int currentEcoType = args.getInt(INTENT_NAME_FORCE_ECO, ECO_TYPE_HIGH);
        if (currentEcoType == ECO_TYPE_MID || currentEcoType == ECO_TYPE_LOW) {
            // 通常画質でリトライ
            stopTaskOnly();
            mOverrideEcoType = ECO_TYPE_HIGH;
            if (mHttpClient == null) {
                mHttpClient = Util.createHttpClient();
            }

            String userAgent = mSharedPreferences.getString(NicoroConfig.USER_AGENT,
                    null);
            mFirstTask = new FirstTask(args, userAgent);
            mFirstTask.execute();
            return true;
        } else {
            return false;
        }
    }

    private class FirstTask extends GetCookieNicoHistoryTask {
        private Bundle mArgs;

        public FirstTask(Bundle args, String userAgent) {
            super(args.getString(INTENT_NAME_VIDEO_NUMBER),
                    (mOverrideEcoType == ECO_TYPE_INVALID
                            ? args.getInt(INTENT_NAME_FORCE_ECO, ECO_TYPE_HIGH)
                            : mOverrideEcoType),
                    NicoroConfig.getCookieUserSession(mSharedPreferences),
                    userAgent, true, mHttpClient);
            mArgs = args;
        }

        @Override
        protected void onPostExecute(CookieNicoHistoryAndBody result) {
            if (result == null) {
                sendErrorMessge(mException);
                return;
            }
            String cookieNicoHistory = result.cookie;
            if (TextUtils.isEmpty(cookieNicoHistory)) {
                if (!retry(mArgs)) {
                    sendErrorMessge(mContext.getResources().getString(
                            R.string.errormessage_get_video_page_failed));
                }
                return;
            }
            WatchVideo video = WatchVideo.createFromWatchHtml(result.html);
            if (video == null) {
                if (!retry(mArgs)) {
                    sendErrorMessge(mContext.getResources().getString(
                            R.string.errormessage_get_video_page_failed));
                }
                return;
            }
            mArgs.putParcelable(INTENT_NAME_WATCH_VIDEO, video);
            mSecondTask = new SecondTask(
                    mArgs, mUserAgent, cookieNicoHistory);
            mSecondTask.execute();
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
            mFirstTask = null;
            mSecondTask = null;
        }
    };

    private class SecondTask extends AsyncTask<Void, Void, Bundle> {
        private Bundle mArgs;
        private String mUserAgent;
        private String mCookieNicoHistory;

        private Exception mException;

        private NicoroAPIManager.VideoPlayerParameterCreator mParameterCreator;

        public SecondTask(Bundle args, String userAgent,
                String cookieNicoHistory) {
            mArgs = args;
            mUserAgent = userAgent;
            mCookieNicoHistory = cookieNicoHistory;
        }

        @Override
        protected void onPreExecute() {
            mParameterCreator = new NicoroAPIManager.VideoPlayerParameterCreator();
        }

        @Override
        protected Bundle doInBackground(Void... params) {
            WatchVideo video = mArgs.getParcelable(
                    INTENT_NAME_WATCH_VIDEO);
            String cookieUserSession = NicoroConfig.getCookieUserSession(mSharedPreferences);
            String userId = mArgs.getString(
                    INTENT_NAME_USER_ID);
            int forceEco;
            if (mOverrideEcoType == ECO_TYPE_INVALID) {
                forceEco = mArgs.getInt(
                        INTENT_NAME_FORCE_ECO, ECO_TYPE_HIGH);
            } else {
                forceEco = mOverrideEcoType;
            }
            Bundle extras = null;
            try {
                extras = mParameterCreator.createExtras(mContext, video,
                        cookieUserSession, mCookieNicoHistory, userId,
                        mUserAgent, forceEco);
                extras.putAll(mArgs);
            } catch (ClientProtocolException e) {
                Log.e(LOG_TAG, e.toString(), e);
                mException = e;
            } catch (FailPreparePlayVideoException e) {
                Log.e(LOG_TAG, e.toString(), e);
                mException = e;
            } catch (IOException e) {
                Log.e(LOG_TAG, e.toString(), e);
                mException = e;
            }
            return extras;
        }

        @Override
        protected void onPostExecute(Bundle result) {
            mFirstTask = null;
            mSecondTask = null;

            if (result == null) {
                if (mException instanceof FailPreparePlayVideoException) {
                    if (!retry(mArgs)) {
                        sendErrorMessge(mException);
                    }
                } else {
                    sendErrorMessge(mException);
                }
                return;
            }
            sendResult(result);
        }

        @Override
        protected void onCancelled() {
            mFirstTask = null;
            mSecondTask = null;
        }

        public void stop() {
            cancel(false);
            if (mParameterCreator != null) {
                mParameterCreator.abort();
            }
        }
    }
}
