/*
 * Copyright (C) 2008-2017, Juick
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.juick;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.juick.adapters.SimpleDateAdapter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * @author Ugnich Anton
 */
@XmlRootElement(name = "juick", namespace = "http://juick.com/message")
@XmlAccessorType()
public class Message implements Comparable {
    private int mid = 0;
    private int rid = 0;
    private int replyto = 0;
    private String text = null;
    private User user = null;
    private final List<Tag> tags;
    private Instant ts;
    private Instant updated;
    private Instant updatedAt;
    private boolean unread;
    @JsonIgnore
    private int privacy = 1;
    @XmlTransient
    @JsonIgnore
    public boolean FriendsOnly = false;
    @XmlTransient
    @JsonIgnore
    public boolean ReadOnly = false;
    @XmlTransient
    @JsonIgnore
    public boolean Hidden = false;
    @JsonIgnore
    @XmlTransient
    public boolean VisitorCanComment = true;
    private int replies = 0;
    private String repliesBy;
    private String attachmentType;
    @XmlTransient
    private Photo photo;
    @XmlTransient
    private Attachment attachment;
    private int likes;
    private User to;
    private String replyQuote;
    @XmlTransient
    private Set<Reaction> reactions;
    private boolean service;

    private Set<String> recommendations;

    public Message() {
        tags = new ArrayList<>();
        reactions = new HashSet<>();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("mid", mid)
                .append("rid", rid)
                .append("replyto", replyto)
                .append("privacy", privacy)
                .append("FriendsOnly", FriendsOnly)
                .append("ReadOnly", ReadOnly)
                .append("Hidden", Hidden)
                .append("VisitorCanComment", VisitorCanComment)
                .append("replies", replies)
                .append("likes", likes)
                .append("reactions", reactions)
                .toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;

        if (!(obj instanceof Message))
            return false;

        Message jmsg = (Message) obj;
        return (this.getMid() == jmsg.getMid() && this.getRid() == jmsg.getRid());
    }

    @Override
    public int compareTo(Object obj) throws ClassCastException {
        if (obj == this)
            return 0;

        if (!(obj instanceof Message))
            throw new ClassCastException();

        Message jmsg = (Message) obj;

        int cmp = Integer.compare(jmsg.getMid(), getMid());

        if (cmp == 0)
            cmp = Integer.compare(getRid(), jmsg.getRid());

        return cmp;
    }

    @JsonProperty("mid")
    @XmlAttribute(name = "mid")
    public int getMid() {
        return mid;
    }

    public void setMid(int mid) {
        this.mid = mid;
    }

    @JsonProperty("rid")
    @XmlAttribute(name = "rid")
    public int getRid() {
        return rid;
    }

    public void setRid(int rid) {
        this.rid = rid;
    }

    @XmlElement(name = "user", namespace = "http://juick.com/user")
    public com.juick.User getUser() {
        return user;
    }

    public void setUser(com.juick.User user) {
        this.user = user;
    }

    @JsonProperty("body")
    @XmlElement(name = "body")
    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    @JsonProperty("timestamp")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC")
    @XmlAttribute(name = "ts")
    @XmlJavaTypeAdapter(SimpleDateAdapter.class)
    public Instant getTimestamp() {
        return ts;
    }

    public void setTimestamp(Instant timestamp) {
        this.ts = timestamp;
    }

    @XmlElement(name = "to", namespace = "http://juick.com/user")
    public User getTo() {
        return to;
    }

    public void setTo(User to) {
        this.to = to;
    }

    @XmlAttribute(name = "quote")
    public String getReplyQuote() {
        return replyQuote;
    }

    public void setReplyQuote(String quote) {
        replyQuote = quote;
    }

    @JsonProperty("replyto")
    @XmlAttribute(name = "replyto")
    public int getReplyto() {
        return replyto;
    }

    public void setReplyto(int replyto) {
        this.replyto = replyto;
    }

    @JsonProperty("tags")
    @XmlElement(name = "tag")
    public List<Tag> getTags() {
        return tags;
    }

    public void setTags(List<Tag> tags) {
        this.tags.clear();
        if (CollectionUtils.isNotEmpty(tags))
            this.tags.addAll(tags);
    }

    @XmlAttribute
    public int getPrivacy() {
        return privacy;
    }

    public void setPrivacy(int privacy) {
        this.privacy = privacy;
    }

    public Photo getPhoto() {
        return photo;
    }

    public void setPhoto(Photo photo) {
        this.photo = photo;
    }

    @XmlAttribute(name = "attach")
    @JsonProperty("attach")
    public String getAttachmentType() {
        return attachmentType;
    }

    public void setAttachmentType(String attachmentType) {
        this.attachmentType = attachmentType;
    }

    @XmlTransient
    public int getReplies() {
        return replies;
    }

    public void setReplies(int replies) {
        this.replies = replies;
    }

    @XmlTransient
    public int getLikes() {
        return likes;
    }

    public void setLikes(int likes) {
        this.likes = likes;
    }

    @JsonProperty("repliesby")
    public String getRepliesBy() {
        return repliesBy;
    }

    public void setRepliesBy(String repliesBy) {
        this.repliesBy = repliesBy;
    }

    public Attachment getAttachment() {
        return attachment;
    }
    public void setAttachment(Attachment attachment) {
        this.attachment = attachment;
    }

    /**
     * @return timestamp of the last comment
     */
    @XmlTransient
    public Instant getUpdated() {
        return updated;
    }

    public void setUpdated(Instant updated) {
        this.updated = updated;
    }

    @XmlTransient
    public boolean isUnread() {
        return unread;
    }

    public void setUnread(boolean unread) {
        this.unread = unread;
    }


    @XmlTransient
    public Set<Reaction> getReactions() {
        return reactions;
    }

    public void setReactions(Set<Reaction> reactions) {
        this.reactions = reactions;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mid, rid);
    }

    public boolean isService() {
        return service;
    }

    public void setService(boolean service) {
        this.service = service;
    }

    public Set<String> getRecommendations() {
        return recommendations;
    }

    public void setRecommendations(Set<String> recommendations) {
        this.recommendations = recommendations;
    }

    /**
     * @return timestamp of the last edit
     */
    @XmlTransient
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC")
    @JsonProperty("updated_at")
    public Instant getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Instant updatedAt) {
        this.updatedAt = updatedAt;
    }
}