package server;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import common.CommonUtils;

import server.token.TokenString;

public class Emote implements Externalizable {

    public enum TargetType { ALLOWED, DISALLOWED, REQUIRED }
    
    private static final long serialVersionUID = 1L;
    private static Map<String,Emote> emoteMap = new HashMap<String,Emote>();
    private static Map<String,Emote> sortedEmoteMap = new TreeMap<String,Emote>();
    
    private String audio;
    private String visual;
    private String both;
    private TargetType targetType = TargetType.ALLOWED;
    private Map<String,String> modifiers = new HashMap<String,String>();
    
    private transient TokenString audioTS;
    private transient TokenString visualTS;
    private transient TokenString bothTS;
    
    private transient boolean audioCompiled;
    private transient boolean visualCompiled;
    private transient boolean bothCompiled;

    public static Emote add(String name) {
        if (emoteMap.containsKey(name)) {
            return null;
        }
        Emote spec = new Emote();
        emoteMap.put(name, spec);
        sortedEmoteMap.put(name, spec);
        return spec;
    }
    
    public static Emote get(String name) {
        return emoteMap.get(name);
    }
    
    public static Emote remove(String name) {
        sortedEmoteMap.remove(name);
        return emoteMap.remove(name);
    }
    
    public static Set<String> list() {
        return sortedEmoteMap.keySet();
    }

    public TokenString getBothTS() {
        if (!bothCompiled) {
            bothTS = both == null ? null : new TokenString("@00E" + both);
            bothCompiled = true;
        }
        return bothTS;
    }
    
    public TokenString getAudioTS() {
        if (!audioCompiled) {
            audioTS = audio == null ? null : new TokenString("@00E" + audio);
            audioCompiled = true;
        }
        return audioTS;
    }
    
    public TokenString getVisualTS() {
        if (!visualCompiled) {
            visualTS = visual == null ? null : new TokenString("@00E" + visual);
            visualCompiled = true;
        }
        return visualTS;
    }
    
    public String getAudio() {
        return audio;
    }
    
    public String getVisual() {
        return visual;
    }
    
    public String getBoth() {
        return both;
    }

    public void setAudio(String audio) {
        this.audio = audio;
        this.audioTS = null;
        this.audioCompiled = false;
    }
    
    public void setVisual(String visual) {
        this.visual = visual;
        this.visualTS = null;
        this.visualCompiled = false;
    }
    
    public void setBoth(String both) {
        this.both = both;
        this.bothTS = null;
        this.bothCompiled = false;
    }
    
    public void setTargetType(TargetType targetType) {
        if (targetType == null) {
            throw new NullPointerException();
        }
        this.targetType = targetType;
    }
    
    public TargetType getTargetType() {
        return targetType;
    }
    
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt(1); // protocol version
        out.writeObject(audio);
        out.writeObject(visual);
        out.writeObject(both);
        out.writeObject(targetType);
        out.writeObject(modifiers);
    }

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        in.readInt();
        audio = (String) in.readObject();
        visual = (String) in.readObject();
        both = (String) in.readObject();
        targetType = (TargetType) in.readObject();
        modifiers = CommonUtils.uncheckedCast(in.readObject());
    }
    
    public Set<String> getModifiers() {
        return modifiers.keySet();
    }

    public String getModifier(String token) {
        return modifiers.get(token);
    }

    public void addModifier(String modifierCommand, String modifier) {
        modifiers.put(modifierCommand, modifier);
    }
    
    public boolean removeModifier(String token) {
        return modifiers.remove(token) != null;
    }

    public static void doSave(ObjectOutputStream oos) throws IOException {
        oos.writeInt(1); // protocol version
        oos.writeObject(sortedEmoteMap);
    }
    
    public static void doLoad(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.readInt(); // protocol version
        sortedEmoteMap = CommonUtils.uncheckedCast(ois.readObject());
        emoteMap.putAll(sortedEmoteMap);
    }

}