package com.ms_square.debugoverlay;

import android.app.Activity;
import android.app.Application;
import android.app.Instrumentation;
import android.content.Intent;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.util.Log;

import com.google.android.libraries.cloudtesting.screenshots.ScreenShotter;
import com.ms_square.debugoverlay.modules.CpuUsageModule;
import com.ms_square.debugoverlay.modules.FpsModule;
import com.ms_square.debugoverlay.modules.LogcatLine;
import com.ms_square.debugoverlay.modules.LogcatLineColorScheme;
import com.ms_square.debugoverlay.modules.LogcatLineFilter;
import com.ms_square.debugoverlay.modules.LogcatModule;
import com.ms_square.debugoverlay.modules.MemInfoModule;
import com.ms_square.debugoverlay.sample.IPAddressModule;
import com.ms_square.debugoverlay.sample.MainActivity;
import com.ms_square.debugoverlay.sample.ScrollingActivity;

import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

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.withId;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;

/**
 * Not yet complete or good enough, but covers some common scenarios as a staring point.
 * TODO: use UiAutomation's takeScreenshot() for screen captures?
 */
abstract class DebugOverlayInstrumentedTest {

    private static final String TAG = DebugOverlayInstrumentedTest.class.getSimpleName();

    DebugOverlay debugOverlay;

    @Before
    public void setUp() {
        DebugOverlay.enableDebugLogging(true);
    }

    @After
    public void tearDown() {
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                if (debugOverlay != null) {
                    debugOverlay.uninstall();
                }
            }
        });
    }

    @Test
    public void useDefaultApplication() {
        assertEquals("Application", getApplication().getClass().getSimpleName());
    }

    @Test
    public void installCpuModule() throws Exception {
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                debugOverlay = new DebugOverlay.Builder(getApplication())
                        .allowSystemLayer(testSystemLayer())
                        .modules(new CpuUsageModule())
                        .build();
                debugOverlay.install();
            }
        });

        waitForOverlay();
    }

    @Test
    public void installCustomModule() throws Exception {
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                debugOverlay = new DebugOverlay.Builder(getApplication())
                        .allowSystemLayer(testSystemLayer())
                        .modules(new IPAddressModule(getApplication()))
                        .build();
                debugOverlay.install();
            }
        });

        waitForOverlay();
    }

    @Test
    public void installModulesTopEnd() {
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                debugOverlay = new DebugOverlay.Builder(getApplication())
                        .allowSystemLayer(testSystemLayer())
                        .modules(new CpuUsageModule(),
                                new MemInfoModule(getApplication()),
                                new FpsModule())
                        .position(Position.TOP_END)
                        .build();
                debugOverlay.install();
            }
        });

        waitForOverlay();
    }

    @Test
    public void installModulesBottomEnd() {
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                debugOverlay = new DebugOverlay.Builder(getApplication())
                        .allowSystemLayer(testSystemLayer())
                        .modules(new CpuUsageModule(),
                                new MemInfoModule(getApplication()),
                                new FpsModule())
                        .position(Position.BOTTOM_END)
                        .build();
                debugOverlay.install();
            }
        });

        waitForOverlay();
    }

    @Test
    public void installLogcatModule() {
        DebugOverlay.enableDebugLogging(false);

        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                debugOverlay = new DebugOverlay.Builder(getApplication())
                        .allowSystemLayer(testSystemLayer())
                        .modules(new CpuUsageModule(),
                                new MemInfoModule(getApplication()),
                                new FpsModule(),
                                new LogcatModule(10))
                        .position(Position.BOTTOM)
                        .build();
                debugOverlay.install();
            }
        });

        waitForOverlay();

        Log.v(TAG, "hello world V");
        Log.d(TAG, "hello world D");

        Log.i(TAG, "hello world I");
        Log.w(TAG, "hello world W");

        Log.e(TAG, "hello world E");
        Log.wtf(TAG, "hello world WTF");

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        DebugOverlay.enableDebugLogging(true);
    }

    @Test
    public void installLogcatModuleWCustomColor() {
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                debugOverlay = new DebugOverlay.Builder(getApplication())
                        .allowSystemLayer(testSystemLayer())
                        .modules(new LogcatModule(10, new LogcatLineColorScheme() {
                            @Override
                            public int getTextColor(LogcatLine.Priority priority, @NonNull String tag) {
                                return Color.CYAN;
                            }
                        }))
                        .build();
                debugOverlay.install();
            }
        });

        waitForOverlay();
    }

    @Test
    public void installLogcatModuleWCustomFilter() {
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                debugOverlay = new DebugOverlay.Builder(getApplication())
                        .allowSystemLayer(testSystemLayer())
                        .modules(new LogcatModule(10, new LogcatLineFilter.SimpleLogcatLineFilter(LogcatLine.Priority.INFO)))
                        .build();
                debugOverlay.install();
            }
        });

        waitForOverlay();
    }

    @Test
    public void installModulesCustomStyle() {
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                debugOverlay = new DebugOverlay.Builder(getApplication())
                        .allowSystemLayer(testSystemLayer())
                        .modules(new CpuUsageModule(),
                                new MemInfoModule(getApplication()),
                                new FpsModule())
                        .position(Position.TOP_START)
                        .bgColor(Color.parseColor("#60000000"))
                        .textColor(Color.MAGENTA)
                        .textSize(14f)
                        .textAlpha(.8f)
                        .build();
                debugOverlay.install();
            }
        });

        waitForOverlay();
    }


    @Test
    public void startSecondActivity() {
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                debugOverlay = new DebugOverlay.Builder(getApplication())
                        .allowSystemLayer(testSystemLayer())
                        .build();
                debugOverlay.install();
            }
        });

        waitForOverlay();

        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(ScrollingActivity.class.getCanonicalName(),
                null, false);
        getInstrumentation().addMonitor(monitor);
        try {
            onView(withId(com.ms_square.debugoverlay.sample.R.id.fab)).perform(click());
            Activity nextActivity = monitor.waitForActivityWithTimeout(5000);
            assertThat(nextActivity, Matchers.is(Matchers.notNullValue()));

            takeActivityScreenShot(nextActivity);

            nextActivity.finish();
        } finally {
            getInstrumentation().removeMonitor(monitor);
        }
    }

    abstract ActivityTestRule getActivityRule();

    abstract boolean testSystemLayer();

    void waitForOverlay() {
        waitForOverlay(5000);
    }

    void waitForOverlay(long millis) {
        if (!testSystemLayer()) {
            Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(MainActivity.class.getCanonicalName(),
                    null, false);
            getInstrumentation().addMonitor(monitor);
            getActivityRule().launchActivity(new Intent(getApplication(), MainActivity.class));
            try {
                monitor.waitForActivityWithTimeout(5000);
            } finally {
                getInstrumentation().removeMonitor(monitor);
            }
        }
        try {
            Thread.currentThread().sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    Instrumentation getInstrumentation() {
        return InstrumentationRegistry.getInstrumentation();
    }

    Application getApplication() {
        return (Application) getInstrumentation().getTargetContext().getApplicationContext();
    }

    void takeActivityScreenShot(Activity activity) {
        // This only captures the main application window. See captured videos in FireBase to check
        // what's being displayed in the overlay.
        // Note that test method name is automatically captured and appended by the lib
        ScreenShotter.takeScreenshot(getClass().getSimpleName(), activity);
    }
}