package com.kryptnostic.storage.v2.models;

import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Optional;
import com.google.common.collect.Maps;
import com.kryptnostic.kodex.v1.constants.Names;
import com.kryptnostic.kodex.v1.crypto.ciphers.BlockCiphertext;

/**
 * This class is a return only JSON data type that used to return nest object tree with objects at various load levels
 * from the server.
 *
 * @author Matthew Tamayo-Rios &lt;matthew@kryptnostic.com&gt;
 *
 */
public class ObjectMetadataEncryptedNode {
    private final Map<UUID, ObjectMetadataEncryptedNode> children;
    private final ObjectMetadata                         metadata;
    private final Optional<BlockCiphertext>              ciphertext;

    public ObjectMetadataEncryptedNode( ObjectMetadata metadata ) {
        this( metadata, Optional.<BlockCiphertext> absent(), Maps.<UUID, ObjectMetadataEncryptedNode> newHashMap() );
    }

    @JsonCreator
    public ObjectMetadataEncryptedNode(
            @JsonProperty( Names.METADATA_FIELD ) ObjectMetadata metadata,
            @JsonProperty( Names.CONTENTS ) Optional<BlockCiphertext> ciphertext,
            @JsonProperty( Names.CHILDREN_FIELD ) Map<UUID, ObjectMetadataEncryptedNode> children ) {
        this.children = Maps.newHashMap();
        this.metadata = metadata;
        this.ciphertext = ciphertext;
    }

    public Map<UUID, ObjectMetadataEncryptedNode> getChildren() {
        return children;
    }

    public void addChild( UUID childId, ObjectMetadataEncryptedNode childNode ) {
        children.put( childId, childNode );
    }

    public void addChildren( Map<UUID, ObjectMetadataEncryptedNode> kids ) {
        children.putAll( kids );
    }

    public ObjectMetadata getMetadata() {
        return metadata;
    }

    public Optional<BlockCiphertext> getData() {
        return ciphertext;
    }

    @Override
    public String toString() {
        return "ObjectMetadataNode [children=" + children + ", metadata=" + metadata + ", ciphertext=" + ciphertext
                + "]";
    }

    public static String printMap( Map<UUID, ObjectMetadataEncryptedNode> map ) {
        StringBuilder build = new StringBuilder();
        if ( map.isEmpty() ) {
            return "\"empty\",\n";
        }
        build.append( "{\n" );
        for ( Entry<UUID, ObjectMetadataEncryptedNode> ent : map.entrySet() ) {
            ObjectMetadataEncryptedNode value = ent.getValue();
            build.append( ent.getKey() );
            build.append( ": " );
            if ( value.getChildren() != null ) {
                build.append( printMap( value.getChildren() ) );
            }
        }
        build.append( "}\n" );
        return build.toString();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ( ( children == null ) ? 0 : children.hashCode() );
        result = prime * result + ( ( ciphertext == null ) ? 0 : ciphertext.hashCode() );
        result = prime * result + ( ( metadata == null ) ? 0 : metadata.hashCode() );
        return result;
    }

    @Override
    public boolean equals( Object obj ) {
        if ( this == obj ) {
            return true;
        }
        if ( obj == null ) {
            return false;
        }
        if ( !( obj instanceof ObjectMetadataEncryptedNode ) ) {
            return false;
        }
        ObjectMetadataEncryptedNode other = (ObjectMetadataEncryptedNode) obj;
        if ( children == null ) {
            if ( other.children != null ) {
                return false;
            }
        } else if ( !children.equals( other.children ) ) {
            return false;
        }
        if ( ciphertext == null ) {
            if ( other.ciphertext != null ) {
                return false;
            }
        } else if ( !ciphertext.equals( other.ciphertext ) ) {
            return false;
        }
        if ( metadata == null ) {
            if ( other.metadata != null ) {
                return false;
            }
        } else if ( !metadata.equals( other.metadata ) ) {
            return false;
        }
        return true;
    }

}

@@ -1378,16 +1378,234 @@
 children
+) %7B%0A        this.children = children;%0A        this.metadata = metadata;%0A        this.ciphertext = ciphertext;%0A    %7D%0A%0A    public ObjectMetadataEncryptedNode( ObjectMetadata metadata, Optional%3CBlockCiphertext%3E ciphertext
  ) %7B%0A   
@@ -2497,24 +2497,173 @@
 de%3E map ) %7B%0A
+        return %22%5Cn%22 + printMap( map, %22%5Ct%22 );%0A    %7D%0A%0A    public static String printMap( Map%3CUUID, ObjectMetadataEncryptedNode%3E map, String prefix ) %7B%0A
         Stri
@@ -2756,18 +2756,11 @@
 rn %22
-%5C%22empty%5C%22,
+%5B/%5D
 %5Cn%22;
@@ -2941,24 +2941,60 @@
 getValue();%0A
+            build.append( prefix );%0A
             
@@ -3165,16 +3165,31 @@
 ildren()
+, prefix + %22%5Ct%22
  ) );%0A  
@@ -3231,16 +3231,25 @@
 .append(
+ prefix +
  %22%7D%5Cn%22 )

add another constructor and fix an existing one