package com.xtremelabs.robolectric.shadows;

import android.app.Activity;
import android.app.Application;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.view.View;
import android.widget.RemoteViews;
import com.xtremelabs.robolectric.internal.AppSingletonizer;
import com.xtremelabs.robolectric.internal.Implementation;
import com.xtremelabs.robolectric.internal.Implements;
import com.xtremelabs.robolectric.internal.RealObject;
import org.apache.commons.lang.ArrayUtils;

import java.util.*;

import static com.xtremelabs.robolectric.Robolectric.newInstanceOf;
import static com.xtremelabs.robolectric.Robolectric.shadowOf;
import static org.apache.commons.lang.ArrayUtils.add;

@SuppressWarnings({"UnusedDeclaration"})
@Implements(AppWidgetManager.class)
public class ShadowAppWidgetManager {
    private static AppSingletonizer<AppWidgetManager> instances = new AppSingletonizer<AppWidgetManager>(AppWidgetManager.class) {
        @Override protected AppWidgetManager get(ShadowApplication shadowApplication) {
            return shadowApplication.appWidgetManager;
        }

        @Override protected void set(ShadowApplication shadowApplication, AppWidgetManager instance) {
            shadowApplication.appWidgetManager = instance;
        }

        @Override
        protected AppWidgetManager createInstance(Application applicationContext) {
            AppWidgetManager appWidgetManager = super.createInstance(applicationContext);
            shadowOf(appWidgetManager).context = applicationContext;
            return appWidgetManager;
        }
    };

    @RealObject private AppWidgetManager realAppWidgetManager;

    private Context context;
    private Map<Integer, WidgetInfo> widgetInfos = new HashMap<Integer, WidgetInfo>();
    private int nextWidgetId = 1;
    private boolean alwaysRecreateViewsDuringUpdate = false;

    private static void bind(AppWidgetManager appWidgetManager, Context context) {
        // todo: implement
    }


    /**
     * Finds or creates an {@code AppWidgetManager} for the given {@code context}
     *
     * @param context the {@code context} for which to produce an assoicated {@code AppWidgetManager}
     * @return the {@code AppWidgetManager} associated with the given {@code context}
     */
    @Implementation
    public static AppWidgetManager getInstance(Context context) {
        return instances.getInstance(context);
    }

    @Implementation
    public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(appWidgetId, views);
        }
    }

    /**
     * Simulates updating an {@code AppWidget} with a new set of views
     *
     * @param appWidgetId id of widget
     * @param views       views to update
     */
    @Implementation
    public void updateAppWidget(int appWidgetId, RemoteViews views) {
        WidgetInfo widgetInfo = getWidgetInfo(appWidgetId);
        int layoutId = views.getLayoutId();
        if (widgetInfo.layoutId != layoutId || alwaysRecreateViewsDuringUpdate) {
            widgetInfo.view = createWidgetView(layoutId);
            widgetInfo.layoutId = layoutId;
        }
        widgetInfo.lastRemoteViews = views;
        views.reapply(context, widgetInfo.view);
    }

    @Implementation
    public int[] getAppWidgetIds(ComponentName provider) {
        int ids[] = new int[0];
        for (int id : widgetInfos.keySet()){
            WidgetInfo widgetInfo = widgetInfos.get(id);
            String widgetClass = widgetInfo.appWidgetProvider.getClass().getName();
            String widgetPackage = widgetInfo.appWidgetProvider.getClass().getPackage().getName();
            if ( provider.getClassName().equals(widgetClass) && provider.getPackageName().equals(widgetPackage) ) {
                ids = add(ids, id);
            }
        }
        return ids.length == 0 ? null : ids;
    }

    /**
     * Triggers a reapplication of the most recent set of actions against the widget, which is what happens when the
     * phone is rotated. Does not attempt to simulate a change in screen geometry.
     *
     * @param appWidgetId the ID of the widget to be affected
     */
    public void reconstructWidgetViewAsIfPhoneWasRotated(int appWidgetId) {
        WidgetInfo widgetInfo = getWidgetInfo(appWidgetId);
        widgetInfo.view = createWidgetView(widgetInfo.layoutId);
        widgetInfo.lastRemoteViews.reapply(context, widgetInfo.view);
    }

    /**
     * Creates a widget by inflating its layout.
     *
     * @param appWidgetProviderClass the app widget provider class
     * @param widgetLayoutId         id of the layout to inflate
     * @return the ID of the new widget
     */
    public int createWidget(Class<? extends AppWidgetProvider> appWidgetProviderClass, int widgetLayoutId) {
        return createWidgets(appWidgetProviderClass, widgetLayoutId, 1)[0];
    }

    /**
     * Creates a bunch of widgets by inflating the same layout multiple times.
     *
     * @param appWidgetProviderClass the app widget provider class
     * @param widgetLayoutId         id of the layout to inflate
     * @param howManyToCreate        number of new widgets to create
     * @return the IDs of the new widgets
     */
    public int[] createWidgets(Class<? extends AppWidgetProvider> appWidgetProviderClass, int widgetLayoutId, int howManyToCreate) {
        AppWidgetProvider appWidgetProvider = newInstanceOf(appWidgetProviderClass);

        int[] newWidgetIds = new int[howManyToCreate];
        for (int i = 0; i < howManyToCreate; i++) {
            View widgetView = createWidgetView(widgetLayoutId);

            int myWidgetId = nextWidgetId++;
            widgetInfos.put(myWidgetId, new WidgetInfo(widgetView, widgetLayoutId, appWidgetProvider));
            newWidgetIds[i] = myWidgetId;
        }

        appWidgetProvider.onUpdate(context, realAppWidgetManager, newWidgetIds);
        return newWidgetIds;
    }

    private void createWidgetProvider(Class<? extends AppWidgetProvider> appWidgetProviderClass, int... newWidgetIds) {
        AppWidgetProvider appWidgetProvider = newInstanceOf(appWidgetProviderClass);
        appWidgetProvider.onUpdate(context, realAppWidgetManager, newWidgetIds);
    }

    private View createWidgetView(int widgetLayoutId) {
        return new Activity().getLayoutInflater().inflate(widgetLayoutId, null);
    }

    /**
     * Non-Android accessor.
     *
     * @param widgetId id of the desired widget
     * @return the widget associated with {@code widgetId}
     */
    public View getViewFor(int widgetId) {
        return getWidgetInfo(widgetId).view;
    }

    /**
     * Non-Android accessor.
     *
     * @param widgetId id of the widget whose provider is to be returned
     * @return the {@code AppWidgetProvider} associated with {@code widgetId}
     */
    public AppWidgetProvider getAppWidgetProviderFor(int widgetId) {
        return getWidgetInfo(widgetId).appWidgetProvider;
    }

    /**
     * Non-Android mechanism that enables testing of widget behavior when all of the views are recreated on every
     * update. This is useful for ensuring that your widget will behave correctly even if it is restarted by the OS
     * between events.
     *
     * @param alwaysRecreate whether or not to always recreate the views
     */
    public void setAlwaysRecreateViewsDuringUpdate(boolean alwaysRecreate) {
        alwaysRecreateViewsDuringUpdate = alwaysRecreate;
    }

    /**
     * Non-Android accessor.
     *
     * @return the state of the{@code alwaysRecreateViewsDuringUpdate} flag
     */
    public boolean getAlwaysRecreateViewsDuringUpdate() {
        return alwaysRecreateViewsDuringUpdate;
    }

    private WidgetInfo getWidgetInfo(int widgetId) {
        return widgetInfos.get(widgetId);
    }

    private class WidgetInfo {
        private View view;
        private int layoutId;
        private AppWidgetProvider appWidgetProvider;
        private RemoteViews lastRemoteViews;

        public WidgetInfo(View view, int layoutId, AppWidgetProvider appWidgetProvider) {
            this.view = view;
            this.layoutId = layoutId;
            this.appWidgetProvider = appWidgetProvider;
        }
    }
}

@@ -3973,33 +3973,8 @@
 turn
- ids.length == 0 ? null :
  ids

Fix for proper return values in ShadowAppWidgetManager.getAppWidgetIds()