/*
 * Copyright (C) 2020 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 com.android.deskclock.ringtone;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;

import androidx.recyclerview.widget.RecyclerView;
import androidx.test.InstrumentationRegistry;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import androidx.test.rule.ActivityTestRule;

import com.android.deskclock.ItemAdapter;
import com.android.deskclock.ItemAdapter.ItemHolder;
import com.android.deskclock.R;
import com.android.deskclock.Utils;
import com.android.deskclock.data.DataModel;
import com.android.deskclock.provider.Alarm;

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

import java.util.Iterator;
import java.util.List;

import static org.junit.Assert.assertEquals;

/**
 * Exercise the user interface that adjusts the selected ringtone.
 */
@RunWith(AndroidJUnit4ClassRunner.class)
public class RingtonePickerActivityTest {

    private RingtonePickerActivity activity;
    private RecyclerView ringtoneList;
    private ItemAdapter<ItemHolder<Uri>> ringtoneAdapter;

    public static final Uri ALERT = Uri.parse("content://settings/system/alarm_alert");
    public static final Uri CUSTOM_RINGTONE_1 = Uri.parse("content://media/external/audio/one.ogg");

    @Rule
    public ActivityTestRule<RingtonePickerActivity> rule =
            new ActivityTestRule<>(RingtonePickerActivity.class, true, false);

    @Before
    @After
    public void setUp() {
        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        prefs.edit().clear().commit();
    }

    @Test
    public void validateDefaultState_TimerRingtonePicker() {
        createTimerRingtonePickerActivity();

        final List<ItemHolder<Uri>> systemRingtoneHolders = ringtoneAdapter.items;

        if (systemRingtoneHolders == null) {
            return;
        }

        final Iterator<ItemHolder<Uri>> itemsIter = systemRingtoneHolders.iterator();

        final HeaderHolder filesHeaderHolder = (HeaderHolder) itemsIter.next();
        assertEquals(R.string.your_sounds, filesHeaderHolder.getTextResId());
        assertEquals(HeaderViewHolder.VIEW_TYPE_ITEM_HEADER, filesHeaderHolder.getItemViewType());

        final AddCustomRingtoneHolder addNewHolder = (AddCustomRingtoneHolder) itemsIter.next();
        assertEquals(AddCustomRingtoneViewHolder.VIEW_TYPE_ADD_NEW, addNewHolder.getItemViewType());

        final HeaderHolder systemHeaderHolder = (HeaderHolder) itemsIter.next();
        assertEquals(R.string.device_sounds, systemHeaderHolder.getTextResId());
        assertEquals(HeaderViewHolder.VIEW_TYPE_ITEM_HEADER, systemHeaderHolder.getItemViewType());

        final RingtoneHolder silentHolder = (RingtoneHolder) itemsIter.next();
        assertEquals(Utils.RINGTONE_SILENT, silentHolder.getUri());
        assertEquals(RingtoneViewHolder.VIEW_TYPE_SYSTEM_SOUND, silentHolder.getItemViewType());

        final RingtoneHolder defaultHolder = (RingtoneHolder) itemsIter.next();
        assertEquals(RingtoneViewHolder.VIEW_TYPE_SYSTEM_SOUND, defaultHolder.getItemViewType());

        Runnable assertRunnable = () -> {
            assertEquals("Silent", silentHolder.getName());
            assertEquals("Timer Expired", defaultHolder.getName());
            assertEquals(DataModel.getDataModel().getDefaultTimerRingtoneUri(),
                defaultHolder.getUri());
            // Verify initial selection.
            assertEquals(
                DataModel.getDataModel().getTimerRingtoneUri(),
                DataModel.getDataModel().getDefaultTimerRingtoneUri());
        };
        InstrumentationRegistry.getInstrumentation().runOnMainSync(assertRunnable);
    }

    @Test
    public void validateDefaultState_AlarmRingtonePicker() {
        createAlarmRingtonePickerActivity(ALERT);

        final List<ItemHolder<Uri>> systemRingtoneHolders = ringtoneAdapter.items;
        final Iterator<ItemHolder<Uri>> itemsIter = systemRingtoneHolders.iterator();

        final HeaderHolder filesHeaderHolder = (HeaderHolder) itemsIter.next();
        assertEquals(R.string.your_sounds, filesHeaderHolder.getTextResId());
        assertEquals(HeaderViewHolder.VIEW_TYPE_ITEM_HEADER, filesHeaderHolder.getItemViewType());

        final AddCustomRingtoneHolder addNewHolder = (AddCustomRingtoneHolder) itemsIter.next();
        assertEquals(AddCustomRingtoneViewHolder.VIEW_TYPE_ADD_NEW, addNewHolder.getItemViewType());

        final HeaderHolder systemHeaderHolder = (HeaderHolder) itemsIter.next();
        assertEquals(R.string.device_sounds, systemHeaderHolder.getTextResId());
        assertEquals(HeaderViewHolder.VIEW_TYPE_ITEM_HEADER, systemHeaderHolder.getItemViewType());

        final RingtoneHolder silentHolder = (RingtoneHolder) itemsIter.next();
        assertEquals(Utils.RINGTONE_SILENT, silentHolder.getUri());
        assertEquals(RingtoneViewHolder.VIEW_TYPE_SYSTEM_SOUND, silentHolder.getItemViewType());

        final RingtoneHolder defaultHolder = (RingtoneHolder) itemsIter.next();
        assertEquals(RingtoneViewHolder.VIEW_TYPE_SYSTEM_SOUND, defaultHolder.getItemViewType());

        Runnable assertRunnable = () -> {
            assertEquals("Silent", silentHolder.getName());
            assertEquals("Default alarm sound", defaultHolder.getName());
            assertEquals(DataModel.getDataModel().getDefaultAlarmRingtoneUri(),
                    defaultHolder.getUri());
        };
        InstrumentationRegistry.getInstrumentation().runOnMainSync(assertRunnable);
    }

    @Test
    public void validateDefaultState_TimerRingtonePicker_WithCustomRingtones() {
        Runnable customRingtoneRunnable = () -> {
            DataModel.getDataModel().addCustomRingtone(CUSTOM_RINGTONE_1, "CustomSound");
        };
        InstrumentationRegistry.getInstrumentation().runOnMainSync(customRingtoneRunnable);
        createTimerRingtonePickerActivity();

        final List<ItemHolder<Uri>> systemRingtoneHolders = ringtoneAdapter.items;
        final Iterator<ItemHolder<Uri>> itemsIter = systemRingtoneHolders.iterator();

        final HeaderHolder filesHeaderHolder = (HeaderHolder) itemsIter.next();
        assertEquals(R.string.your_sounds, filesHeaderHolder.getTextResId());
        assertEquals(HeaderViewHolder.VIEW_TYPE_ITEM_HEADER, filesHeaderHolder.getItemViewType());

        final CustomRingtoneHolder customRingtoneHolder = (CustomRingtoneHolder) itemsIter.next();
        assertEquals("CustomSound", customRingtoneHolder.getName());
        assertEquals(CUSTOM_RINGTONE_1, customRingtoneHolder.getUri());
        assertEquals(RingtoneViewHolder.VIEW_TYPE_CUSTOM_SOUND,
                customRingtoneHolder.getItemViewType());

        final AddCustomRingtoneHolder addNewHolder = (AddCustomRingtoneHolder) itemsIter.next();
        assertEquals(AddCustomRingtoneViewHolder.VIEW_TYPE_ADD_NEW, addNewHolder.getItemViewType());

        final HeaderHolder systemHeaderHolder = (HeaderHolder) itemsIter.next();
        assertEquals(R.string.device_sounds, systemHeaderHolder.getTextResId());
        assertEquals(HeaderViewHolder.VIEW_TYPE_ITEM_HEADER, systemHeaderHolder.getItemViewType());

        final RingtoneHolder silentHolder = (RingtoneHolder) itemsIter.next();
        assertEquals(Utils.RINGTONE_SILENT, silentHolder.getUri());
        assertEquals(RingtoneViewHolder.VIEW_TYPE_SYSTEM_SOUND, silentHolder.getItemViewType());

        final RingtoneHolder defaultHolder = (RingtoneHolder) itemsIter.next();
        assertEquals(RingtoneViewHolder.VIEW_TYPE_SYSTEM_SOUND, defaultHolder.getItemViewType());

        Runnable assertRunnable = () -> {
            assertEquals("Silent", silentHolder.getName());
            assertEquals("Timer Expired", defaultHolder.getName());
            assertEquals(DataModel.getDataModel().getDefaultTimerRingtoneUri(),
                    defaultHolder.getUri());
            // Verify initial selection.
            assertEquals(
                    DataModel.getDataModel().getTimerRingtoneUri(),
                    DataModel.getDataModel().getDefaultTimerRingtoneUri());
        };
        InstrumentationRegistry.getInstrumentation().runOnMainSync(assertRunnable);

        Runnable removeCustomRingtoneRunnable = () -> {
            DataModel.getDataModel().removeCustomRingtone(CUSTOM_RINGTONE_1);
        };
        InstrumentationRegistry.getInstrumentation().runOnMainSync(removeCustomRingtoneRunnable);
    }

    @Test
    public void validateDefaultState_AlarmRingtonePicker_WithCustomRingtones() {
        Runnable customRingtoneRunnable = () -> {
            DataModel.getDataModel().addCustomRingtone(CUSTOM_RINGTONE_1, "CustomSound");
        };
        InstrumentationRegistry.getInstrumentation().runOnMainSync(customRingtoneRunnable);
        createAlarmRingtonePickerActivity(ALERT);

        final List<ItemHolder<Uri>> systemRingtoneHolders = ringtoneAdapter.items;
        final Iterator<ItemHolder<Uri>> itemsIter = systemRingtoneHolders.iterator();

        final HeaderHolder filesHeaderHolder = (HeaderHolder) itemsIter.next();
        assertEquals(R.string.your_sounds, filesHeaderHolder.getTextResId());
        assertEquals(HeaderViewHolder.VIEW_TYPE_ITEM_HEADER, filesHeaderHolder.getItemViewType());

        final CustomRingtoneHolder customRingtoneHolder = (CustomRingtoneHolder) itemsIter.next();
        assertEquals("CustomSound", customRingtoneHolder.getName());
        assertEquals(CUSTOM_RINGTONE_1, customRingtoneHolder.getUri());
        assertEquals(RingtoneViewHolder.VIEW_TYPE_CUSTOM_SOUND,
                customRingtoneHolder.getItemViewType());

        final AddCustomRingtoneHolder addNewHolder = (AddCustomRingtoneHolder) itemsIter.next();
        assertEquals(AddCustomRingtoneViewHolder.VIEW_TYPE_ADD_NEW, addNewHolder.getItemViewType());

        final HeaderHolder systemHeaderHolder = (HeaderHolder) itemsIter.next();
        assertEquals(R.string.device_sounds, systemHeaderHolder.getTextResId());
        assertEquals(HeaderViewHolder.VIEW_TYPE_ITEM_HEADER, systemHeaderHolder.getItemViewType());

        final RingtoneHolder silentHolder = (RingtoneHolder) itemsIter.next();
        assertEquals(Utils.RINGTONE_SILENT, silentHolder.getUri());
        assertEquals(RingtoneViewHolder.VIEW_TYPE_SYSTEM_SOUND, silentHolder.getItemViewType());

        final RingtoneHolder defaultHolder = (RingtoneHolder) itemsIter.next();
        assertEquals(RingtoneViewHolder.VIEW_TYPE_SYSTEM_SOUND, defaultHolder.getItemViewType());

        Runnable assertRunnable = () -> {
            assertEquals("Silent", silentHolder.getName());
            assertEquals("Default alarm sound", defaultHolder.getName());
            assertEquals(DataModel.getDataModel().getDefaultAlarmRingtoneUri(),
                    defaultHolder.getUri());
        };
        InstrumentationRegistry.getInstrumentation().runOnMainSync(assertRunnable);

        Runnable removeCustomRingtoneRunnable = () -> {
            DataModel.getDataModel().removeCustomRingtone(CUSTOM_RINGTONE_1);
        };
        InstrumentationRegistry.getInstrumentation().runOnMainSync(removeCustomRingtoneRunnable);
    }

    private void createTimerRingtonePickerActivity() {
        final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
        final Intent newIntent = new Intent();

        Runnable createIntentRunnable = () -> {
            final Intent intent = RingtonePickerActivity.createTimerRingtonePickerIntent(context);
            newIntent.fillIn(intent, 0);
        };
        InstrumentationRegistry.getInstrumentation().runOnMainSync(createIntentRunnable);

        createRingtonePickerActivity(newIntent);
    }

    private void createAlarmRingtonePickerActivity(Uri ringtone) {
        final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
        final Intent newIntent = new Intent();

        Runnable createIntentRunnable = () -> {
            // Use the custom ringtone in some alarms.
            final Alarm alarm = new Alarm(1, 1);
            alarm.enabled = true;
            alarm.vibrate = true;
            alarm.alert = ringtone;
            alarm.deleteAfterUse = true;

            final Intent intent =
                    RingtonePickerActivity.createAlarmRingtonePickerIntent(context, alarm);
            newIntent.fillIn(intent, 0);
        };
        InstrumentationRegistry.getInstrumentation().runOnMainSync(createIntentRunnable);

        createRingtonePickerActivity(newIntent);
    }

    @SuppressWarnings("unchecked")
    private void createRingtonePickerActivity(Intent intent) {
        activity = rule.launchActivity(intent);
        ringtoneList = activity.findViewById(R.id.ringtone_content);
        ringtoneAdapter = (ItemAdapter<ItemHolder<Uri>>) ringtoneList.getAdapter();
    }
}
