/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.system.helpers;

import android.app.Instrumentation;
import android.content.Context;
import android.provider.Settings;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.UiObjectNotFoundException;
import android.support.test.uiautomator.UiScrollable;
import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;

/**
 * Implement common helper functions for accessibility.
 */
public class AccessibilityHelper {
    public static final String SETTINGS_PACKAGE = "com.android.settings";
    public static final String BUTTON = "android.widget.Button";
    public static final String CHECK_BOX = "android.widget.CheckBox";
    public static final String IMAGE_BUTTON = "android.widget.ImageButton";
    public static final String TEXT_VIEW = "android.widget.TextView";
    public static final String SWITCH = "android.widget.Switch";
    public static final String CHECKED_TEXT_VIEW = "android.widget.CheckedTextView";
    public static final String RADIO_BUTTON = "android.widget.RadioButton";
    public static final String SEEK_BAR = "android.widget.SeekBar";
    public static final String SPINNER = "android.widget.Spinner";
    public static final int SHORT_TIMEOUT = 2000;
    public static final int LONG_TIMEOUT = 5000;
    public static AccessibilityHelper sInstance = null;
    private Context mContext = null;
    private Instrumentation mInstrumentation = null;
    private UiDevice mDevice = null;
    private SettingsHelper mSettingsHelper = null;

    public enum SwitchStatus {
        ON,
        OFF;
    }

    private AccessibilityHelper(Instrumentation instr) {
        mInstrumentation = instr;
        mSettingsHelper = SettingsHelper.getInstance();
        mDevice = UiDevice.getInstance(instr);
        mContext = mInstrumentation.getTargetContext();
    }

    public static AccessibilityHelper getInstance(Instrumentation instr) {
        if (sInstance == null) {
            sInstance = new AccessibilityHelper(instr);
        }
        return sInstance;
    }

    /**
     * Set Talkback "ON"/"OFF".
     *
     * @param value "ON"/"OFF"
     * @throws Exception
     */
    public void setTalkBackSetting(SwitchStatus value) throws Exception {
        launchSpecificAccessibilitySetting("TalkBack");
        UiObject2 swtBar = mDevice.wait(
                Until.findObject(By.res(SETTINGS_PACKAGE, "switch_bar")), SHORT_TIMEOUT)
                .findObject(By.res(SETTINGS_PACKAGE, "switch_widget"));
        if (swtBar != null && !swtBar.getText().equals(value.toString())) {
            swtBar.click();
            UiObject2 confirmBtn = mDevice.wait(
                    Until.findObject(By.res("android:id/button1")), LONG_TIMEOUT);
            if (confirmBtn != null) {
                confirmBtn.click();
            }
            // First time enable talkback, tutorial open.
            if (mDevice.wait(Until.hasObject(By.text("TalkBack tutorial")), SHORT_TIMEOUT)) {
                mDevice.pressBack(); // back to talkback setting page
            }
        }
        mDevice.pressBack();
    }

    /**
     * Set high contrast "ON"/"OFF".
     *
     * @param value "ON"/"OFF"
     * @throws Exception
     */
    public void setHighContrast(SwitchStatus value) throws Exception {
        launchSpecificAccessibilitySetting("Accessibility");
        setSettingSwitchValue("High contrast text", value);
    }

    /**
     * Launch specific accessibility setting page.
     *
     * @param settingName Specific accessibility setting name
     * @throws Exception
     */
    public void launchSpecificAccessibilitySetting(String settingName) throws Exception {
        mSettingsHelper.launchSettingsPage(mContext, Settings.ACTION_ACCESSIBILITY_SETTINGS);
        int maxTry = 3;
        while (maxTry-- >= 0) {
            Thread.sleep(SHORT_TIMEOUT);
            UiObject2 actionBar = mDevice.wait(Until.findObject(
                    By.res(SETTINGS_PACKAGE, "action_bar").enabled(true)), SHORT_TIMEOUT);
            if (actionBar == null) {
                mSettingsHelper.launchSettingsPage(mContext,
                        Settings.ACTION_ACCESSIBILITY_SETTINGS);
            } else {
                String actionBarText = actionBar.findObject(By.clazz(TEXT_VIEW)).getText();
                if (actionBarText.equals(settingName)) {
                    break;
                } else if (actionBarText.equals("Accessibility")) {
                    getSettingFromList(settingName).click();
                } else {
                    mDevice.wait(Until.findObject(By.res(SETTINGS_PACKAGE, "action_bar")
                            .enabled(true)), SHORT_TIMEOUT)
                            .findObject(By.clazz(IMAGE_BUTTON)).click();
                }
            }
        }
    }

    /**
     * Set switch "ON"/"OFF".
     *
     * @param settingTag setting name
     * @param value "ON"/"OFF"
     * @return true/false
     * @throws UiObjectNotFoundException
     * @throws InterruptedException
     */
    private boolean setSettingSwitchValue(String settingTag, SwitchStatus value)
            throws UiObjectNotFoundException, InterruptedException {
        UiObject2 cellSwitch = getSettingFromList(settingTag)
                .getParent().getParent().findObject(By.clazz(SWITCH));
        if (cellSwitch != null) {
            if (!cellSwitch.getText().equals(value.toString())) {
                cellSwitch.click();
                UiObject2 okBtn = mDevice.wait(Until.findObject(
                        By.res("android:id/button1")), LONG_TIMEOUT);
                if (okBtn != null) {
                    okBtn.click();
                }
            }
            return cellSwitch.getText().equals(value.toString());
        }
        return false;
    }

    /**
     * Get setting name text object from list.
     *
     * @param settingName setting name
     * @return UiObject2
     * @throws UiObjectNotFoundException
     */
    private UiObject2 getSettingFromList(String settingName)
            throws UiObjectNotFoundException {
        UiScrollable listScrollable = new UiScrollable(
                new UiSelector().resourceId(SETTINGS_PACKAGE + ":id/recycler_view"));
        if (listScrollable != null) {
            listScrollable.scrollToBeginning(100);
            listScrollable.scrollIntoView(
                    new UiSelector().resourceId("android:id/title").text(settingName));
            return mDevice.findObject(By.res("android:id/title").text(settingName));
        } else {
            throw new UiObjectNotFoundException("Fail to get scrollable list %s.");
        }
    }
}
