package cs451.net.event;

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Objects;


/**
 * Message abstraction class
 *
 * @author C. Hölzl
 */
public class Message implements Comparable<Message> {

    /**
     * Type of Message
     */
    public enum TYPE {
        NONE("NONE", 'N'),
        ERR("ERR", 'E'),
        BCST("BCST", 'B'),
        DATA("DATA", ' '),
        ACK("ACK", 'A'),
        HRTB("HRTB",'H');

        public final Character c;
        public final String tag;
        TYPE(String tag, Character c){
            this.tag = tag;
            this.c = c;
        }

    }

    public Integer src = -1;
    public final Integer id;
    public TYPE tpe;

    /**
     * Creates a new message from a given message
     * @param m Message to copy
     * @return Message
     */
    public static Message MSG(Message m) {
        return new Message(m);
    }

    /**
     * Build a Data Message
     * @param id message id
     * @return Message
     */
    public static Message DMSG(Integer id){
        return new Message(id,TYPE.DATA);
    }
    public static Message DMSG(Integer id, Integer src) {
        return new Message(id,TYPE.DATA,src);
    }

    /**
     * Build a Heartbeat Message
     * @return Message
     */
    public static Message HRTB(){
        return new Message(0,TYPE.HRTB);
    }

    /**
     * Build a Message of a given type
     * @param mess_id Message ID
     * @param tpe Message Type
     * @return Message
     */
    public static Message TMSG(Integer mess_id, TYPE tpe) {
        return new Message(mess_id, tpe);
    }

    /**
     * @return Empty Message with no type nor ID
     */
    public static Message EMPTY(){
        return new Message(-1, TYPE.NONE);
    }


    /**
     * Converts a character to a given type
     * @param c char to convert
     * @return Type
     */
    private static TYPE CharacterToTpe(Character c){
        return Arrays.stream(TYPE.values()).filter(type -> type.c==c).findFirst().orElse(TYPE.NONE);
    }

    /**
     * Create a message from a ByteBuffer
     * @param b buffer
     * @return Message
     */
    public static Message FromBuffer(ByteBuffer b) {
        Character tpe = b.getChar();
        Integer id = b.getInt();
        Integer src = b.getInt();
        return new Message(id,CharacterToTpe(tpe),src);
    }

    /**
     * Puts a message into a ByteBuffer
     * @param b buffer
     */
    public void ToBuffer(ByteBuffer b){
        b.putChar(tpe.c);
        b.putInt(id);
        b.putInt(src);
    }

    private Message(Message m) {
        this.id = m.id;
        this.tpe = m.tpe;
        this.src = m.src;
    }
    private Message(Integer id, TYPE tpe){
        this.id = id;
        this.tpe = tpe;
    }
    private Message(Integer id, TYPE tpe, Integer src){
        this.id = id;
        this.tpe = tpe;
        this.src = src;
    }

    @Override
    public String toString() {
        if (tpe == TYPE.DATA) {
            return src + "-" + id;
        }
        return src + "-" + tpe.c + id;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Message m = (Message) obj;
        return id.equals(m.id) && src.equals(m.src);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id,src);
    }

    @Override
    public int compareTo(Message m) {
        return id.compareTo(m.id);
    }
}
