/*
 * Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
 *
 * This file is part of Loop Habit Tracker.
 *
 * Loop Habit Tracker is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * Loop Habit Tracker is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package org.isoron.uhabits.espresso;

import android.app.*;
import android.content.*;
import android.support.test.*;
import android.support.test.espresso.*;
import android.support.test.espresso.intent.rule.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;

import org.hamcrest.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;

import java.util.*;

import static android.support.test.espresso.Espresso.*;
import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.*;
import static android.support.test.espresso.assertion.ViewAssertions.*;
import static android.support.test.espresso.intent.Intents.*;
import static android.support.test.espresso.intent.matcher.IntentMatchers.*;
import static android.support.test.espresso.matcher.ViewMatchers.*;
import static org.hamcrest.Matchers.*;
import static org.isoron.uhabits.espresso.HabitViewActions.*;
import static org.isoron.uhabits.espresso.MainActivityActions.*;
import static org.isoron.uhabits.espresso.ShowHabitActivityActions.*;

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainTest
{
    private SystemHelper sys;

    @Rule
    public IntentsTestRule<MainActivity> activityRule =
        new IntentsTestRule<>(MainActivity.class);

    @Before
    public void setup()
    {
        Context context =
            InstrumentationRegistry.getInstrumentation().getContext();
        sys = new SystemHelper(context);
        sys.disableAllAnimations();
        sys.acquireWakeLock();
        sys.unlockScreen();

        Instrumentation.ActivityResult okResult =
            new Instrumentation.ActivityResult(Activity.RESULT_OK,
                new Intent());

        intending(hasAction(equalTo(Intent.ACTION_SEND))).respondWith(okResult);
        intending(hasAction(equalTo(Intent.ACTION_SENDTO))).respondWith(
            okResult);
        intending(hasAction(equalTo(Intent.ACTION_VIEW))).respondWith(okResult);

        skipTutorial();
    }

    public void skipTutorial()
    {
        try
        {
            for (int i = 0; i < 10; i++)
                onView(allOf(withClassName(endsWith("AppCompatImageButton")),
                    isDisplayed())).perform(click());
        }
        catch (NoMatchingViewException e)
        {
            // ignored
        }
    }

    @After
    public void tearDown()
    {
        sys.releaseWakeLock();
    }

    /**
     * User opens menu, clicks about, sees about screen.
     */
    @Test
    public void testAbout()
    {
        clickMenuItem(R.string.about);
        onView(isRoot()).perform(swipeUp());
    }

    /**
     * User creates a habit, toggles a bunch of checkmarks, clicks the habit to
     * open the statistics screen, scrolls down to some views, then scrolls the
     * views backwards and forwards in time.
     */
    @Test
    public void testAddHabitAndViewStats() throws InterruptedException
    {
        String name = addHabit(true);

        onData(Matchers.allOf(is(instanceOf(Habit.class)),
            HabitMatchers.withName(name)))
            .onChildView(withId(R.id.checkmarkPanel))
            .perform(toggleAllCheckmarks());

        Thread.sleep(1200);

        onData(Matchers.allOf(is(instanceOf(Habit.class)),
            HabitMatchers.withName(name)))
            .onChildView(withId(R.id.label))
            .perform(click());

        onView(withId(R.id.scoreView)).perform(scrollTo(), swipeRight());

        onView(withId(R.id.frequencyChart)).perform(scrollTo(), swipeRight());
    }

    /**
     * User opens the app, clicks the add button, types some bogus information,
     * tries to save, dialog displays an error.
     */
    @Test
    public void testAddInvalidHabit()
    {
        onView(withId(R.id.actionAdd)).perform(click());

        typeHabitData("", "", "15", "7");

        onView(withId(R.id.buttonSave)).perform(click());
        onView(withId(R.id.tvName)).check(matches(isDisplayed()));
    }

    /**
     * User opens the app, creates some habits, selects them, archives them,
     * select 'show archived' on the menu, selects the previously archived
     * habits and then deletes them.
     */
    @Test
    public void testArchiveHabits()
    {
        List<String> names = new LinkedList<>();

        for (int i = 0; i < 3; i++)
            names.add(addHabit());

        selectHabits(names);

        clickMenuItem(R.string.archive);
        assertHabitsDontExist(names);

        clickMenuItem(R.string.show_archived);

        assertHabitsExist(names);
        selectHabits(names);
        clickMenuItem(R.string.unarchive);

        clickMenuItem(R.string.show_archived);

        assertHabitsExist(names);
        deleteHabits(names);
    }

    /**
     * User creates a habit, selects the habit, clicks edit button, changes some
     * information about the habit, click save button, sees changes on the main
     * window, selects habit again, changes color, then deletes the habit.
     */
    @Test
    public void testEditHabit()
    {
        String name = addHabit();

        onData(Matchers.allOf(is(instanceOf(Habit.class)),
            HabitMatchers.withName(name)))
            .onChildView(withId(R.id.label))
            .perform(longClick());

        clickMenuItem(R.string.edit);

        String modifiedName = "Modified " + new Random().nextInt(10000);
        typeHabitData(modifiedName, "", "1", "1");

        onView(withId(R.id.buttonSave)).perform(click());

        assertHabitExists(modifiedName);

        selectHabit(modifiedName);
        clickMenuItem(R.string.color_picker_default_title);
        pressBack();

        deleteHabit(modifiedName);
    }

    /**
     * User creates a habit, opens statistics page, clicks button to edit
     * history, adds some checkmarks, closes dialog, sees the modified history
     * calendar.
     */
    @Test
    public void testEditHistory()
    {
        String name = addHabit();

        onData(Matchers.allOf(is(instanceOf(Habit.class)),
            HabitMatchers.withName(name)))
            .onChildView(withId(R.id.label))
            .perform(click());

        openHistoryEditor();
        onView(withClassName(endsWith("HabitHistoryView"))).perform(
            clickAtRandomLocations(20));

        pressBack();
        onView(withId(R.id.historyChart)).perform(scrollTo(), swipeRight(),
            swipeLeft());
    }

    /**
     * User creates a habit, opens settings, clicks export as CSV, is asked what
     * activity should handle the file.
     */
    @Test
    public void testExportCSV()
    {
        addHabit();
        clickMenuItem(R.string.settings);
        clickSettingsItem("Export as CSV");
        intended(hasAction(Intent.ACTION_SEND));
    }

    /**
     * User creates a habit, exports full backup, deletes the habit, restores
     * backup, sees that the previously created habit has appeared back.
     */
    @Test
    public void testExportImportDB()
    {
        String name = addHabit();

        clickMenuItem(R.string.settings);

        String date =
            DateFormats.getBackupDateFormat().format(DateUtils.getLocalTime());
        date = date.substring(0, date.length() - 2);

        clickSettingsItem("Export full backup");
        intended(hasAction(Intent.ACTION_SEND));

        deleteHabit(name);

        clickMenuItem(R.string.settings);
        clickSettingsItem("Import data");

        onData(
            allOf(is(instanceOf(String.class)), startsWith("Backups"))).perform(
            click());

        onData(
            allOf(is(instanceOf(String.class)), containsString(date))).perform(
            click());

        selectHabit(name);
    }

    /**
     * User opens the settings and generates a bug report.
     */
    @Test
    public void testGenerateBugReport()
    {
        clickMenuItem(R.string.settings);
        clickSettingsItem("Generate bug report");
        intended(hasAction(Intent.ACTION_SEND));
    }

    /**
     * User opens menu, clicks Help, sees website.
     */
    @Test
    public void testHelp()
    {
        clickMenuItem(R.string.help);
        intended(hasAction(Intent.ACTION_VIEW));
    }

    /**
     * User opens menu, clicks settings, sees settings screen.
     */
    @Test
    public void testSettings()
    {
        clickMenuItem(R.string.settings);
    }
}
