package org.javers.core.metamodel.object;

import org.javers.common.collections.Defaults;
import org.javers.common.collections.Sets;
import org.javers.common.validation.Validate;
import org.javers.core.metamodel.property.Property;

import java.util.*;

import org.javers.common.collections.Arrays;

/**
 * @author bartosz walacik
 */
public class CdoSnapshotState {
    private final Map<Property, Object> properties;

    CdoSnapshotState(Map<Property, Object> state) {
        Validate.argumentIsNotNull(state);
        this.properties = state;
    }

    int size(){
        return properties.size();
    }

    public Object getPropertyValue(Property property) {
        Validate.argumentIsNotNull(property);
        Object val = properties.get(property);
        if (val == null){
            return Defaults.defaultValue(property.getType());
        }
        return val;
    }

    boolean isNull(Property property) {
        Validate.argumentIsNotNull(property);
        return !properties.containsKey(property);
    }

    public Set<Property> getProperties() {
        return Collections.unmodifiableSet(properties.keySet());
    }

    @Override
    public boolean equals(Object o) {
        if (o == null || !(o instanceof CdoSnapshotState)) return false;

        CdoSnapshotState that = (CdoSnapshotState) o;

        if (this.properties.size() != that.properties.size()){
            return false;
        }

        for (Property p :  this.properties.keySet()) {
            if (!propertyEquals(that, p)){
                return false;
            }
        }

        return true;
    }

    private boolean propertyEquals(CdoSnapshotState that, Property property){
        Object thisValue = this.getPropertyValue(property);
        Validate.conditionFulfilled(thisValue != null, "null value at CdoCnapshot property " + property.getName());

        Object thatValue = that.getPropertyValue(property);

        if (property.getType().isArray()){
            return Arrays.equals(thisValue, thatValue);
        }

        return thisValue.equals(thatValue);
    }

    /**
     * List of properties with changed values (when comparing to the previous state)
     */
    public List<Property> differentValues(CdoSnapshotState previous) {
        List<Property> different = new ArrayList<>();

        for (Property property : properties.keySet()) {
            if (previous.isNull(property)){
                continue;
            }
            if (!propertyEquals(previous, property)){
                different.add(property);
            }
        }

        //add nullified
        different.addAll(Sets.xor(properties.keySet(), previous.properties.keySet()));

        return different;
    }

    @Override
    public int hashCode() {
        return properties.hashCode();
    }
}

@@ -36,16 +36,61 @@
 bject;%0A%0A
+import org.javers.common.collections.Arrays;%0A
 import o
@@ -295,54 +295,8 @@
 *;%0A%0A
-import org.javers.common.collections.Arrays;%0A%0A
 /**%0A
@@ -376,32 +376,30 @@
 e final Map%3C
-Property
+String
 , Object%3E pr
@@ -434,24 +434,22 @@
 ate(Map%3C
-Property
+String
 , Object
@@ -737,32 +737,42 @@
 ies.get(property
+.getName()
 );%0A        if (v
@@ -891,31 +891,239 @@
     
-boolean isNull(Property
+/**%0A     * TODO no null defaultValue%0A     */%0A    public Object getPropertyValue(String propertyName) %7B%0A        Validate.argumentIsNotNull(propertyName);%0A        return properties.get(propertyName);%0A    %7D%0A%0A%0A    boolean isNull(String
  pro
@@ -1119,32 +1119,36 @@
 (String property
+Name
 ) %7B%0A        Vali
@@ -1170,32 +1170,36 @@
 NotNull(property
+Name
 );%0A        retur
@@ -1228,24 +1228,28 @@
 Key(property
+Name
 );%0A    %7D%0A%0A  
@@ -1261,24 +1261,22 @@
 lic Set%3C
-Property
+String
 %3E getPro
@@ -1653,26 +1653,28 @@
    for (
-Property p
+String pName
  :  this
@@ -1736,16 +1736,20 @@
 (that, p
+Name
 ))%7B%0A    
@@ -1885,24 +1885,22 @@
 e that, 
-Property
+String
  propert
@@ -1900,16 +1900,20 @@
 property
+Name
 )%7B%0A     
@@ -1956,32 +1956,36 @@
 tyValue(property
+Name
 );%0A        Valid
@@ -1983,103 +1983,57 @@
     
-Validate.conditionFulfilled(this
+Object that
 Value 
-!
 = 
-null, %22null value at CdoCnapshot p
+that.getP
 roperty
- %22 + 
+Value(
 property
 .get
@@ -2028,26 +2028,20 @@
 property
-.get
 Name
-()
 );%0A%0A    
@@ -2048,89 +2048,120 @@
     
-Object thatValue = that.getPropertyValue(property);%0A%0A        if (property.getType
+if (thisValue == null %7C%7C thatValue == null)%7B%0A            return false;%0A        %7D%0A%0A        if (thisValue.getClass
 ().i
@@ -2402,32 +2402,30 @@
 public List%3C
-Property
+String
 %3E differentV
@@ -2472,24 +2472,22 @@
    List%3C
-Property
+String
 %3E differ
@@ -2525,24 +2525,22 @@
    for (
-Property
+String
  propert
@@ -2540,16 +2540,20 @@
 property
+Name
  : prope
@@ -2602,32 +2602,36 @@
 .isNull(property
+Name
 ))%7B%0A            
@@ -2708,16 +2708,20 @@
 property
+Name
 ))%7B%0A    
@@ -2746,32 +2746,36 @@
 ent.add(property
+Name
 );%0A            %7D

work in progress