package org.jivesoftware.smack.packet;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jxmpp.jid.Jid;

public final class Message extends Stanza implements Cloneable {
   public static final String ELEMENT = "message";
   public static final String BODY = "body";
   private Message.Type type;
   private String thread;
   private final Set<Message.Subject> subjects;
   private final Set<Message.Body> bodies;

   public Message() {
      this.thread = null;
      this.subjects = new HashSet();
      this.bodies = new HashSet();
   }

   public Message(Jid to) {
      this.thread = null;
      this.subjects = new HashSet();
      this.bodies = new HashSet();
      this.setTo(to);
   }

   public Message(Jid to, Message.Type type) {
      this(to);
      this.setType(type);
   }

   public Message(Jid to, String body) {
      this(to);
      this.setBody(body);
   }

   public Message(Message other) {
      super((Stanza)other);
      this.thread = null;
      this.subjects = new HashSet();
      this.bodies = new HashSet();
      this.type = other.type;
      this.thread = other.thread;
      this.subjects.addAll(other.subjects);
      this.bodies.addAll(other.bodies);
   }

   public Message.Type getType() {
      return this.type == null ? Message.Type.normal : this.type;
   }

   public void setType(Message.Type type) {
      this.type = type;
   }

   public String getSubject() {
      return this.getSubject((String)null);
   }

   public String getSubject(String language) {
      Message.Subject subject = this.getMessageSubject(language);
      return subject == null ? null : subject.subject;
   }

   private Message.Subject getMessageSubject(String language) {
      language = this.determineLanguage(language);
      Iterator i$ = this.subjects.iterator();

      Message.Subject subject;
      do {
         if (!i$.hasNext()) {
            return null;
         }

         subject = (Message.Subject)i$.next();
      } while(!language.equals(subject.language));

      return subject;
   }

   public Set<Message.Subject> getSubjects() {
      return Collections.unmodifiableSet(this.subjects);
   }

   public void setSubject(String subject) {
      if (subject == null) {
         this.removeSubject("");
      } else {
         this.addSubject((String)null, subject);
      }
   }

   public Message.Subject addSubject(String language, String subject) {
      language = this.determineLanguage(language);
      Message.Subject messageSubject = new Message.Subject(language, subject);
      this.subjects.add(messageSubject);
      return messageSubject;
   }

   public boolean removeSubject(String language) {
      language = this.determineLanguage(language);
      Iterator i$ = this.subjects.iterator();

      Message.Subject subject;
      do {
         if (!i$.hasNext()) {
            return false;
         }

         subject = (Message.Subject)i$.next();
      } while(!language.equals(subject.language));

      return this.subjects.remove(subject);
   }

   public boolean removeSubject(Message.Subject subject) {
      return this.subjects.remove(subject);
   }

   public List<String> getSubjectLanguages() {
      Message.Subject defaultSubject = this.getMessageSubject((String)null);
      List<String> languages = new ArrayList();
      Iterator i$ = this.subjects.iterator();

      while(i$.hasNext()) {
         Message.Subject subject = (Message.Subject)i$.next();
         if (!subject.equals(defaultSubject)) {
            languages.add(subject.language);
         }
      }

      return Collections.unmodifiableList(languages);
   }

   public String getBody() {
      return this.getBody((String)null);
   }

   public String getBody(String language) {
      Message.Body body = this.getMessageBody(language);
      return body == null ? null : body.message;
   }

   private Message.Body getMessageBody(String language) {
      language = this.determineLanguage(language);
      Iterator i$ = this.bodies.iterator();

      Message.Body body;
      do {
         if (!i$.hasNext()) {
            return null;
         }

         body = (Message.Body)i$.next();
      } while(!language.equals(body.language));

      return body;
   }

   public Set<Message.Body> getBodies() {
      return Collections.unmodifiableSet(this.bodies);
   }

   public void setBody(String body) {
      if (body == null) {
         this.removeBody("");
      } else {
         this.addBody((String)null, body);
      }
   }

   public Message.Body addBody(String language, String body) {
      language = this.determineLanguage(language);
      Message.Body messageBody = new Message.Body(language, body);
      this.bodies.add(messageBody);
      return messageBody;
   }

   public boolean removeBody(String language) {
      language = this.determineLanguage(language);
      Iterator i$ = this.bodies.iterator();

      Message.Body body;
      do {
         if (!i$.hasNext()) {
            return false;
         }

         body = (Message.Body)i$.next();
      } while(!language.equals(body.language));

      return this.bodies.remove(body);
   }

   public boolean removeBody(Message.Body body) {
      return this.bodies.remove(body);
   }

   public List<String> getBodyLanguages() {
      Message.Body defaultBody = this.getMessageBody((String)null);
      List<String> languages = new ArrayList();
      Iterator i$ = this.bodies.iterator();

      while(i$.hasNext()) {
         Message.Body body = (Message.Body)i$.next();
         if (!body.equals(defaultBody)) {
            languages.add(body.language);
         }
      }

      return Collections.unmodifiableList(languages);
   }

   public String getThread() {
      return this.thread;
   }

   public void setThread(String thread) {
      this.thread = thread;
   }

   private String determineLanguage(String language) {
      language = "".equals(language) ? null : language;
      if (language == null && this.language != null) {
         return this.language;
      } else {
         return language == null ? getDefaultLanguage() : language;
      }
   }

   public XmlStringBuilder toXML() {
      XmlStringBuilder buf = new XmlStringBuilder();
      buf.halfOpenElement("message");
      this.addCommonAttributes(buf);
      buf.optAttribute("type", (Enum)this.type);
      buf.rightAngleBracket();
      Message.Subject defaultSubject = this.getMessageSubject((String)null);
      if (defaultSubject != null) {
         buf.element("subject", defaultSubject.subject);
      }

      Iterator i$ = this.getSubjects().iterator();

      while(i$.hasNext()) {
         Message.Subject subject = (Message.Subject)i$.next();
         if (!subject.equals(defaultSubject)) {
            buf.halfOpenElement("subject").xmllangAttribute(subject.language).rightAngleBracket();
            buf.escape(subject.subject);
            buf.closeElement("subject");
         }
      }

      Message.Body defaultBody = this.getMessageBody((String)null);
      if (defaultBody != null) {
         buf.element("body", defaultBody.message);
      }

      Iterator i$ = this.getBodies().iterator();

      while(i$.hasNext()) {
         Message.Body body = (Message.Body)i$.next();
         if (!body.equals(defaultBody)) {
            buf.halfOpenElement("body").xmllangAttribute(body.getLanguage()).rightAngleBracket();
            buf.escape(body.getMessage());
            buf.closeElement("body");
         }
      }

      buf.optElement("thread", this.thread);
      if (this.type == Message.Type.error) {
         this.appendErrorIfExists(buf);
      }

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

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

   public static enum Type {
      normal,
      chat,
      groupchat,
      headline,
      error;

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

   public static class Body {
      private final String message;
      private final String language;

      private Body(String language, String message) {
         if (language == null) {
            throw new NullPointerException("Language cannot be null.");
         } else if (message == null) {
            throw new NullPointerException("Message cannot be null.");
         } else {
            this.language = language;
            this.message = message;
         }
      }

      public String getLanguage() {
         return this.language;
      }

      public String getMessage() {
         return this.message;
      }

      public int hashCode() {
         int prime = true;
         int result = 1;
         int result = 31 * result + this.language.hashCode();
         result = 31 * result + this.message.hashCode();
         return result;
      }

      public boolean equals(Object obj) {
         if (this == obj) {
            return true;
         } else if (obj == null) {
            return false;
         } else if (this.getClass() != obj.getClass()) {
            return false;
         } else {
            Message.Body other = (Message.Body)obj;
            return this.language.equals(other.language) && this.message.equals(other.message);
         }
      }

      // $FF: synthetic method
      Body(String x0, String x1, Object x2) {
         this(x0, x1);
      }
   }

   public static class Subject {
      private final String subject;
      private final String language;

      private Subject(String language, String subject) {
         if (language == null) {
            throw new NullPointerException("Language cannot be null.");
         } else if (subject == null) {
            throw new NullPointerException("Subject cannot be null.");
         } else {
            this.language = language;
            this.subject = subject;
         }
      }

      public String getLanguage() {
         return this.language;
      }

      public String getSubject() {
         return this.subject;
      }

      public int hashCode() {
         int prime = true;
         int result = 1;
         int result = 31 * result + this.language.hashCode();
         result = 31 * result + this.subject.hashCode();
         return result;
      }

      public boolean equals(Object obj) {
         if (this == obj) {
            return true;
         } else if (obj == null) {
            return false;
         } else if (this.getClass() != obj.getClass()) {
            return false;
         } else {
            Message.Subject other = (Message.Subject)obj;
            return this.language.equals(other.language) && this.subject.equals(other.subject);
         }
      }

      // $FF: synthetic method
      Subject(String x0, String x1, Object x2) {
         this(x0, x1);
      }
   }
}
