/*
 * Decompiled with CFR 0.152.
 */
package org.jivesoftware.smack;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.packet.PlainStreamElement;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.TopLevelStreamElement;

public class SynchronizationPoint<E extends Exception> {
    private static final Logger LOGGER = Logger.getLogger(SynchronizationPoint.class.getName());
    private final AbstractXMPPConnection connection;
    private final Lock connectionLock;
    private final Condition condition;
    private State state;
    private E failureException;

    public SynchronizationPoint(AbstractXMPPConnection connection) {
        this.connection = connection;
        this.connectionLock = connection.getConnectionLock();
        this.condition = connection.getConnectionLock().newCondition();
        this.init();
    }

    public void init() {
        this.connectionLock.lock();
        this.state = State.Initial;
        this.failureException = null;
        this.connectionLock.unlock();
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public void sendAndWaitForResponse(TopLevelStreamElement request) throws SmackException.NoResponseException, SmackException.NotConnectedException, InterruptedException {
        assert (this.state == State.Initial);
        this.connectionLock.lock();
        try {
            if (request != null) {
                if (request instanceof Stanza) {
                    this.connection.sendStanza((Stanza)request);
                } else if (request instanceof PlainStreamElement) {
                    this.connection.send((PlainStreamElement)request);
                } else {
                    throw new IllegalStateException("Unsupported element type");
                }
                this.state = State.RequestSent;
            }
            this.waitForConditionOrTimeout();
        }
        finally {
            this.connectionLock.unlock();
        }
        this.checkForResponse();
    }

    public void sendAndWaitForResponseOrThrow(PlainStreamElement request) throws E, SmackException.NoResponseException, SmackException.NotConnectedException, InterruptedException {
        this.sendAndWaitForResponse(request);
        switch (this.state) {
            case Failure: {
                if (this.failureException == null) break;
                throw this.failureException;
            }
        }
    }

    public void checkIfSuccessOrWaitOrThrow() throws SmackException.NoResponseException, E {
        this.checkIfSuccessOrWait();
        if (this.state == State.Failure) {
            throw this.failureException;
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public void checkIfSuccessOrWait() throws SmackException.NoResponseException {
        this.connectionLock.lock();
        try {
            if (this.state == State.Success) {
                return;
            }
            this.waitForConditionOrTimeout();
        }
        finally {
            this.connectionLock.unlock();
        }
        this.checkForResponse();
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public void reportSuccess() {
        this.connectionLock.lock();
        try {
            this.state = State.Success;
            this.condition.signal();
        }
        finally {
            this.connectionLock.unlock();
        }
    }

    public void reportFailure() {
        this.reportFailure(null);
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public void reportFailure(E failureException) {
        this.connectionLock.lock();
        try {
            this.state = State.Failure;
            this.failureException = failureException;
            this.condition.signal();
        }
        finally {
            this.connectionLock.unlock();
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public boolean wasSuccessful() {
        this.connectionLock.lock();
        try {
            boolean bl = this.state == State.Success;
            return bl;
        }
        finally {
            this.connectionLock.unlock();
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public boolean requestSent() {
        this.connectionLock.lock();
        try {
            boolean bl = this.state == State.RequestSent;
            return bl;
        }
        finally {
            this.connectionLock.unlock();
        }
    }

    private void waitForConditionOrTimeout() {
        long remainingWait = TimeUnit.MILLISECONDS.toNanos(this.connection.getPacketReplyTimeout());
        while (this.state == State.RequestSent || this.state == State.Initial) {
            try {
                if ((remainingWait = this.condition.awaitNanos(remainingWait)) > 0L) continue;
                this.state = State.NoResponse;
                break;
            }
            catch (InterruptedException e) {
                LOGGER.log(Level.WARNING, "Thread interrupt while waiting for condition or timeout ignored", e);
            }
        }
    }

    private void checkForResponse() throws SmackException.NoResponseException {
        switch (this.state) {
            case Initial: 
            case NoResponse: 
            case RequestSent: {
                throw SmackException.NoResponseException.newWith(this.connection);
            }
        }
    }

    private static enum State {
        Initial,
        RequestSent,
        NoResponse,
        Success,
        Failure;

    }
}
