package jp.sourceforge.nicoro;

import android.app.Activity;
import android.app.Application;
import android.app.Service;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.support.v4.app.FragmentManager;

import java.io.File;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

import jp.gr.java_conf.shiseissi.commonlib.ThreadUtil;

public class NicoroApplication extends Application {
    private static final boolean DEBUG_LOGD = Release.IS_DEBUG & true;

    private static final String KEY_UNCAUGHT_EXCEPTION = "pref_key_uncaught_exception";

	private ThumbnailCacher mThumbnailCacher;
	private ThumbInfoCacher mThumbInfoCacher;

	private VideoCacheServiceConnection mVideoCacheServiceConnection;

    private SoftReference<SharedPreferences.Editor> mPrefEditorPlayState =
        new SoftReference<SharedPreferences.Editor>(null);

	private static WeakReference<NicoroApplication> sInstance =
	    new WeakReference<NicoroApplication>(null);

	@Override
	public void onCreate() {
	    sInstance = new WeakReference<NicoroApplication>(this);
		super.onCreate();

		UncaughtExceptionHandlerWrapper handler =
		    new UncaughtExceptionHandlerWrapper(getApplicationContext());
	    handler.setDefaultHandler();

        if (DEBUG_LOGD) {
            FragmentManager.enableDebugLogging(true);
        }

	    StaticRes.init(this);

//        // メモリ予約
//        Runtime runtime = Runtime.getRuntime();
//        long reserveSize = runtime.maxMemory() / 2
//            - (runtime.totalMemory() - runtime.freeMemory());
//        if (reserveSize > 0) {
//            byte[] temp = new byte[(int) reserveSize];
//            temp = null;
//            runtime.gc();
//        }


        String cacheDirDefaultPath = VideoLoader.getStreamTempDirDefault(this);
        // 最初に新キャッシュディレクトリは予め作っておく
        File cacheDirDefault = new File(cacheDirDefaultPath);
        if (!cacheDirDefault.exists()) {
            cacheDirDefault.mkdirs();
        }

        SharedPreferences sharedPreference = Util.getDefaultSharedPreferencesMultiProcess(this);
        SharedPreferences.Editor editor = null;
        Resources res = getResources();
        final String prefKeySelectCacheDir = res.getString(
                R.string.pref_key_select_cache_dir);
        if (sharedPreference.getString(prefKeySelectCacheDir, null) == null) {
            String cacheDir;
            File streamTempDirOld = new File(VideoLoader.STREAM_TEMP_DIR_OLD_VERSION);
            if (streamTempDirOld.isDirectory()) {
                // 古いバージョンのキャッシュディレクトリが存在していたらそちらを使う
                cacheDir = VideoLoader.STREAM_TEMP_DIR_OLD_VERSION;
            } else {
                cacheDir = cacheDirDefaultPath;
            }
            if (editor == null) {
                editor = sharedPreference.edit();
            }
            editor.putString(prefKeySelectCacheDir, cacheDir);
        }
        final String prefKeyPlayerLandscapeOnly = res.getString(
                R.string.pref_key_player_landscape_only);
        if (sharedPreference.contains(prefKeyPlayerLandscapeOnly)) {
            if (editor == null) {
                editor = sharedPreference.edit();
            }

            // 新パラメータに変換
            boolean landscapeOnly = sharedPreference.getBoolean(
                    prefKeyPlayerLandscapeOnly, false);
            if (landscapeOnly) {
                editor.putInt(NicoroConfig.PLAYER_SCREEN_ORIENTATION,
                        ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            }

            editor.remove(prefKeyPlayerLandscapeOnly);
        }
        if (editor != null) {
            editor.commit();
        }
	}

	@Override
	public void onLowMemory() {
		super.onLowMemory();
		if (mThumbnailCacher != null) {
			mThumbnailCacher.onLowMemory();
		}
		if (mThumbInfoCacher != null) {
			mThumbInfoCacher.onLowMemory();
		}
	}

	public static NicoroApplication getInstance(Activity activity) {
	    return (NicoroApplication) activity.getApplication();
	}

    public static NicoroApplication getInstance(Service service) {
        return (NicoroApplication) service.getApplication();
    }

    public static NicoroApplication getInstance() {
        return sInstance.get();
    }

	public ThumbnailCacher getThumbnailCacher() {
        if (!ThreadUtil.isInMainThread(this)) {
            throw new RuntimeException("This method can be called from main thread");
        }

		if (mThumbnailCacher == null) {
			mThumbnailCacher = new ThumbnailCacher();
			mThumbnailCacher.start();
		}
		return mThumbnailCacher;
	}

	public ThumbInfoCacher getThumbInfoCacher() {
        if (!ThreadUtil.isInMainThread(this)) {
            throw new RuntimeException("This method can be called from main thread");
        }

		if (mThumbInfoCacher == null) {
			mThumbInfoCacher = new ThumbInfoCacher();
			mThumbInfoCacher.start();
		}
		return mThumbInfoCacher;
	}

    public SharedPreferences.Editor getPrefEditorPlayState() {
        SharedPreferences.Editor editor = mPrefEditorPlayState.get();
        if (editor == null) {
            editor = AbstractPlayerFragment.getSharedPreferencesPlayState(
                    this).edit();
            mPrefEditorPlayState = new SoftReference<SharedPreferences.Editor>(editor);
        }
        return editor;
    }

    public VideoCacheServiceConnection getVideoCacheServiceConnection() {
        if (!ThreadUtil.isInMainThread(this)) {
            throw new RuntimeException("This method can be called from main thread");
        }

        if (mVideoCacheServiceConnection == null) {
            mVideoCacheServiceConnection = new VideoCacheServiceConnection();
        }
        return mVideoCacheServiceConnection;
    }

	public String processLastUncaughtException() {
        SharedPreferences sp = Util.getDefaultSharedPreferencesMultiProcess(this);
        String lastUncaught = sp.getString(KEY_UNCAUGHT_EXCEPTION, null);
        if (lastUncaught != null) {
            sp.edit().remove(KEY_UNCAUGHT_EXCEPTION).commit();
        }
        return lastUncaught;
	}

	private static class UncaughtExceptionHandlerWrapper
	implements Thread.UncaughtExceptionHandler {
	    private WeakReference<Context> mRefContext;
	    private Thread.UncaughtExceptionHandler mDefaultHandler;

	    UncaughtExceptionHandlerWrapper(Context context) {
	        mRefContext = new WeakReference<Context>(context);
	        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
	        assert !(mDefaultHandler instanceof UncaughtExceptionHandlerWrapper);
	    }

	    void setDefaultHandler() {
	        Thread.setDefaultUncaughtExceptionHandler(this);
	    }

        @Override
        public void uncaughtException(Thread thread, Throwable ex) {
            Context context = mRefContext.get();
            if (context != null) {
                StringBuilder builder = new StringBuilder();
                builder.append(ex.toString()).append("\n\n");
                StackTraceElement[] traces = ex.getStackTrace();
                assert traces != null;
                assert traces.length > 0;
                builder.append(traces[0].toString());
                if (!traces[0].getClassName().startsWith("jp.sourceforge.nicoro")) {
                    for (int i = 1; i < traces.length; ++i) {
                        if (traces[i].getClassName().startsWith("jp.sourceforge.nicoro")) {
                            builder.append("\n...\n").append(traces[i].toString());
                            break;
                        }
                    }
                }

                SharedPreferences sp = Util.getDefaultSharedPreferencesMultiProcess(context);
                sp.edit().putString(KEY_UNCAUGHT_EXCEPTION, builder.toString())
                    .commit();
            }
            mDefaultHandler.uncaughtException(thread, ex);
        }
	}
}
