package eu.siacs.conversations.xmpp.jingle;

import androidx.annotation.NonNull;

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;

import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;

public abstract class AbstractJingleConnection {

    public static final String JINGLE_MESSAGE_PROPOSE_ID_PREFIX = "jm-propose-";
    public static final String JINGLE_MESSAGE_PROCEED_ID_PREFIX = "jm-proceed-";

    final JingleConnectionManager jingleConnectionManager;
    protected final XmppConnectionService xmppConnectionService;
    protected final Id id;
    private final Jid initiator;

    AbstractJingleConnection(final JingleConnectionManager jingleConnectionManager, final Id id, final Jid initiator) {
        this.jingleConnectionManager = jingleConnectionManager;
        this.xmppConnectionService = jingleConnectionManager.getXmppConnectionService();
        this.id = id;
        this.initiator = initiator;
    }

    boolean isInitiator() {
        return initiator.equals(id.account.getJid());
    }

    abstract void deliverPacket(JinglePacket jinglePacket);

    public Id getId() {
        return id;
    }

    abstract void notifyRebound();


    public static class Id implements OngoingRtpSession {
        public final Account account;
        public final Jid with;
        public final String sessionId;

        private Id(final Account account, final Jid with, final String sessionId) {
            Preconditions.checkNotNull(account);
            Preconditions.checkNotNull(with);
            Preconditions.checkNotNull(sessionId);
            this.account = account;
            this.with = with;
            this.sessionId = sessionId;
        }

        public static Id of(Account account, JinglePacket jinglePacket) {
            return new Id(account, jinglePacket.getFrom(), jinglePacket.getSessionId());
        }

        public static Id of(Account account, Jid with, final String sessionId) {
            return new Id(account, with, sessionId);
        }

        public static Id of(Account account, Jid with) {
            return new Id(account, with, JingleConnectionManager.nextRandomId());
        }

        public static Id of(Message message) {
            return new Id(
                    message.getConversation().getAccount(),
                    message.getCounterpart(),
                    JingleConnectionManager.nextRandomId()
            );
        }

        public Contact getContact() {
            return account.getRoster().getContact(with);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Id id = (Id) o;
            return Objects.equal(account.getUuid(), id.account.getUuid()) &&
                    Objects.equal(with, id.with) &&
                    Objects.equal(sessionId, id.sessionId);
        }

        @Override
        public int hashCode() {
            return Objects.hashCode(account.getUuid(), with, sessionId);
        }

        @Override
        public Account getAccount() {
            return account;
        }

        @Override
        public Jid getWith() {
            return with;
        }

        @Override
        public String getSessionId() {
            return sessionId;
        }

        @Override
        @NonNull
        public String toString() {
            return MoreObjects.toStringHelper(this)
                    .add("account", account.getJid())
                    .add("with", with)
                    .add("sessionId", sessionId)
                    .toString();
        }
    }


    public enum State {
        NULL, //default value; nothing has been sent or received yet
        PROPOSED,
        ACCEPTED,
        PROCEED,
        REJECTED,
        REJECTED_RACED, //used when we want to reject but haven’t received session init yet
        RETRACTED,
        RETRACTED_RACED, //used when receiving a retract after we already asked to proceed
        SESSION_INITIALIZED, //equal to 'PENDING'
        SESSION_INITIALIZED_PRE_APPROVED,
        SESSION_ACCEPTED, //equal to 'ACTIVE'
        TERMINATED_SUCCESS, //equal to 'ENDED' (after successful call) ui will just close
        TERMINATED_DECLINED_OR_BUSY, //equal to 'ENDED' (after other party declined the call)
        TERMINATED_CONNECTIVITY_ERROR, //equal to 'ENDED' (but after network failures; ui will display retry button)
        TERMINATED_CANCEL_OR_TIMEOUT, //more or less the same as retracted; caller pressed end call before session was accepted
        TERMINATED_APPLICATION_FAILURE,
        TERMINATED_SECURITY_ERROR
    }
}
