package org.jivesoftware.smack;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.util.dns.HostAddress;
import org.jxmpp.jid.Jid;

public class SmackException extends Exception {
   private static final long serialVersionUID = 1844674365368214457L;

   public SmackException(Throwable wrappedThrowable) {
      super(wrappedThrowable);
   }

   public SmackException(String message) {
      super(message);
   }

   public SmackException(String message, Throwable wrappedThrowable) {
      super(message, wrappedThrowable);
   }

   protected SmackException() {
   }

   public static class ResourceBindingNotOfferedException extends SmackException {
      private static final long serialVersionUID = 2346934138253437571L;

      public ResourceBindingNotOfferedException() {
         super("Resource binding was not offered by server");
      }
   }

   public static class FeatureNotSupportedException extends SmackException {
      private static final long serialVersionUID = 4713404802621452016L;
      private final String feature;
      private final Jid jid;

      public FeatureNotSupportedException(String feature) {
         this(feature, (Jid)null);
      }

      public FeatureNotSupportedException(String feature, Jid jid) {
         super(feature + " not supported" + (jid == null ? "" : " by '" + jid + "'"));
         this.jid = jid;
         this.feature = feature;
      }

      public String getFeature() {
         return this.feature;
      }

      public Jid getJid() {
         return this.jid;
      }
   }

   public static class ConnectionException extends SmackException {
      private static final long serialVersionUID = 1686944201672697996L;
      private final List<HostAddress> failedAddresses;

      public ConnectionException(Throwable wrappedThrowable) {
         super(wrappedThrowable);
         this.failedAddresses = new ArrayList(0);
      }

      private ConnectionException(String message, List<HostAddress> failedAddresses) {
         super(message);
         this.failedAddresses = failedAddresses;
      }

      public static SmackException.ConnectionException from(List<HostAddress> failedAddresses) {
         String DELIMITER = ", ";
         StringBuilder sb = new StringBuilder("The following addresses failed: ");
         Iterator i$ = failedAddresses.iterator();

         while(i$.hasNext()) {
            HostAddress hostAddress = (HostAddress)i$.next();
            sb.append(hostAddress.getErrorMessage());
            sb.append(", ");
         }

         sb.setLength(sb.length() - ", ".length());
         return new SmackException.ConnectionException(sb.toString(), failedAddresses);
      }

      public List<HostAddress> getFailedAddresses() {
         return this.failedAddresses;
      }
   }

   public static class SecurityNotPossibleException extends SmackException {
      private static final long serialVersionUID = -6836090872690331336L;

      public SecurityNotPossibleException(String message) {
         super(message);
      }
   }

   public static class SecurityRequiredByServerException extends SmackException.SecurityRequiredException {
      private static final long serialVersionUID = 8268148813117631819L;

      public SecurityRequiredByServerException() {
         super("SSL/TLS required by server but disabled in client");
      }
   }

   public static class SecurityRequiredByClientException extends SmackException.SecurityRequiredException {
      private static final long serialVersionUID = 2395325821201543159L;

      public SecurityRequiredByClientException() {
         super("SSL/TLS required by client but not supported by server");
      }
   }

   public abstract static class SecurityRequiredException extends SmackException {
      private static final long serialVersionUID = 384291845029773545L;

      public SecurityRequiredException(String message) {
         super(message);
      }
   }

   public static class IllegalStateChangeException extends SmackException {
      private static final long serialVersionUID = -1766023961577168927L;
   }

   public static class NotConnectedException extends SmackException {
      private static final long serialVersionUID = 9197980400776001173L;

      public NotConnectedException() {
         super("Client is not, or no longer, connected");
      }
   }

   public static class AlreadyConnectedException extends SmackException {
      private static final long serialVersionUID = 5011416918049135231L;

      public AlreadyConnectedException() {
         super("Client is already connected");
      }
   }

   public static class AlreadyLoggedInException extends SmackException {
      private static final long serialVersionUID = 5011416918049935231L;

      public AlreadyLoggedInException() {
         super("Client is already logged in");
      }
   }

   public static class NotLoggedInException extends SmackException {
      private static final long serialVersionUID = 3216216839100019278L;

      public NotLoggedInException() {
         super("Client is not logged in");
      }
   }

   public static class NoResponseException extends SmackException {
      private static final long serialVersionUID = -6523363748984543636L;
      private final StanzaFilter filter;

      private NoResponseException(String message, StanzaFilter filter) {
         super(message);
         this.filter = filter;
      }

      public StanzaFilter getFilter() {
         return this.filter;
      }

      public static SmackException.NoResponseException newWith(XMPPConnection connection) {
         return newWith(connection, (StanzaFilter)null);
      }

      public static SmackException.NoResponseException newWith(XMPPConnection connection, PacketCollector collector) {
         return newWith(connection, collector.getStanzaFilter());
      }

      public static SmackException.NoResponseException newWith(XMPPConnection connection, StanzaFilter filter) {
         long replyTimeout = connection.getPacketReplyTimeout();
         StringBuilder sb = new StringBuilder(256);
         sb.append("No response received within reply timeout. Timeout was " + replyTimeout + "ms (~" + replyTimeout / 1000L + "s). Used filter: ");
         if (filter != null) {
            sb.append(filter.toString());
         } else {
            sb.append("No filter used or filter was 'null'");
         }

         sb.append('.');
         return new SmackException.NoResponseException(sb.toString(), filter);
      }
   }
}
