/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * 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.google.samples.apps.iosched.appwidget;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.text.format.DateUtils;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;

import com.google.samples.apps.iosched.R;
import com.google.samples.apps.iosched.model.ScheduleHelper;
import com.google.samples.apps.iosched.model.ScheduleItem;
import com.google.samples.apps.iosched.provider.ScheduleContract;
import com.google.samples.apps.iosched.settings.SettingsUtils;
import com.google.samples.apps.iosched.myschedule.MyScheduleActivity;
import com.google.samples.apps.iosched.ui.SimpleSectionedListAdapter;
import com.google.samples.apps.iosched.ui.TaskStackBuilderProxyActivity;
import com.google.samples.apps.iosched.util.AccountUtils;
import com.google.samples.apps.iosched.util.TimeUtils;
import com.google.samples.apps.iosched.util.UIUtils;

import java.util.*;

import static com.google.samples.apps.iosched.util.LogUtils.*;

/**
 * This is the service that provides the factory to be bound to the collection service.
 */
public class ScheduleWidgetRemoteViewsService extends RemoteViewsService {

    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new WidgetRemoteViewsFactory(this.getApplicationContext());
    }

    /**
     * This is the factory that will provide data to the collection widget.
     */
    private static class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
        private static final String TAG = makeLogTag(WidgetRemoteViewsFactory.class);

        private Context mContext;
        private SparseIntArray mPMap;
        private List<SimpleSectionedListAdapter.Section> mSections;
        private SparseBooleanArray mHeaderPositionMap;

        StringBuilder mBuffer = new StringBuilder();
        Formatter mFormatter = new Formatter(mBuffer, Locale.getDefault());
        private ArrayList<ScheduleItem> mScheduleItems;
        private int mDefaultSessionColor;
        private int mDefaultStartEndTimeColor;

        public WidgetRemoteViewsFactory(Context context) {
            mContext = context;
        }

        public void onCreate() {
            // Since we reload the cursor in onDataSetChanged() which gets called immediately after
            // onCreate(), we do nothing here.
        }

        public void onDestroy() {

        }

        public int getCount() {
            if (mScheduleItems == null || !AccountUtils.hasActiveAccount(mContext)) {
                return 0;
            }
            if (mScheduleItems.size() < 10) {
                init();
            }
            return mScheduleItems.size();
        }

        public int getItemViewType(int position) {
            if (position < 0 || position >= mScheduleItems.size()) {
                LOGE(TAG, "Invalid view position passed to MyScheduleAdapter: " + position);
                return VIEW_TYPE_NORMAL;
            }
            ScheduleItem item = mScheduleItems.get(position);
            long now = UIUtils.getCurrentTime(mContext);
            if (item.startTime <= now && now <= item.endTime && item.type == ScheduleItem.SESSION) {
                return VIEW_TYPE_NOW;
            } else {
                return VIEW_TYPE_NORMAL;
            }
        }

        private static final int VIEW_TYPE_NORMAL = 0;
        private static final int VIEW_TYPE_NOW = 1;

        public RemoteViews getViewAt(int position) {
            RemoteViews rv;

            boolean isSectionHeader = mHeaderPositionMap.get(position);
            int offset = mPMap.get(position);

            if (isSectionHeader) {
                rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_schedule_header);
                SimpleSectionedListAdapter.Section section = mSections.get(offset - 1);
                rv.setTextViewText(R.id.widget_schedule_day, section.getTitle());

            } else {
                int itemPosition = position - offset;

                Intent homeIntent = new Intent(mContext, MyScheduleActivity.class);

                final ScheduleItem item = mScheduleItems.get(itemPosition);
                ScheduleItem nextItem = (itemPosition < mScheduleItems.size() - 1) ? mScheduleItems.get(itemPosition + 1) : null;

                if (mDefaultSessionColor < 0) {
                    mDefaultSessionColor = mContext.getResources().getColor(R.color.default_session_color);
                }

                int itemViewType = getItemViewType(itemPosition);
                boolean isNowPlaying = false;
                boolean isPastDuringConference = false;
                mDefaultStartEndTimeColor = R.color.body_text_2;

                if (itemViewType == VIEW_TYPE_NOW) {
                    isNowPlaying = true;
                    mDefaultStartEndTimeColor = R.color.body_text_1;
                }

                rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_schedule_item);


                if (itemPosition < 0 || itemPosition >= mScheduleItems.size()) {
                    LOGE(TAG, "Invalid view position passed to MyScheduleAdapter: " + position);
                    return rv;
                }

                long now = UIUtils.getCurrentTime(mContext);
                rv.setTextViewText(R.id.start_end_time, formatTime(now, item));

                rv.setViewVisibility(R.id.live_now_badge, View.GONE);

                // Set default colors to time indicators, in case they were overridden by conflict warning:
                if (!isNowPlaying) {
                    rv.setTextColor(R.id.start_end_time, mContext.getResources().getColor(mDefaultStartEndTimeColor));
                }

                if (item.type == ScheduleItem.FREE) {
                    rv.setImageViewResource(R.id.icon, R.drawable.ic_browse);

                    rv.setTextViewText(R.id.slot_title, mContext.getText(R.string.browse_sessions));
                    rv.setTextColor(R.id.slot_title, mContext.getResources().getColor(R.color.flat_button_text));

                    rv.setTextViewText(R.id.slot_room, item.subtitle);
                    rv.setTextColor(R.id.slot_room, mContext.getResources().getColor(R.color.body_text_2));

                    Intent fillIntent = TaskStackBuilderProxyActivity.getFillIntent(
                            homeIntent,
                            new Intent(Intent.ACTION_VIEW, ScheduleContract.Sessions.buildUnscheduledSessionsInInterval(
                                    item.startTime, item.endTime))
                    );
                    rv.setOnClickFillInIntent(R.id.box, fillIntent);

                } else if (item.type == ScheduleItem.BREAK) {
                    rv.setImageViewResource(R.id.icon, UIUtils.getBreakIcon(item.title));

                    rv.setTextViewText(R.id.slot_title, item.title);
                    rv.setTextColor(R.id.slot_title, mContext.getResources().getColor(R.color.body_text_1));

                    rv.setTextViewText(R.id.slot_room, item.room);
                    rv.setTextColor(R.id.slot_room, mContext.getResources().getColor(R.color.body_text_2));

                } else if (item.type == ScheduleItem.SESSION) {
                    rv.setImageViewResource(R.id.icon, UIUtils.getSessionIcon(item.sessionType));

                    rv.setTextViewText(R.id.slot_title, item.title);
                    rv.setTextColor(R.id.slot_title, mContext.getResources().getColor(R.color.body_text_1));

                    rv.setTextViewText(R.id.slot_room, item.room);
                    rv.setTextColor(R.id.slot_room, mContext.getResources().getColor(R.color.body_text_2));

                    // show or hide the "LIVE NOW" badge
                    final boolean showLiveBadge = 0 != (item.flags & ScheduleItem.FLAG_HAS_LIVESTREAM)
                            && now >= item.startTime && now <= item.endTime;
                    rv.setViewVisibility(R.id.live_now_badge, (showLiveBadge ? View.VISIBLE : View.GONE));

                    // show or hide the "conflict" warning
                    if (!isPastDuringConference) {
                        final boolean showConflict = 0 != (item.flags & ScheduleItem.FLAG_CONFLICTS_WITH_PREVIOUS);
                        if (showConflict && !isNowPlaying) {
                            int conflictColor = mContext.getResources().getColor(R.color.my_schedule_conflict);
                            rv.setTextColor(R.id.start_end_time, conflictColor);
                        }
                    }

                    Intent fillIntent = TaskStackBuilderProxyActivity.getFillIntent(
                            homeIntent,
                            new Intent(Intent.ACTION_VIEW, ScheduleContract.Sessions.buildSessionUri(item.sessionId)));
                    rv.setOnClickFillInIntent(R.id.box, fillIntent);

                } else {
                    LOGE(TAG, "Invalid item type in MyScheduleAdapter: " + item.type);
                }
            }

            return rv;
        }

        @Override
        public RemoteViews getLoadingView() {
            return null;
        }

        @Override
        public int getViewTypeCount() {
            return 4;
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public boolean hasStableIds() {
            return true;
        }

        @Override
        public void onDataSetChanged() {
            init();
        }

        private void init() {
            ScheduleHelper scheduleHelper = new ScheduleHelper(mContext);

            //Fetch all sessions and blocks
            List<ScheduleItem> allScheduleItems = scheduleHelper.getScheduleData(Long.MIN_VALUE, Long.MAX_VALUE);

            String displayTimeZone = SettingsUtils.getDisplayTimeZone(mContext).getID();

            mSections = new ArrayList<SimpleSectionedListAdapter.Section>();
            long previousTime = -1;
            long time;
            mPMap = new SparseIntArray();
            mHeaderPositionMap = new SparseBooleanArray();
            int offset = 0;
            int globalPosition = 0;
            int position = 0;
            mScheduleItems = new ArrayList<ScheduleItem>();
            for (ScheduleItem item : allScheduleItems) {
                if (item.endTime <= UIUtils.getCurrentTime(mContext)) {
                    continue;
                }
                mScheduleItems.add(item);
                time = item.startTime;
                if (!UIUtils.isSameDayDisplay(previousTime, time, mContext)) {
                    mBuffer.setLength(0);
                    mSections.add(new SimpleSectionedListAdapter.Section(position,
                            DateUtils.formatDateRange(
                                    mContext, mFormatter,
                                    time, time,
                                    DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_SHOW_DATE,
                                    displayTimeZone
                            ).toString()
                    ));
                    ++offset;
                    mHeaderPositionMap.put(globalPosition, true);
                    mPMap.put(globalPosition, offset);
                    ++globalPosition;
                }
                mHeaderPositionMap.put(globalPosition, false);
                mPMap.put(globalPosition, offset);
                ++globalPosition;
                ++position;
                previousTime = time;
            }
        }

        private String formatTime(long now, ScheduleItem item) {
            StringBuilder time = new StringBuilder();
            if (item.startTime <= now) {
                // session is happening now!
                if (0 != (item.flags & ScheduleItem.FLAG_HAS_LIVESTREAM)) {
                    // session has live stream
                    time.append(mContext.getString(R.string.watch_now));
                } else {
                    time.append(mContext.getString(R.string.session_now));
                }
            } else {
                // session in the future
                time.append(TimeUtils.formatShortTime(mContext, new Date(item.startTime)));
            }
            time.append(" - ");
            time.append(TimeUtils.formatShortTime(mContext, new Date(item.endTime)));
            return time.toString();
        }
    }
}
