package ab.issue.main;

import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.support.test.filters.LargeTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

import ab.issue.R;
import ab.issue.scanner.camera.BarcodeScannerFragment;
import ab.issue.scanner.camera.BarcodeScannerViewModel;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;

/**
 * Instrumentation test, which will execute on an Android device.
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
@LargeTest
@RunWith(AndroidJUnit4.class)
public class MainActivityInstrumentedTest {

    @Rule
    public ActivityTestRule<MainActivity> mainActivityRule = new ActivityTestRule<>(MainActivity.class);

    @Test
    public void testCameraFlashlightToggle() {

        performToggleTest(R.id.camera_facing,
                input ->
                {
                    assert input != null;
                    return input.getCameraFacing();

                }, input ->
                {
                    assert input != null;
                    return input == R.drawable.ic_camera_rear_black_24dp
                            ? R.drawable.ic_camera_front_black_24dp
                            : R.drawable.ic_camera_rear_black_24dp;

                });

        performToggleTest(R.id.toggle_flashlight,
                input ->
                {
                    assert input != null;
                    return input.getFlashlight();

                }, input ->
                {
                    assert input != null;
                    return input == R.drawable.ic_flash_on_black_24dp
                            ? R.drawable.ic_flash_off_black_24dp
                            : R.drawable.ic_flash_on_black_24dp;

                });
    }

    private void performToggleTest(int viewId,
                                   Function<BarcodeScannerViewModel, LiveData<Integer>> liveDataFunction,
                                   Function<Integer, Integer> expectedFunction) {

        FragmentManager supportFragmentManager = mainActivityRule.getActivity().getSupportFragmentManager();
        mainActivityRule.getActivity().runOnUiThread(supportFragmentManager::executePendingTransactions);
        Fragment fragment = supportFragmentManager.findFragmentById(R.id.fragment);
        assertThat(fragment, instanceOf(BarcodeScannerFragment.class));
        BarcodeScannerFragment scannerFragment = (BarcodeScannerFragment) fragment;
        LiveData<Integer> data = liveDataFunction.apply(scannerFragment.getViewModel());

        final List<Integer> expected = new ArrayList<>();
        Observer<Integer> initialValueObserver = integer -> {
            assert integer != null;
            expected.add(expectedFunction.apply(integer));
        };
        assert data != null;
        data.observe(fragment, initialValueObserver);
        mainActivityRule.getActivity().runOnUiThread(() -> data.removeObserver(initialValueObserver));

        performToggleTest(viewId, data, fragment, expected.get(0));

        performToggleTest(viewId, data, fragment, expectedFunction.apply(expected.get(0)));
    }

    private void performToggleTest(int viewId, LiveData<Integer> data, Fragment fragment, int expected) {

        // perform the test
        onView(withId(viewId)).perform(click());

        // assert the expected
        Observer<Integer> observer = iconRes -> assertThat(iconRes, equalTo(expected));
        data.observe(fragment, observer);
        mainActivityRule.getActivity().runOnUiThread(() -> data.removeObserver(observer));
    }
}
