package com.bumptech.glide.request.target;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.graphics.Bitmap;
import android.widget.RemoteViews;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowAppWidgetManager;

@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, emulateSdk = 18, shadows = AppWidgetTargetTest.UpdateShadowAppWidgetManager.class)
public class AppWidgetTargetTest {

    private UpdateShadowAppWidgetManager shadowManager;
    private RemoteViews views;
    private int viewId;

    @Before
    public void setUp() {
        shadowManager = Robolectric.shadowOf_(AppWidgetManager.getInstance(Robolectric.application));
        viewId = 1234;
        views = mock(RemoteViews.class);
    }

    @Test
    public void testSetsBitmapOnRemoteViewsWithViewIdWhenCreatedWithComponentName() {
        ComponentName componentName = mock(ComponentName.class);
        AppWidgetTarget target = new AppWidgetTarget(Robolectric.application, views, viewId, componentName);

        Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
        target.onResourceReady(bitmap, null /*glideAnimation*/);

        verify(views).setImageViewBitmap(eq(viewId), eq(bitmap));
    }

    @Test
    public void testUpdatesAppWidgetWhenCreatedWithComponentName() {
        ComponentName componentName = mock(ComponentName.class);
        AppWidgetTarget target = new AppWidgetTarget(Robolectric.application, views, viewId, componentName);

        target.onResourceReady(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888), null /*glideAnimation*/);

        assertEquals(componentName, shadowManager.updatedComponentName);
        assertEquals(views, shadowManager.updatedRemoteViews);
    }

    @Test
    public void testSetsBitmapOnRemoteViewsWithViewIdWhenCreatedWithWidgetIds() {
        int[] widgetIds = new int[] { 1 };
        AppWidgetTarget target = new AppWidgetTarget(Robolectric.application, views, viewId, widgetIds);

        Bitmap bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
        target.onResourceReady(bitmap, null /*glideAnimation*/);

        verify(views).setImageViewBitmap(eq(viewId), eq(bitmap));
    }

    @Test
    public void testUpdatesAppWidgetWhenCreatedWithWidgetIds() {
        int[] widgetIds = new int[] { 1 };
        AppWidgetTarget target = new AppWidgetTarget(Robolectric.application, views, viewId, widgetIds);

        target.onResourceReady(Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888), null /*glideAnimation*/);

        assertThat(widgetIds).isEqualTo(shadowManager.updatedWidgetIds);
        assertEquals(views, shadowManager.updatedRemoteViews);
    }

    @Test(expected = NullPointerException.class)
    public void testThrowsWhenGivenNullContextWithWidgetIds() {
        new AppWidgetTarget(null /*context*/, views, 1234 /*viewId*/, 1 /*widgetIds*/);
    }

    @Test(expected = NullPointerException.class)
    public void testThrowsWhenGivenNullContextWithComponentName() {
        new AppWidgetTarget(null /*context*/, views, 1234 /*viewId*/, mock(ComponentName.class));
    }

    @Test(expected = NullPointerException.class)
    public void testThrowsWhenGivenNullRemoteViewsWithWidgetIds() {
        new AppWidgetTarget(Robolectric.application, null /*remoteViews*/, viewId, 1 /*widgetIds*/);
    }

    @Test(expected = NullPointerException.class)
    public void testThrowsWhenGivenNullRemoteViewsWithComponentName() {
        new AppWidgetTarget(Robolectric.application, null /*remoteViews*/, viewId, mock(ComponentName.class));
    }

    @Test(expected = NullPointerException.class)
    public void testThrowsWhenGivenNullWidgetIds() {
        new AppWidgetTarget(Robolectric.application, views, viewId, (int[]) null /*widgetIds*/);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testThrowsWhenGivenEmptyWidgetIds() {
        new AppWidgetTarget(Robolectric.application, views, viewId);
    }

    @Test(expected = NullPointerException.class)
    public void testThrowsWhenGivenNullComponentName() {
        new AppWidgetTarget(Robolectric.application, views, viewId, (ComponentName) null);
    }

    @Implements(AppWidgetManager.class)
    public static class UpdateShadowAppWidgetManager extends ShadowAppWidgetManager {
        public int[] updatedWidgetIds;
        public RemoteViews updatedRemoteViews;
        public ComponentName updatedComponentName;

        @Implementation
        public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
            updatedWidgetIds = appWidgetIds;
            updatedRemoteViews = views;
        }

        @Implementation
        public void updateAppWidget(ComponentName componentName, RemoteViews views) {
            updatedComponentName = componentName;
            updatedRemoteViews = views;
        }
    }
}