package eu.modernmt.model;

import java.io.Serializable;
import java.util.*;

/**
 * Created by davide on 18/01/17.
 */
public class ContextVector implements Iterable<ContextVector.Entry>, Serializable {

    public static class Builder {

        private final HashMap<Memory, Float> entries;
        private int limit = 0;

        public Builder(int initialCapacity) {
            entries = new HashMap<>(initialCapacity);
        }

        public Builder() {
            entries = new HashMap<>();
        }

        public Builder setLimit(int limit) {
            this.limit = limit;
            return this;
        }

        public Builder add(long memory, float score) {
            this.add(new Memory(memory), score);
            return this;
        }

        public Builder add(Memory memory, float score) {
            if (score > 0.f)
                entries.put(memory, score);
            return this;
        }

        public ContextVector build() {
            List<Entry> list = new ArrayList<>(this.entries.size());
            for (Map.Entry<Memory, Float> e : this.entries.entrySet())
                list.add(new Entry(e.getKey(), e.getValue()));

            Collections.sort(list);
            Collections.reverse(list);

            if (limit > 0 && list.size() > limit)
                list = list.subList(0, limit);

            return new ContextVector(list.toArray(new Entry[0]));
        }
    }

    public static ContextVector fromString(String string) throws IllegalArgumentException {
        String[] elements = string.split(",");

        ContextVector.Builder builder = new ContextVector.Builder(elements.length);

        for (String element : elements) {
            String[] kv = element.split(":");

            if (kv.length != 2)
                throw new IllegalArgumentException(string);

            long memory;
            float score;

            try {
                memory = Long.parseLong(kv[0]);
                score = Float.parseFloat(kv[1]);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException(string);
            }

            if (memory < 1)
                throw new IllegalArgumentException(string);

            if (score < 0.f || score > 1.f)
                throw new IllegalArgumentException(string);

            builder.add(memory, score);
        }

        return builder.build();
    }

    public static class Entry implements Comparable<Entry>, Serializable {

        public final Memory memory;
        public final float score;

        private Entry(Memory memory, float score) {
            this.memory = memory;
            this.score = score;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Entry entry = (Entry) o;

            if (Float.compare(entry.score, score) != 0) return false;
            return memory.equals(entry.memory);
        }

        @Override
        public int hashCode() {
            int result = memory.hashCode();
            result = 31 * result + (score != +0.0f ? Float.floatToIntBits(score) : 0);
            return result;
        }


        @Override
        public int compareTo(Entry o) {
            return Float.compare(score, o.score);
        }
    }

    private final Entry[] entries;

    private ContextVector(Entry[] entries) {
        this.entries = entries;
    }

    public int size() {
        return entries.length;
    }

    public boolean isEmpty() {
        return entries.length == 0;
    }

    @Override
    public Iterator<Entry> iterator() {
        return new Iterator<Entry>() {

            private int i = 0;

            @Override
            public boolean hasNext() {
                return i < entries.length;
            }

            @Override
            public Entry next() {
                return entries[i++];
            }
        };
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < entries.length; i++) {
            if (i > 0)
                builder.append(',');
            builder.append(entries[i].memory.getId());
            builder.append(':');
            builder.append(entries[i].score);
        }

        return builder.toString();
    }
}

@@ -1850,22 +1850,42 @@
    long 
-memory
+id;%0A            UUID owner
 ;%0A      
@@ -1938,22 +1938,562 @@
         
-memory
+int idx = kv%5B0%5D.indexOf('@');%0A                if (idx %3C 0) %7B%0A                    id = Long.parseLong(kv%5B0%5D);%0A                    owner = null;%0A                %7D else %7B%0A                    String ownerString = kv%5B0%5D.substring(0, idx);%0A                    String idString = kv%5B0%5D.substring(idx + 1);%0A%0A                    id = Long.parseLong(idString);%0A%0A                    String%5B%5D uc = ownerString.split(%22/%22);%0A                    if (uc.length != 2)%0A                        throw new IllegalArgumentException(string);%0A%0A                    long msb
  = Long.
@@ -2498,32 +2498,153 @@
 g.parseLong(
-kv
+uc
 %5B0%5D);%0A
+                    long lsb = Long.parseLong(uc%5B1%5D);%0A                    owner = new UUID(msb, lsb);%0A                %7D%0A%0A
             
@@ -2819,22 +2819,18 @@
     if (
-memory
+id
  %3C 1)%0A  
@@ -3017,22 +3017,43 @@
 der.add(
-memory
+new Memory(id, owner, null)
 , score)

Support new ContextVector string format