/*
 * Copyright (c) 2013 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.ims;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;

import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.telecom.Call;
import android.telecom.Connection;

/**
 * Provides the conference information (defined in RFC 4575) for IMS conference call.
 *
 * @hide
 */
public class ImsConferenceState implements Parcelable {
    /**
     * conference-info : user
     */
    // user (String) : Tel or SIP URI
    public static final String USER = "user";
    // user > display text (String)
    public static final String DISPLAY_TEXT = "display-text";
    // user > endpoint (String) : URI or GRUU or Phone number
    public static final String ENDPOINT = "endpoint";
    // user > endpoint > status
    public static final String STATUS = "status";

    /**
     * status-type (String) :
     * "pending" : Endpoint is not yet in the session, but it is anticipated that he/she will
     *      join in the near future.
     * "dialing-out" : Focus has dialed out to connect the endpoint to the conference,
     *      but the endpoint is not yet in the roster (probably being authenticated).
     * "dialing-in" : Endpoint is dialing into the conference, not yet in the roster
     *      (probably being authenticated).
     * "alerting" : PSTN alerting or SIP 180 Ringing was returned for the outbound call;
     *      endpoint is being alerted.
     * "on-hold" : Active signaling dialog exists between an endpoint and a focus,
     *      but endpoint is "on-hold" for this conference, i.e., he/she is neither "hearing"
     *      the conference mix nor is his/her media being mixed in the conference.
     * "connected" : Endpoint is a participant in the conference. Depending on the media policies,
     *      he/she can send and receive media to and from other participants.
     * "disconnecting" : Focus is in the process of disconnecting the endpoint
     *      (e.g. in SIP a DISCONNECT or BYE was sent to the endpoint).
     * "disconnected" : Endpoint is not a participant in the conference, and no active dialog
     *      exists between the endpoint and the focus.
     * "muted-via-focus" : Active signaling dialog exists beween an endpoint and a focus and
     *      the endpoint can "listen" to the conference, but the endpoint's media is not being
     *      mixed into the conference.
     * "connect-fail" : Endpoint fails to join the conference by rejecting the conference call.
     */
    public static final String STATUS_PENDING = "pending";
    public static final String STATUS_DIALING_OUT = "dialing-out";
    public static final String STATUS_DIALING_IN = "dialing-in";
    public static final String STATUS_ALERTING = "alerting";
    public static final String STATUS_ON_HOLD = "on-hold";
    public static final String STATUS_CONNECTED = "connected";
    public static final String STATUS_DISCONNECTING = "disconnecting";
    public static final String STATUS_DISCONNECTED = "disconnected";
    public static final String STATUS_MUTED_VIA_FOCUS = "muted-via-focus";
    public static final String STATUS_CONNECT_FAIL = "connect-fail";
    public static final String STATUS_SEND_ONLY = "sendonly";
    public static final String STATUS_SEND_RECV = "sendrecv";

    /**
     * conference-info : SIP status code (integer)
     */
    public static final String SIP_STATUS_CODE = "sipstatuscode";

    public HashMap<String, Bundle> mParticipants = new HashMap<String, Bundle>();

    public ImsConferenceState() {
    }

    public ImsConferenceState(Parcel in) {
        readFromParcel(in);
    }

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

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(mParticipants.size());

        if (mParticipants.size() > 0) {
            Set<Entry<String, Bundle>> entries = mParticipants.entrySet();

            if (entries != null) {
                Iterator<Entry<String, Bundle>> iterator = entries.iterator();

                while (iterator.hasNext()) {
                    Entry<String, Bundle> entry = iterator.next();

                    out.writeString(entry.getKey());
                    out.writeParcelable(entry.getValue(), 0);
                }
            }
        }
    }

    private void readFromParcel(Parcel in) {
        int size = in.readInt();

        for (int i = 0; i < size; ++i) {
            String user = in.readString();
            Bundle state = in.readParcelable(null);
            mParticipants.put(user, state);
        }
    }

    public static final Creator<ImsConferenceState> CREATOR =
            new Creator<ImsConferenceState>() {
        @Override
        public ImsConferenceState createFromParcel(Parcel in) {
            return new ImsConferenceState(in);
        }

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

    /**
     * Translates an {@code ImsConferenceState} status type to a telecom connection state.
     *
     * @param status The status type.
     * @return The corresponding {@link android.telecom.Connection} state.
     */
    public static int getConnectionStateForStatus(String status) {
        if (status.equals(STATUS_PENDING)) {
            return Connection.STATE_INITIALIZING;
        } else if (status.equals(STATUS_DIALING_IN)) {
            return Connection.STATE_RINGING;
        } else if (status.equals(STATUS_ALERTING) ||
                status.equals(STATUS_DIALING_OUT)) {
            return Connection.STATE_DIALING;
        } else if (status.equals(STATUS_ON_HOLD) ||
                status.equals(STATUS_SEND_ONLY)) {
            return Connection.STATE_HOLDING;
        } else if (status.equals(STATUS_CONNECTED) ||
                status.equals(STATUS_MUTED_VIA_FOCUS) ||
                status.equals(STATUS_DISCONNECTING) ||
                status.equals(STATUS_SEND_RECV)) {
            return Connection.STATE_ACTIVE;
        } else if (status.equals(STATUS_DISCONNECTED)) {
            return Connection.STATE_DISCONNECTED;
        }
        return Call.STATE_ACTIVE;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        sb.append(ImsConferenceState.class.getSimpleName());
        sb.append(" ");
        if (mParticipants.size() > 0) {
            Set<Entry<String, Bundle>> entries = mParticipants.entrySet();

            if (entries != null) {
                Iterator<Entry<String, Bundle>> iterator = entries.iterator();
                sb.append("<");
                while (iterator.hasNext()) {
                    Entry<String, Bundle> entry = iterator.next();
                    sb.append(entry.getKey());
                    sb.append(": ");
                    Bundle participantData = entry.getValue();

                    for (String key : participantData.keySet()) {
                        sb.append(key);
                        sb.append("=");
                        if (ENDPOINT.equals(key) || USER.equals(key)) {
                            sb.append(android.telecom.Log.pii(participantData.get(key)));
                        } else {
                            sb.append(participantData.get(key));
                        }
                        sb.append(", ");
                    }
                }
                sb.append(">");
            }
        }
        sb.append("]");
        return sb.toString();
    }
}
