package io.ipfs.api;

import io.ipfs.cid.*;
import io.ipfs.multihash.Multihash;

import java.util.*;
import java.util.stream.*;

public class MerkleNode {

    public final Multihash hash;
    public final Optional<String> name;
    public final Optional<Integer> size;
    public final Optional<String> largeSize;
    public final Optional<Integer> type;
    public final List<MerkleNode> links;
    public final Optional<byte[]> data;

    public MerkleNode(String hash,
                      Optional<String> name,
                      Optional<Integer> size,
                      Optional<String> largeSize,
                      Optional<Integer> type,
                      List<MerkleNode> links,
                      Optional<byte[]> data) {
        this.name = name;
        this.hash = Cid.decode(hash);
        this.size = size;
        this.largeSize = largeSize;
        this.type = type;
        this.links = links;
        this.data = data;
    }

    public MerkleNode(String hash) {
        this(hash, Optional.empty());
    }

    public MerkleNode(String hash, Optional<String> name) {
        this(hash, name, Optional.empty(), Optional.empty(), Optional.empty(), Arrays.asList(), Optional.empty());
    }

    @Override
    public boolean equals(Object b) {
        if (!(b instanceof MerkleNode))
            return false;
        MerkleNode other = (MerkleNode) b;
        return hash.equals(other.hash); // ignore name hash says it all
    }

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

    public static MerkleNode fromJSON(Object rawjson) {
        if (rawjson instanceof String)
            return new MerkleNode((String)rawjson);
        Map json = (Map)rawjson;
        String hash = (String)json.get("Hash");
        if (hash == null)
            hash = (String)json.get("Key");
        Optional<String> name = json.containsKey("Name") ?
                Optional.of((String) json.get("Name")) :
                Optional.empty();
        Object rawSize = json.get("Size");
        Optional<Integer> size = rawSize instanceof Integer ?
                Optional.of((Integer) rawSize) :
                Optional.empty();
        Optional<String> largeSize = rawSize instanceof String ?
                Optional.of((String) json.get("Size")) :
                Optional.empty();
        if ("error".equals(json.get("Type")))
            throw new IllegalStateException("Remote IPFS error: " + json.get("Message"));
        Optional<Integer> type = json.containsKey("Type") ?
                Optional.of((Integer) json.get("Type")) :
                Optional.empty();
        List<Object> linksRaw = (List<Object>) json.get("Links");
        List<MerkleNode> links = linksRaw == null ?
                Collections.emptyList() :
                linksRaw.stream().map(x -> MerkleNode.fromJSON(x)).collect(Collectors.toList());
        Optional<byte[]> data = json.containsKey("Data") ? Optional.of(((String)json.get("Data")).getBytes()): Optional.empty();
        return new MerkleNode(hash, name, size, largeSize, type, links, data);
    }

    public Object toJSON() {
        Map res = new TreeMap<>();
        res.put("Hash", hash);
        res.put("Links", links.stream().map(x -> x.hash).collect(Collectors.toList()));
        if (data.isPresent())
            res.put("Data", data.get());
        if (name.isPresent())
            res.put("Name", name.get());
        if (size.isPresent())
            res.put("Size", size.isPresent() ? size.get() : largeSize.get());
        if (type.isPresent())
            res.put("Type", type.get());
        return res;
    }

    public String toJSONString() {
        return JSONParser.toString(toJSON());
    }
}

@@ -3136,16 +3136,32 @@
      Map
+%3CString, Object%3E
  res = n
@@ -3172,24 +3172,24 @@
 reeMap%3C%3E();%0A
-
         res.
@@ -3307,27 +3307,23 @@
     
-if (
 data.i
-s
+f
 Present(
 ))%0A 
@@ -3314,38 +3314,32 @@
 a.ifPresent(
-))%0A           
+bytes -%3E
  res.put(%22Da
@@ -3347,17 +3347,13 @@
 a%22, 
-data.get(
+bytes
 ));%0A
@@ -3364,27 +3364,23 @@
     
-if (
 name.i
-s
+f
 Present(
 ))%0A 
@@ -3371,38 +3371,28 @@
 e.ifPresent(
-))%0A           
+s -%3E
  res.put(%22Na
@@ -3400,17 +3400,9 @@
 e%22, 
-name.get(
+s
 ));%0A
@@ -3422,32 +3422,34 @@
 ize.isPresent())
+ %7B
 %0A            res
@@ -3470,49 +3470,87 @@
 ize.
-isPresent() ? size.get() : largeSize.get(
+get());%0A        %7D else %7B%0A            largeSize.ifPresent(s -%3E res.put(%22Size%22, s
 ));%0A
@@ -3561,27 +3561,33 @@
     
-if (
+%7D%0A        
 type.i
-s
+f
 Present(
 ))%0A 
@@ -3582,30 +3582,26 @@
 Present(
-))%0A           
+integer -%3E
  res.put
@@ -3613,17 +3613,15 @@
 e%22, 
-type.get(
+integer
 ));%0A

Fix for size information at merkle node.