/*
 * Copyright (C) 2012 Google Inc.
 * Licensed to 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.mail.ui;

import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;

import com.android.mail.providers.Conversation;
import com.android.mail.providers.Message;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import java.util.Map;
import java.util.Set;

/**
 * A small class to keep state for conversation view when restoring.
 *
 */
public class ConversationViewState implements Parcelable {

    // N.B. don't serialize entire Messages because they contain body HTML/text

    private final Map<Uri, MessageViewState> mMessageViewStates = Maps.newHashMap();

    private byte[] mConversationInfo;

    public static final class ExpansionState {
        public static int NONE = 0;
        public static int EXPANDED = 1;
        public static int COLLAPSED = 2;
        public static int SUPER_COLLAPSED = 3;

        private ExpansionState() {}

        public static boolean isExpanded(int state) {
            return state == EXPANDED;
        }
        public static boolean isSuperCollapsed(int state) {
            return state == SUPER_COLLAPSED;
        }
    }

    public ConversationViewState() {}

    /**
     * Copy constructor that will copy overall conversation state, but NOT individual message state.
     */
    public ConversationViewState(ConversationViewState other) {
        mConversationInfo = other.mConversationInfo;
    }

    public boolean isUnread(Message m) {
        final MessageViewState mvs = mMessageViewStates.get(m.uri);
        return (mvs != null && !mvs.read);
    }

    public void setReadState(Message m, boolean read) {
        MessageViewState mvs = mMessageViewStates.get(m.uri);
        if (mvs == null) {
            mvs = new MessageViewState();
        }
        mvs.read = read;
        mMessageViewStates.put(m.uri, mvs);
    }

    public boolean getShouldShowImages(Message m) {
        final MessageViewState mvs = mMessageViewStates.get(m.uri);
        return (mvs != null && mvs.showImages);
    }

    public void setShouldShowImages(Message m, boolean showImages) {
        MessageViewState mvs = mMessageViewStates.get(m.uri);
        if (mvs == null) {
            mvs = new MessageViewState();
        }
        mvs.showImages = showImages;
        mMessageViewStates.put(m.uri, mvs);
    }

    /**
     * Returns the expansion state of a message in a conversation view.
     *
     * @param m a Message in the conversation
     * @return 1 = expanded, 2 = collapsed, 3 = super collapsed, or null otherwise
     * (see {@link ExpansionState}).
     */
    public Integer getExpansionState(Message m) {
        final MessageViewState mvs = mMessageViewStates.get(m.uri);
        return (mvs == null ? null : mvs.expansionState);
    }

    public void setExpansionState(Message m, int expansionState) {
        MessageViewState mvs = mMessageViewStates.get(m.uri);
        if (mvs == null) {
            mvs = new MessageViewState();
        }
        mvs.expansionState = expansionState;
        mMessageViewStates.put(m.uri, mvs);
    }

    public byte[] getConversationInfo() {
        return mConversationInfo;
    }

    public void setInfoForConversation(Conversation conv) {
        mConversationInfo = conv.conversationInfo.toBlob();
    }

    /**
     * Returns URIs of all unread messages in the conversation per
     * {@link #setReadState(Message, boolean)}. Returns an empty set for read conversations.
     *
     */
    public Set<Uri> getUnreadMessageUris() {
        final Set<Uri> result = Sets.newHashSet();
        for (Uri uri : mMessageViewStates.keySet()) {
            final MessageViewState mvs = mMessageViewStates.get(uri);
            if (mvs != null && !mvs.read) {
                result.add(uri);
            }
        }
        return result;
    }

    public boolean contains(Message m) {
        return mMessageViewStates.containsKey(m.uri);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        final Bundle states = new Bundle();
        for (Uri uri : mMessageViewStates.keySet()) {
            final MessageViewState mvs = mMessageViewStates.get(uri);
            states.putParcelable(uri.toString(), mvs);
        }
        dest.writeBundle(states);
        dest.writeByteArray(mConversationInfo);
    }

    private ConversationViewState(Parcel source, ClassLoader loader) {
        final Bundle states = source.readBundle(loader);
        for (String key : states.keySet()) {
            final MessageViewState state = states.getParcelable(key);
            mMessageViewStates.put(Uri.parse(key), state);
        }
        mConversationInfo = source.createByteArray();
    }

    public static final ClassLoaderCreator<ConversationViewState> CREATOR =
            new ClassLoaderCreator<ConversationViewState>() {

        @Override
        public ConversationViewState createFromParcel(Parcel source) {
            return new ConversationViewState(source, null);
        }

        @Override
        public ConversationViewState createFromParcel(Parcel source, ClassLoader loader) {
            return new ConversationViewState(source, loader);
        }

        @Override
        public ConversationViewState[] newArray(int size) {
            return new ConversationViewState[size];
        }

    };

    // Keep per-message state in an inner Parcelable.
    // This is a semi-private implementation detail.
    static class MessageViewState implements Parcelable {

        public boolean read;
        /**
         * See {@link ExpansionState} for values.
         *
         */
        public Integer expansionState;
        public boolean showImages;

        public MessageViewState() {}

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(read ? 1 : 0);
            dest.writeInt(expansionState == null ? -1 : expansionState.intValue());
            dest.writeInt(showImages ? 1 : 0);
        }

        private MessageViewState(Parcel source) {
            read = (source.readInt() != 0);
            final int expandedVal = source.readInt();
            expansionState = (expandedVal == -1) ? null : expandedVal;
            showImages = (source.readInt() != 0);
        }

        @SuppressWarnings("hiding")
        public static final Parcelable.Creator<MessageViewState> CREATOR =
                new Parcelable.Creator<MessageViewState>() {

            @Override
            public MessageViewState createFromParcel(Parcel source) {
                return new MessageViewState(source);
            }

            @Override
            public MessageViewState[] newArray(int size) {
                return new MessageViewState[size];
            }

        };

    }

}
