package org.jivesoftware.smack.packet;

import java.util.Locale;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.XmlStringBuilder;

public final class Presence extends Stanza implements Cloneable {
   public static final String ELEMENT = "presence";
   private Presence.Type type;
   private String status;
   private int priority;
   private Presence.Mode mode;

   public Presence(Presence.Type type) {
      this.type = Presence.Type.available;
      this.status = null;
      this.priority = Integer.MIN_VALUE;
      this.mode = null;
      this.setType(type);
   }

   public Presence(Presence.Type type, String status, int priority, Presence.Mode mode) {
      this.type = Presence.Type.available;
      this.status = null;
      this.priority = Integer.MIN_VALUE;
      this.mode = null;
      this.setType(type);
      this.setStatus(status);
      this.setPriority(priority);
      this.setMode(mode);
   }

   public Presence(Presence other) {
      super((Stanza)other);
      this.type = Presence.Type.available;
      this.status = null;
      this.priority = Integer.MIN_VALUE;
      this.mode = null;
      this.type = other.type;
      this.status = other.status;
      this.priority = other.priority;
      this.mode = other.mode;
   }

   public boolean isAvailable() {
      return this.type == Presence.Type.available;
   }

   public boolean isAway() {
      return this.type == Presence.Type.available && (this.mode == Presence.Mode.away || this.mode == Presence.Mode.xa || this.mode == Presence.Mode.dnd);
   }

   public Presence.Type getType() {
      return this.type;
   }

   public void setType(Presence.Type type) {
      this.type = (Presence.Type)Objects.requireNonNull(type, "Type cannot be null");
   }

   public String getStatus() {
      return this.status;
   }

   public void setStatus(String status) {
      this.status = status;
   }

   public int getPriority() {
      return this.priority;
   }

   public void setPriority(int priority) {
      if (priority >= -128 && priority <= 128) {
         this.priority = priority;
      } else {
         throw new IllegalArgumentException("Priority value " + priority + " is not valid. Valid range is -128 through 128.");
      }
   }

   public Presence.Mode getMode() {
      return this.mode == null ? Presence.Mode.available : this.mode;
   }

   public void setMode(Presence.Mode mode) {
      this.mode = mode;
   }

   public XmlStringBuilder toXML() {
      XmlStringBuilder buf = new XmlStringBuilder();
      buf.halfOpenElement("presence");
      this.addCommonAttributes(buf);
      if (this.type != Presence.Type.available) {
         buf.attribute("type", (Enum)this.type);
      }

      buf.rightAngleBracket();
      buf.optElement("status", this.status);
      if (this.priority != Integer.MIN_VALUE) {
         buf.element("priority", Integer.toString(this.priority));
      }

      if (this.mode != null && this.mode != Presence.Mode.available) {
         buf.element("show", (Enum)this.mode);
      }

      buf.append(this.getExtensionsXML());
      this.appendErrorIfExists(buf);
      buf.closeElement("presence");
      return buf;
   }

   public Presence clone() {
      return new Presence(this);
   }

   public static enum Mode {
      chat,
      available,
      away,
      xa,
      dnd;

      public static Presence.Mode fromString(String string) {
         return valueOf(string.toLowerCase(Locale.US));
      }
   }

   public static enum Type {
      available,
      unavailable,
      subscribe,
      subscribed,
      unsubscribe,
      unsubscribed,
      error,
      probe;

      public static Presence.Type fromString(String string) {
         return valueOf(string.toLowerCase(Locale.US));
      }
   }
}
