/*
 * Copyright (C) 2019 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 android.telephony.ims;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.net.Uri;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join
 * or leave. Please see Section 6 (Group Chat) - GSMA RCC.71 (RCS Universal Profile Service
 * Definition Document)
 *
 * @hide
 */
public class RcsGroupThread extends RcsThread {
    /**
     * Public constructor only for RcsMessageStoreController to initialize new threads.
     *
     * @hide
     */
    public RcsGroupThread(RcsControllerCall rcsControllerCall, int threadId) {
        super(rcsControllerCall, threadId);
    }

    /**
     * @return Returns {@code true} as this is always a group thread
     */
    @Override
    public boolean isGroup() {
        return true;
    }

    /**
     * @return Returns the given name of this {@link RcsGroupThread}. Please see US6-2 - GSMA RCC.71
     * (RCS Universal Profile Service Definition Document)
     * @throws RcsMessageStoreException if the value could not be read from the storage
     */
    @Nullable
    @WorkerThread
    public String getGroupName() throws RcsMessageStoreException {
        return mRcsControllerCall.call(
                (iRcs, callingPackage) -> iRcs.getGroupThreadName(mThreadId, callingPackage));
    }

    /**
     * Sets the name of this {@link RcsGroupThread} and saves it into storage. Please see US6-2 -
     * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
     *
     * @throws RcsMessageStoreException if the value could not be persisted into storage
     */
    @WorkerThread
    public void setGroupName(String groupName) throws RcsMessageStoreException {
        mRcsControllerCall.callWithNoReturn(
                (iRcs, callingPackage) -> iRcs.setGroupThreadName(mThreadId, groupName,
                        callingPackage));
    }

    /**
     * @return Returns a URI that points to the group's icon {@link RcsGroupThread}. Please see
     * US6-2 - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
     * @throws RcsMessageStoreException if the value could not be read from the storage
     */
    @Nullable
    public Uri getGroupIcon() throws RcsMessageStoreException {
        return mRcsControllerCall.call(
                (iRcs, callingPackage) -> iRcs.getGroupThreadIcon(mThreadId, callingPackage));
    }

    /**
     * Sets the icon for this {@link RcsGroupThread} and saves it into storage. Please see US6-2 -
     * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
     *
     * @throws RcsMessageStoreException if the value could not be persisted into storage
     */
    @WorkerThread
    public void setGroupIcon(@Nullable Uri groupIcon) throws RcsMessageStoreException {
        mRcsControllerCall.callWithNoReturn(
                (iRcs, callingPackage) -> iRcs.setGroupThreadIcon(mThreadId, groupIcon,
                        callingPackage));
    }

    /**
     * @return Returns the owner of this thread or {@code null} if there doesn't exist an owner
     * @throws RcsMessageStoreException if the value could not be read from the storage
     */
    @Nullable
    @WorkerThread
    public RcsParticipant getOwner() throws RcsMessageStoreException {
        return new RcsParticipant(
                mRcsControllerCall,
                mRcsControllerCall.call(
                        (iRcs, callingPackage) -> iRcs.getGroupThreadOwner(mThreadId,
                                callingPackage)));
    }

    /**
     * Sets the owner of this {@link RcsGroupThread} and saves it into storage. This is intended to
     * be used for selecting a new owner for a group thread if the owner leaves the thread. The
     * owner needs to be in the list of existing participants.
     *
     * @param participant The new owner of the thread. {@code null} values are allowed.
     * @throws RcsMessageStoreException if the operation could not be persisted into storage
     */
    @WorkerThread
    public void setOwner(@Nullable RcsParticipant participant) throws RcsMessageStoreException {
        mRcsControllerCall.callWithNoReturn(
                (iRcs, callingPackage) -> iRcs.setGroupThreadOwner(mThreadId, participant.getId(),
                        callingPackage));
    }

    /**
     * Adds a new {@link RcsParticipant} to this group thread and persists into storage. If the user
     * is actively participating in this {@link RcsGroupThread}, an {@link RcsParticipant} on behalf
     * of them should be added.
     *
     * @param participant The new participant to be added to the thread.
     * @throws RcsMessageStoreException if the operation could not be persisted into storage
     */
    @WorkerThread
    public void addParticipant(@NonNull RcsParticipant participant)
            throws RcsMessageStoreException {
        if (participant == null) {
            return;
        }

        mRcsControllerCall.callWithNoReturn(
                (iRcs, callingPackage) -> iRcs.addParticipantToGroupThread(mThreadId,
                        participant.getId(), callingPackage));
    }

    /**
     * Removes an {@link RcsParticipant} from this group thread and persists into storage. If the
     * removed participant was the owner of this group, the owner will become null.
     *
     * @throws RcsMessageStoreException if the operation could not be persisted into storage
     */
    @WorkerThread
    public void removeParticipant(@NonNull RcsParticipant participant)
            throws RcsMessageStoreException {
        if (participant == null) {
            return;
        }

        mRcsControllerCall.callWithNoReturn(
                (iRcs, callingPackage) -> iRcs.removeParticipantFromGroupThread(mThreadId,
                        participant.getId(), callingPackage));
    }

    /**
     * Returns the set of {@link RcsParticipant}s that contribute to this group thread. The
     * returned set does not support modifications, please use
     * {@link RcsGroupThread#addParticipant(RcsParticipant)}
     * and {@link RcsGroupThread#removeParticipant(RcsParticipant)} instead.
     *
     * @return the immutable set of {@link RcsParticipant} in this group thread.
     * @throws RcsMessageStoreException if the values could not be read from the storage
     */
    @WorkerThread
    @NonNull
    public Set<RcsParticipant> getParticipants() throws RcsMessageStoreException {
        RcsParticipantQueryParams queryParameters =
                new RcsParticipantQueryParams.Builder().setThread(this).build();

        RcsParticipantQueryResult queryResult = new RcsParticipantQueryResult(
                mRcsControllerCall,
                mRcsControllerCall.call(
                        (iRcs, callingPackage) -> iRcs.getParticipants(queryParameters,
                                callingPackage)));

        List<RcsParticipant> participantList = queryResult.getParticipants();
        Set<RcsParticipant> participantSet = new LinkedHashSet<>(participantList);
        return Collections.unmodifiableSet(participantSet);
    }

    /**
     * Returns the conference URI for this {@link RcsGroupThread}. Please see 4.4.5.2 - GSMA RCC.53
     * (RCS Device API 1.6 Specification
     *
     * @throws RcsMessageStoreException if the value could not be read from the storage
     */
    @Nullable
    @WorkerThread
    public Uri getConferenceUri() throws RcsMessageStoreException {
        return mRcsControllerCall.call(
                (iRcs, callingPackage) -> iRcs.getGroupThreadConferenceUri(mThreadId,
                        callingPackage));
    }

    /**
     * Sets the conference URI for this {@link RcsGroupThread} and persists into storage. Please see
     * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
     *
     * @param conferenceUri The URI as String to be used as the conference URI.
     * @throws RcsMessageStoreException if the value could not be persisted into storage
     */
    @Nullable
    @WorkerThread
    public void setConferenceUri(Uri conferenceUri) throws RcsMessageStoreException {
        mRcsControllerCall.callWithNoReturn(
                (iRcs, callingPackage) -> iRcs.setGroupThreadConferenceUri(mThreadId, conferenceUri,
                        callingPackage));
    }
}
