package org.mozilla.gecko;

import java.util.EnumSet;

import org.mozilla.gecko.PrefsHelper.PrefHandlerBase;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.util.ThreadUtils;

import android.os.Build;
import android.os.Bundle;
import android.util.Log;

public class DynamicToolbar {
    private static final String LOGTAG = "DynamicToolbar";

    private static final String STATE_ENABLED = "dynamic_toolbar";
    private static final String CHROME_PREF = "browser.chrome.dynamictoolbar";

    // DynamicToolbar is enabled iff prefEnabled is true *and* accessibilityEnabled is false,
    // so it is disabled by default on startup. We do not enable it until we explicitly get
    // the pref from Gecko telling us to turn it on.
    private volatile boolean prefEnabled;
    private boolean accessibilityEnabled;
    // On some device we have to force-disable the dynamic toolbar because of
    // bugs in the Android code. See bug 1231554.
    private final boolean forceDisabled;

    private final int prefObserverId;
    private final EnumSet<PinReason> pinFlags = EnumSet.noneOf(PinReason.class);
    private LayerView layerView;
    private OnEnabledChangedListener enabledChangedListener;
    private boolean temporarilyVisible;

    public enum PinReason {
        RELAYOUT,
        ACTION_MODE,
        FULL_SCREEN
    }

    public enum VisibilityTransition {
        IMMEDIATE,
        ANIMATE
    }

    /**
     * Listener for changes to the dynamic toolbar's enabled state.
     */
    public interface OnEnabledChangedListener {
        /**
         * This callback is executed on the UI thread.
         */
        public void onEnabledChanged(boolean enabled);
    }

    public DynamicToolbar() {
        // Listen to the dynamic toolbar pref
        prefObserverId = PrefsHelper.getPref(CHROME_PREF, new PrefHandler());
        forceDisabled = isForceDisabled();
        if (forceDisabled) {
            Log.i(LOGTAG, "Force-disabling dynamic toolbar for " + Build.MODEL + " (" + Build.DEVICE + "/" + Build.PRODUCT + ")");
        }
    }

    public static boolean isForceDisabled() {
        // Force-disable dynamic toolbar on the variants of the Galaxy Note 10.1
        // and Note 8.0 running Android 4.1.2. (Bug 1231554). This includes
        // the following model numbers:
        //  GT-N8000, GT-N8005, GT-N8010, GT-N8013, GT-N8020
        //  GT-N5100, GT-N5110, GT-N5120
        return Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN
            && (Build.MODEL.startsWith("GT-N80") ||
                Build.MODEL.startsWith("GT-N51"));
    }

    public void destroy() {
        PrefsHelper.removeObserver(prefObserverId);
    }

    public void setLayerView(LayerView layerView) {
        ThreadUtils.assertOnUiThread();

        this.layerView = layerView;
    }

    public void setEnabledChangedListener(OnEnabledChangedListener listener) {
        ThreadUtils.assertOnUiThread();

        enabledChangedListener = listener;
    }

    public void onSaveInstanceState(Bundle outState) {
        ThreadUtils.assertOnUiThread();

        outState.putBoolean(STATE_ENABLED, prefEnabled);
    }

    public void onRestoreInstanceState(Bundle savedInstanceState) {
        ThreadUtils.assertOnUiThread();

        if (savedInstanceState != null) {
            prefEnabled = savedInstanceState.getBoolean(STATE_ENABLED);
        }
    }

    public boolean isEnabled() {
        ThreadUtils.assertOnUiThread();

        if (forceDisabled) {
            return false;
        }

        return prefEnabled && !accessibilityEnabled;
    }

    public void setAccessibilityEnabled(boolean enabled) {
        ThreadUtils.assertOnUiThread();

        if (accessibilityEnabled == enabled) {
            return;
        }

        // Disable the dynamic toolbar when accessibility features are enabled,
        // and re-read the preference when they're disabled.
        accessibilityEnabled = enabled;
        if (prefEnabled) {
            triggerEnabledListener();
        }
    }

    public void setVisible(boolean visible, VisibilityTransition transition) {
        ThreadUtils.assertOnUiThread();

        if (layerView == null) {
            return;
        }

        // Don't hide the ActionBar/Toolbar, if it's pinned open by TextSelection.
        if (visible == false && pinFlags.contains(PinReason.ACTION_MODE)) {
            return;
        }

        final boolean isImmediate = transition == VisibilityTransition.IMMEDIATE;
        if (visible) {
            layerView.getDynamicToolbarAnimator().showToolbar(isImmediate);
        } else {
            layerView.getDynamicToolbarAnimator().hideToolbar(isImmediate);
        }
    }

    public void setTemporarilyVisible(boolean visible, VisibilityTransition transition) {
        ThreadUtils.assertOnUiThread();

        if (layerView == null) {
            return;
        }

        if (visible == temporarilyVisible) {
            // nothing to do
            return;
        }

        temporarilyVisible = visible;
        final boolean isImmediate = transition == VisibilityTransition.IMMEDIATE;
        if (visible) {
            layerView.getDynamicToolbarAnimator().showToolbar(isImmediate);
        } else {
            layerView.getDynamicToolbarAnimator().hideToolbar(isImmediate);
        }
    }

    public void persistTemporaryVisibility() {
        ThreadUtils.assertOnUiThread();

        if (temporarilyVisible) {
            temporarilyVisible = false;
            setVisible(true, VisibilityTransition.IMMEDIATE);
        }
    }

    public void setPinned(boolean pinned, PinReason reason) {
        ThreadUtils.assertOnUiThread();

        if (layerView == null) {
            return;
        }

        if (pinned) {
            pinFlags.add(reason);
        } else {
            pinFlags.remove(reason);
        }

        layerView.getDynamicToolbarAnimator().setPinned(!pinFlags.isEmpty());
    }

    private void triggerEnabledListener() {
        if (enabledChangedListener != null) {
            enabledChangedListener.onEnabledChanged(isEnabled());
        }
    }

    private class PrefHandler extends PrefHandlerBase {
        @Override
        public void prefValue(String pref, boolean value) {
            if (value == prefEnabled) {
                return;
            }

            prefEnabled = value;

            ThreadUtils.postToUiThread(new Runnable() {
                @Override
                public void run() {
                    // If accessibility is enabled, the dynamic toolbar is
                    // forced to be off.
                    if (!accessibilityEnabled) {
                        triggerEnabledListener();
                    }
                }
            });
        }

        @Override
        public boolean isObserver() {
            // We want to be notified of changes to be able to switch mode
            // without restarting.
            return true;
        }
    }
}
