package com.codepoetics.octarine.records;

import com.codepoetics.octarine.morphisms.FluentCollection;
import com.codepoetics.octarine.morphisms.FluentMap;
import com.codepoetics.octarine.records.fixed.FixedRecord;
import org.pcollections.PMap;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public interface Record {

    static Record empty() { return of(); }
    static Record of(List<Value> values) {
        return of(FluentCollection.from(values).toStream());
    }

    static Record of(Value...values) {
        return of(FluentCollection.from(values).toStream());
    }

    static Record of(Stream<Value> values) {
        return of(FluentMap.<Key<?>, Object>from(values.map(Value::toPair)).toPMap());
    }

    static Record of(PMap<Key<?>, Object> values) {
        if (values == null) { throw new IllegalArgumentException("values must not be null"); }
        return new Record() {

            @Override
            @SuppressWarnings("unchecked")
            public <T> Optional<T> get(Key<T> key) {
                return Optional.<T>ofNullable((T) values.get(key));
            }
            @Override
            public PMap<Key<?>, Object> values() {
                return values;
            }


            @Override
            public String toString() {
                return toString(this);
            }

            @Override
            public int hashCode() { return hashCode(this); }

            @Override

            public boolean equals(Object other) {
                return equals(this, other);
            }
        };
    }

    <T> Optional<T> get(Key<T> key);
    PMap<Key<?>, Object> values();

    default boolean containsKey(Key<?> key) {
        return values().containsKey(key);
    }

    default Record with(Value...values) {
        return with(Record.of(values));
    }
    default Record with(PMap<Key<?>, Object> values) { return Record.of(values().plusAll(values)); }
    default Record with(Record other) { return with(other.values()); }

    default Record without(Key<?>...keys) {
        return without(FluentCollection.<Key<?>>from(keys).toSet());
    }
    default Record without(Set<Key<?>> keys) {
        return Record.of(values().minusAll(keys));
    }

    default MutableRecord mutable() { return MutableRecord.from(this); }
    default Record immutable() { return this; }
    default Record fixed() { return FixedRecord.of(values()); }

    static String toString(Record record) {
        return "{" + String.join(", ", record.values().entrySet()
                .stream()
                .map(e -> String.format("%s: %s", e.getKey().name(), e.getValue()))
                .collect(Collectors.toList())) + "}";
    }

    static int hashCode(Record record) {
        return record.values().hashCode();
    }

    static boolean equals(Record self, Object other) {
        if (other != null && other instanceof Record) {
            return ((Record) other).values().equals(self.values());
        }
        return false;
    }

}

@@ -1389,16 +1389,23 @@
  return 
+Record.
 toString
@@ -1412,16 +1412,16 @@
 (this);%0A
-
         
@@ -1492,16 +1492,23 @@
  return 
+Record.
 hashCode
@@ -1583,32 +1583,32 @@
 Object other) %7B%0A
-
                 
@@ -1614,16 +1614,23 @@
  return 
+Record.
 equals(t
@@ -2569,38 +2569,37 @@
     
-return %22%7B%22 + String.join(%22, %22,
+Stream%3CString%3E descriptions =
  rec
@@ -2731,16 +2731,17 @@
 alue()))
+;
 %0A       
@@ -2741,24 +2741,59 @@
         
-        
+return %22%7B%22 + String.join(%22, %22, descriptions
 .collect

Fix compile error