package com.github.nsnjson.decoding;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.*;

import java.util.Optional;

import static com.github.nsnjson.format.Format.*;

public class Decoder {

    public static Optional<JsonNode> decode(ObjectNode presentation) {
        switch (type(presentation)) {
            case TYPE_MARKER_NULL:
                return decodeNull();
            case TYPE_MARKER_NUMBER:
                return decodeNumber(presentation);
            case TYPE_MARKER_STRING:
                return decodeString(presentation);
            case TYPE_MARKER_BOOLEAN:
                return decodeBoolean(presentation);
            case TYPE_MARKER_ARRAY:
                return decodeArray(presentation);
            case TYPE_MARKER_OBJECT:
                return decodeObject(presentation);
        }

        return Optional.empty();
    }

    private static int type(ObjectNode presentation) {
        return presentation.get(FIELD_TYPE).asInt();
    }

    private static Optional<JsonNode> decodeNull() {
        return Optional.of(NullNode.getInstance());
    }

    private static Optional<JsonNode> decodeBoolean(ObjectNode presentation) {
        if (presentation.has(FIELD_VALUE)) {
            JsonNode valueNode = presentation.get(FIELD_VALUE);

            if (valueNode.isInt()) {
                switch (valueNode.asInt()) {
                    case BOOLEAN_TRUE:
                        return Optional.of(BooleanNode.getTrue());
                    case BOOLEAN_FALSE:
                        return Optional.of(BooleanNode.getFalse());
                }
            }
        }

        return Optional.empty();
    }

    private static Optional<JsonNode> decodeNumber(ObjectNode presentation) {
        if (presentation.has(FIELD_VALUE)) {
            JsonNode valueNode = presentation.get(FIELD_VALUE);

            if (valueNode.isInt()) {
                return Optional.of(IntNode.valueOf(valueNode.asInt()));
            }
            else if (valueNode.isLong()) {
                return Optional.of(LongNode.valueOf(valueNode.asLong()));
            }
            else if (valueNode.isDouble()) {
                return Optional.of(DoubleNode.valueOf(valueNode.asDouble()));
            }
        }

        return Optional.empty();
    }

    private static Optional<JsonNode> decodeString(ObjectNode presentation) {
        if (presentation.has(FIELD_VALUE)) {
            JsonNode value = presentation.get(FIELD_VALUE);

            if (value.isTextual()) {
                return Optional.of(TextNode.valueOf(value.asText()));
            }
         }

        return Optional.empty();
    }


    private static Optional<JsonNode> decodeArray(ObjectNode presentation) {
        if (presentation.has(FIELD_VALUE)) {
            JsonNode valueNode = presentation.get(FIELD_VALUE);

            if (valueNode.isArray()) {
                ArrayNode array = new ObjectMapper().createArrayNode();

                ArrayNode presentationOfArrayItems = (ArrayNode) valueNode;

                for (int i = 0; i < presentationOfArrayItems.size(); i++) {
                    JsonNode itemPresentation = presentationOfArrayItems.get(i);

                    if (itemPresentation.isObject()) {
                        Optional<JsonNode> itemOption = decode((ObjectNode) itemPresentation);

                        if (itemOption.isPresent()) {
                            JsonNode item = itemOption.get();

                            array.add(item);
                        }
                    }
                }

                return Optional.of(array);
            }
        }

        return Optional.empty();
    }

    private static Optional<JsonNode> decodeObject(ObjectNode presentation) {
        if (presentation.has(FIELD_VALUE)) {
            JsonNode valueNode = presentation.get(FIELD_VALUE);

            if (valueNode.isArray()) {
                ObjectNode object = new ObjectMapper().createObjectNode();

                ArrayNode presentationOfObjectFields = (ArrayNode) valueNode;

                for (int i = 0; i < presentationOfObjectFields.size(); i++) {
                    JsonNode fieldPresentation = presentationOfObjectFields.get(i);

                    if (fieldPresentation.isObject()) {
                        if (fieldPresentation.has(FIELD_NAME)) {
                            JsonNode nameNode = fieldPresentation.get(FIELD_NAME);

                            if (nameNode.isTextual()) {
                                String name = nameNode.asText();

                                Optional<JsonNode> valueOption = decode((ObjectNode) fieldPresentation);

                                if (valueOption.isPresent()) {
                                    JsonNode value = valueOption.get();

                                    object.set(name, value);
                                }
                            }
                        }
                    }
                }

                return Optional.of(object);
            }
        }

        return Optional.empty();
    }

}
@@ -136,24 +136,53 @@
 va.util.
-Optional
+*;%0Aimport java.util.function.Function
 ;%0A%0Aimpor
@@ -260,21 +260,22 @@
    p
-ublic
+rivate
  static 
 Opti
@@ -274,156 +274,168 @@
 tic 
-Optional%3CJsonNode%3E decode(ObjectNode presentation) %7B%0A        switch (type(presentation)) %7B%0A            case TYPE_MARKER_NULL:%0A                return
+final Map%3CInteger, Function%3CObjectNode, Optional%3CJsonNode%3E%3E%3E resolvers = new HashMap%3C%3E();%0A    static %7B%0A        resolvers.put(TYPE_MARKER_NULL, (presentation) -%3E
  dec
@@ -443,16 +443,17 @@
 deNull()
+)
 ;%0A      
@@ -450,33 +450,38 @@
 ));%0A        
-    case 
+resolvers.put(
 TYPE_MARKER_
@@ -490,322 +490,229 @@
 MBER
-:%0A                return decodeNumber(presentation);%0A            case TYPE_MARKER_STRING:%0A                return decodeString(presentation);%0A            case TYPE_MARKER_BOOLEAN:%0A                return decodeBoolean(presentation);%0A            case TYPE_MARKER_ARRAY:%0A                return decodeArray(presentation
+, Decoder::decodeNumber);%0A        resolvers.put(TYPE_MARKER_STRING, Decoder::decodeString);%0A        resolvers.put(TYPE_MARKER_BOOLEAN, Decoder::decodeBoolean);%0A        resolvers.put(TYPE_MARKER_ARRAY, Decoder::decodeArray
 );%0A 
@@ -718,25 +718,30 @@
         
-  
-  case 
+resolvers.put(
 TYPE_MAR
@@ -754,46 +754,96 @@
 JECT
-:
+, Decoder::decodeObject);
 %0A    
+%7D%0A%0A
     
-        return
+public static Optional%3CJsonNode%3E
  decode
+(
 Object
-(
+Node 
 pres
@@ -855,20 +855,10 @@
 ion)
-;%0A        %7D%0A
+ %7B
 %0A   
@@ -870,38 +870,126 @@
 return Optional.
-empty(
+ofNullable(resolvers.get(type(presentation))).flatMap(resolver -%3E resolver.apply(presentation)
 );%0A    %7D%0A%0A    pr

Implement resolvers in Decoder