package org.emdev.ui.uimanager;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.content.ComponentName;
import android.content.Context;
import android.view.View;
import android.view.Window;

import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import org.emdev.BaseDroidApp;

@TargetApi(11)
public class UIManager3x implements IUIManager {

    private static final String SYS_UI_CLS = "com.android.systemui.SystemUIService";
    private static final String SYS_UI_PKG = "com.android.systemui";
    private static final ComponentName SYS_UI = new ComponentName(SYS_UI_PKG, SYS_UI_CLS);

    private static final String SU_PATH1 = "/system/bin/su";
    private static final String SU_PATH2 = "/system/xbin/su";
    private static final String AM_PATH = "/system/bin/am";

    private static final Map<ComponentName, Data> data = new HashMap<ComponentName, Data>() {

        /**
         *
         */
        private static final long serialVersionUID = -6627308913610357179L;

        @Override
        public Data get(final Object key) {
            Data existing = super.get(key);
            if (existing == null) {
                existing = new Data();
                put((ComponentName) key, existing);
            }
            return existing;
        }

    };

    @Override
    public void setTitleVisible(final Activity activity, final boolean visible, final boolean firstTime) {
        if (firstTime) {
            try {
                final Window window = activity.getWindow();
                window.requestFeature(Window.FEATURE_ACTION_BAR);
                window.requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
                activity.setProgressBarIndeterminate(true);
                activity.setProgressBarIndeterminateVisibility(true);
                window.setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS, 1);
            } catch (final Throwable th) {
                LCTX.e("Error on requestFeature call: " + th.getMessage());
            }
        }
        try {
            if (visible) {
                activity.getActionBar().show();
            } else {
                activity.getActionBar().hide();
            }
            data.get(activity.getComponentName()).titleVisible = visible;
        } catch (final Throwable th) {
            LCTX.e("Error on requestFeature call: " + th.getMessage());
        }
    }

    @Override
    public boolean isTitleVisible(final Activity activity) {
        return data.get(activity.getComponentName()).titleVisible;
    }

    @Override
    public void setFullScreenMode(final Activity activity, final View view, final boolean fullScreen) {
        data.get(activity.getComponentName()).fullScreen = fullScreen;
        if (fullScreen) {
            stopSystemUI(activity);
        } else {
            startSystemUI(activity);
        }
    }

    @Override
    public void openOptionsMenu(final Activity activity, final View view) {
        activity.openOptionsMenu();
    }

    @Override
    public void invalidateOptionsMenu(final Activity activity) {
        activity.invalidateOptionsMenu();
    }

    @Override
    public void onMenuOpened(final Activity activity) {
        if (data.get(activity.getComponentName()).fullScreen
                && data.get(activity.getComponentName()).fullScreenState.get()) {
            startSystemUI(activity);
        }
    }

    @Override
    public void onMenuClosed(final Activity activity) {
        if (data.get(activity.getComponentName()).fullScreen
                && !data.get(activity.getComponentName()).fullScreenState.get()) {
            stopSystemUI(activity);
        }
    }

    @Override
    public void onPause(final Activity activity) {
        if (data.get(activity.getComponentName()).fullScreen
                && data.get(activity.getComponentName()).fullScreenState.get()) {
            startSystemUI(activity);
        }
    }

    @Override
    public void onResume(final Activity activity) {
        if (data.get(activity.getComponentName()).fullScreen
                && !data.get(activity.getComponentName()).fullScreenState.get()) {
            stopSystemUI(activity);
        }
    }

    @Override
    public void onDestroy(final Activity activity) {
        if (data.get(activity.getComponentName()).fullScreen
                && data.get(activity.getComponentName()).fullScreenState.get()) {
            startSystemUI(activity);
        }
    }

    @Override
    public boolean isTabletUi(final Activity activity) {
        return true;
    }

    protected void startSystemUI(final Activity activity) {
        if (isSystemUIRunning()) {
            data.get(activity.getComponentName()).fullScreenState.set(false);
            return;
        }
        exec(false, activity, AM_PATH, "startservice", "-n", SYS_UI.flattenToString());
    }

    protected void stopSystemUI(final Activity activity) {
        if (!isSystemUIRunning()) {
            data.get(activity.getComponentName()).fullScreenState.set(true);
            return;
        }

        final String su = getSuPath();
        if (su == null) {
            data.get(activity.getComponentName()).fullScreenState.set(false);
            return;
        }

        exec(true, activity, su, "-c", "service call activity 79 s16 " + SYS_UI_PKG);
    }

    protected boolean isSystemUIRunning() {
        final Context ctx = BaseDroidApp.context;
        final ActivityManager am = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
        final List<RunningServiceInfo> rsiList = am.getRunningServices(1000);

        for (final RunningServiceInfo rsi : rsiList) {
            LCTX.d("Service: " + rsi.service);
            if (SYS_UI.equals(rsi.service)) {
                LCTX.e("System UI service found");
                return true;
            }
        }
        return false;
    }

    protected void exec(final boolean expected, final Activity activity, final String... as) {
        (new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    final boolean result = execImpl(as);
                    data.get(activity.getComponentName()).fullScreenState.set(result ? expected : !expected);
                } catch (final Throwable th) {
                    LCTX.e("Changing full screen mode failed: " + th.getCause());
                    data.get(activity.getComponentName()).fullScreenState.set(!expected);
                }
            }
        })).start();
    }

    private boolean execImpl(final String... as) {
        try {
            LCTX.d("Execute: " + Arrays.toString(as));
            final Process process = Runtime.getRuntime().exec(as);
            final InputStreamReader r = new InputStreamReader(process.getInputStream());
            final StringWriter w = new StringWriter();
            final char ac[] = new char[8192];
            int i = 0;
            do {
                i = r.read(ac);
                if (i > 0) {
                    w.write(ac, 0, i);
                }
            } while (i != -1);
            r.close();
            process.waitFor();

            final int exitValue = process.exitValue();
            final String text = w.toString();
            LCTX.d("Result code: " + exitValue);
            LCTX.d("Output:\n" + text);

            return 0 == exitValue;

        } catch (final IOException e) {
            throw new IllegalStateException(e);
        } catch (final InterruptedException e) {
            throw new IllegalStateException(e);
        }
    }

    private static String getSuPath() {
        final File su1 = new File(SU_PATH1);
        if (su1.exists() && su1.isFile() && su1.canExecute()) {
            return SU_PATH1;
        }
        final File su2 = new File(SU_PATH2);
        if (su2.exists() && su2.isFile() && su2.canExecute()) {
            return SU_PATH2;
        }
        return null;
    }

    private static class Data {

        boolean fullScreen = false;
        boolean titleVisible = true;
        final AtomicBoolean fullScreenState = new AtomicBoolean();
    }

}
