/*
 * Copyright 2021 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 androidx.media3.exoplayer.rtsp;

import static androidx.media3.common.util.Util.castNonNull;

import android.net.Uri;
import androidx.annotation.Nullable;
import androidx.media3.common.Format;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;

/**
 * Records all the information in a SDP message.
 *
 * <p>SDP messages encapsulate information on the media play back session, including session
 * configuration information, formats of each playable track, etc. SDP is defined in RFC4566.
 */
@UnstableApi
/* package */ final class SessionDescription {

  /** Builder class for {@link SessionDescription}. */
  public static final class Builder {
    private final HashMap<String, String> attributes;
    private final ImmutableList.Builder<MediaDescription> mediaDescriptionListBuilder;
    private int bitrate;
    @Nullable private String sessionName;
    @Nullable private String origin;
    @Nullable private String timing;
    @Nullable private Uri uri;
    @Nullable private String connection;
    @Nullable private String key;
    @Nullable private String sessionInfo;
    @Nullable private String emailAddress;
    @Nullable private String phoneNumber;

    /** Creates a new instance. */
    public Builder() {
      attributes = new HashMap<>();
      mediaDescriptionListBuilder = new ImmutableList.Builder<>();
      bitrate = Format.NO_VALUE;
    }

    /**
     * Sets {@link SessionDescription#sessionName}.
     *
     * <p>This property must be set before calling {@link #build()}.
     *
     * @param sessionName The {@link SessionDescription#sessionName}.
     * @return This builder.
     */
    public Builder setSessionName(String sessionName) {
      this.sessionName = sessionName;
      return this;
    }

    /**
     * Sets {@link SessionDescription#sessionInfo}. The default is {@code null}.
     *
     * @param sessionInfo The {@link SessionDescription#sessionInfo}.
     * @return This builder.
     */
    public Builder setSessionInfo(String sessionInfo) {
      this.sessionInfo = sessionInfo;
      return this;
    }

    /**
     * Sets {@link SessionDescription#uri}. The default is {@code null}.
     *
     * @param uri The {@link SessionDescription#uri}.
     * @return This builder.
     */
    public Builder setUri(Uri uri) {
      this.uri = uri;
      return this;
    }

    /**
     * Sets {@link SessionDescription#origin}.
     *
     * <p>This property must be set before calling {@link #build()}.
     *
     * @param origin The {@link SessionDescription#origin}.
     * @return This builder.
     */
    public Builder setOrigin(String origin) {
      this.origin = origin;
      return this;
    }

    /**
     * Sets {@link SessionDescription#connection}. The default is {@code null}.
     *
     * @param connection The {@link SessionDescription#connection}.
     * @return This builder.
     */
    public Builder setConnection(String connection) {
      this.connection = connection;
      return this;
    }

    /**
     * Sets {@link SessionDescription#bitrate}. The default is {@link Format#NO_VALUE}.
     *
     * @param bitrate The {@link SessionDescription#bitrate} in bits per second.
     * @return This builder.
     */
    public Builder setBitrate(int bitrate) {
      this.bitrate = bitrate;
      return this;
    }

    /**
     * Sets {@link SessionDescription#timing}.
     *
     * <p>This property must be set before calling {@link #build()}.
     *
     * @param timing The {@link SessionDescription#timing}.
     * @return This builder.
     */
    public Builder setTiming(String timing) {
      this.timing = timing;
      return this;
    }

    /**
     * Sets {@link SessionDescription#key}. The default is {@code null}.
     *
     * @param key The {@link SessionDescription#key}.
     * @return This builder.
     */
    public Builder setKey(String key) {
      this.key = key;
      return this;
    }

    /**
     * Sets {@link SessionDescription#emailAddress}. The default is {@code null}.
     *
     * @param emailAddress The {@link SessionDescription#emailAddress}.
     * @return This builder.
     */
    public Builder setEmailAddress(String emailAddress) {
      this.emailAddress = emailAddress;
      return this;
    }

    /**
     * Sets {@link SessionDescription#phoneNumber}. The default is {@code null}.
     *
     * @param phoneNumber The {@link SessionDescription#phoneNumber}.
     * @return This builder.
     */
    public Builder setPhoneNumber(String phoneNumber) {
      this.phoneNumber = phoneNumber;
      return this;
    }

    /**
     * Adds one attribute to {@link SessionDescription#attributes}.
     *
     * @param attributeName The name of the attribute.
     * @param attributeValue The value of the attribute.
     * @return This builder.
     */
    public Builder addAttribute(String attributeName, String attributeValue) {
      attributes.put(attributeName, attributeValue);
      return this;
    }

    /**
     * Adds one {@link MediaDescription} to the {@link SessionDescription#mediaDescriptionList}.
     *
     * @param mediaDescription The {@link MediaDescription}.
     * @return This builder.
     */
    public Builder addMediaDescription(MediaDescription mediaDescription) {
      mediaDescriptionListBuilder.add(mediaDescription);
      return this;
    }

    /**
     * Builds a new {@link SessionDescription} instance.
     *
     * @return The newly built {@link SessionDescription} instance.
     */
    public SessionDescription build() {
      return new SessionDescription(this);
    }
  }

  /** The only supported SDP version, will be checked against every SDP message received. */
  public static final String SUPPORTED_SDP_VERSION = "0";
  /** The control attribute name. */
  public static final String ATTR_CONTROL = "control";
  /** The format property attribute name. */
  public static final String ATTR_FMTP = "fmtp";
  /** The length property attribute name. */
  public static final String ATTR_LENGTH = "length";
  /** The range property attribute name. */
  public static final String ATTR_RANGE = "range";
  /** The RTP format mapping property attribute name. */
  public static final String ATTR_RTPMAP = "rtpmap";
  /** The tool property attribute name. */
  public static final String ATTR_TOOL = "tool";
  /** The type property attribute name. */
  public static final String ATTR_TYPE = "type";

  /**
   * All the session attributes, mapped from attribute name to value. The value is {@code ""} if not
   * present.
   */
  public final ImmutableMap<String, String> attributes;
  /**
   * The {@link MediaDescription MediaDescriptions} for each media track included in the session.
   */
  public final ImmutableList<MediaDescription> mediaDescriptionList;
  /** The name of a session. */
  @Nullable public final String sessionName;
  /** The origin sender info. */
  @Nullable public final String origin;
  /** The timing info. */
  @Nullable public final String timing;
  /** The estimated bitrate in bits per seconds. */
  public final int bitrate;
  /** The uri of a linked content. */
  @Nullable public final Uri uri;
  /** The connection info. */
  @Nullable public final String connection;
  /** The encryption method and key info. */
  @Nullable public final String key;
  /** The email info. */
  @Nullable public final String emailAddress;
  /** The phone number info. */
  @Nullable public final String phoneNumber;
  /** The session info, a detailed description of the session. */
  @Nullable public final String sessionInfo;

  /** Creates a new instance. */
  private SessionDescription(Builder builder) {
    this.attributes = ImmutableMap.copyOf(builder.attributes);
    this.mediaDescriptionList = builder.mediaDescriptionListBuilder.build();
    this.sessionName = castNonNull(builder.sessionName);
    this.origin = castNonNull(builder.origin);
    this.timing = castNonNull(builder.timing);
    this.uri = builder.uri;
    this.connection = builder.connection;
    this.bitrate = builder.bitrate;
    this.key = builder.key;
    this.emailAddress = builder.emailAddress;
    this.phoneNumber = builder.phoneNumber;
    this.sessionInfo = builder.sessionInfo;
  }

  @Override
  public boolean equals(@Nullable Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    SessionDescription that = (SessionDescription) o;
    return bitrate == that.bitrate
        && attributes.equals(that.attributes)
        && mediaDescriptionList.equals(that.mediaDescriptionList)
        && Util.areEqual(origin, that.origin)
        && Util.areEqual(sessionName, that.sessionName)
        && Util.areEqual(timing, that.timing)
        && Util.areEqual(sessionInfo, that.sessionInfo)
        && Util.areEqual(uri, that.uri)
        && Util.areEqual(emailAddress, that.emailAddress)
        && Util.areEqual(phoneNumber, that.phoneNumber)
        && Util.areEqual(connection, that.connection)
        && Util.areEqual(key, that.key);
  }

  @Override
  public int hashCode() {
    int result = 7;
    result = 31 * result + attributes.hashCode();
    result = 31 * result + mediaDescriptionList.hashCode();
    result = 31 * result + (origin == null ? 0 : origin.hashCode());
    result = 31 * result + (sessionName == null ? 0 : sessionName.hashCode());
    result = 31 * result + (timing == null ? 0 : timing.hashCode());
    result = 31 * result + bitrate;
    result = 31 * result + (sessionInfo == null ? 0 : sessionInfo.hashCode());
    result = 31 * result + (uri == null ? 0 : uri.hashCode());
    result = 31 * result + (emailAddress == null ? 0 : emailAddress.hashCode());
    result = 31 * result + (phoneNumber == null ? 0 : phoneNumber.hashCode());
    result = 31 * result + (connection == null ? 0 : connection.hashCode());
    result = 31 * result + (key == null ? 0 : key.hashCode());
    return result;
  }
}
