package com.github.nsnjson.decoding;

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

import java.util.*;
import java.util.function.Function;

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

public class Decoder {

    private static final Map<Integer, Function<ObjectNode, Optional<JsonNode>>> resolvers = new HashMap<>();
    static {
        resolvers.put(TYPE_MARKER_NULL, (presentation) -> decodeNull());
        resolvers.put(TYPE_MARKER_NUMBER, Decoder::decodeNumber);
        resolvers.put(TYPE_MARKER_STRING, Decoder::decodeString);
        resolvers.put(TYPE_MARKER_BOOLEAN, Decoder::decodeBoolean);
        resolvers.put(TYPE_MARKER_ARRAY, Decoder::decodeArray);
        resolvers.put(TYPE_MARKER_OBJECT, Decoder::decodeObject);
    }

    public static Optional<JsonNode> decode(JsonNode presentation) {
        if (presentation.isObject()) {
            return Optional.ofNullable(resolvers.get(type((ObjectNode) presentation))).flatMap(resolver -> resolver.apply((ObjectNode) presentation));
        } else {
            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(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(fieldPresentation);

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

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

                return Optional.of(object);
            }
        }

        return Optional.empty();
    }

}
@@ -73,55 +73,16 @@
 ind.
-*;%0Aimport com.fasterxml.jackson.databind.n
+JsonN
 ode
-.*
 ;%0A%0Ai
@@ -101,2644 +101,197 @@
 til.
-*;%0Aimport java.util.function.Function;%0A%0Aimport static com.github.nsnjson.format.Format.*;%0A%0Apublic class Decoder %7B%0A%0A    private static 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 decodeNull());%0A        resolvers.put(TYPE_MARKER_NUMBER, 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        resolvers.put(TYPE_MARKER_OBJECT, Decoder::decodeObject);%0A    %7D%0A%0A    public static Optional%3CJsonNode%3E decode(JsonNode presentation) %7B%0A        if (presentation.isObject()) %7B%0A            return Optional.ofNullable(resolvers.get(type((ObjectNode) presentation))).flatMap(resolver -%3E resolver.apply((ObjectNode) presentation));%0A        %7D else %7B%0A            return Optional.empty();%0A        %7D%0A    %7D%0A%0A    private static int type(ObjectNode presentation) %7B%0A        return presentation.get(FIELD_TYPE).asInt();%0A    %7D%0A%0A    private static Optional%3CJsonNode%3E decodeNull() %7B%0A        return Optional.of(NullNode.getInstance());%0A    %7D%0A%0A    private static Optional%3CJsonNode%3E decodeBoolean(ObjectNode presentation) %7B%0A        if (presentation.has(FIELD_VALUE)) %7B%0A            JsonNode valueNode = presentation.get(FIELD_VALUE);%0A%0A            if (valueNode.isInt()) %7B%0A                switch (valueNode.asInt()) %7B%0A                    case BOOLEAN_TRUE:%0A                        return Optional.of(BooleanNode.getTrue());%0A                    case BOOLEAN_FALSE:%0A                        return Optional.of(BooleanNode.getFalse());%0A                %7D%0A            %7D%0A        %7D%0A%0A        return Optional.empty();%0A    %7D%0A%0A    private static Optional%3CJsonNode%3E decodeNumber(ObjectNode presentation) %7B%0A        if (presentation.has(FIELD_VALUE)) %7B%0A            JsonNode valueNode = presentation.get(FIELD_VALUE);%0A%0A            if (valueNode.isInt()) %7B%0A                return Optional.of(IntNode.valueOf(valueNode.asInt()));%0A            %7D%0A            else if (valueNode.isLong()) %7B%0A                return Optional.of(LongNode.valueOf(valueNode.asLong()));%0A            %7D%0A            else if (valueNode.isDouble()) %7B%0A                return Optional.of(DoubleNode.valueOf(valueNode.asDouble()));%0A            %7D%0A        %7D%0A%0A        return Optional.empty();%0A    %7D%0A%0A    private static Optional%3CJsonNode%3E decodeString(ObjectNode presentation) %7B%0A        if (presentation.has(FIELD_VALUE)) %7B%0A            JsonNode value = presentation.get(FIELD_VALUE);%0A%0A            if (value.isTextual()) %7B%0A     
+Optional;%0A%0Apublic class Decoder %7B%0A%0A    private static final Decoding DEFAULT_DECODING = new DefaultDecoding();%0A%0A    public static Optional%3CJsonNode%3E decode(JsonNode presentation) %7B%0A
         
    r
@@ -290,2533 +290,51 @@
     
-   
 return 
-Optional.of(TextNode.valueOf(value.asText()));%0A            %7D%0A         %7D%0A%0A        return Optional.empty();%0A    %7D%0A%0A%0A    private static Optional%3CJsonNode%3E decodeArray(ObjectNode presentation) %7B%0A        if (presentation.has(FIELD_VALUE)) %7B%0A            JsonNode valueNode = presentation.get(FIELD_VALUE);%0A%0A            if (valueNode.isArray()) %7B%0A                ArrayNode array = new ObjectMapper().createArrayNode();%0A%0A                ArrayNode presentationOfArrayItems = (ArrayNode) valueNode;%0A%0A                for (int i = 0; i %3C presentationOfArrayItems.size(); i++) %7B%0A                    JsonNode itemPresentation = presentationOfArrayItems.get(i);%0A%0A                    if (itemPresentation.isObject()) %7B%0A                        Optional%3CJsonNode%3E itemOption = decode(itemPresentation);%0A%0A                        if (itemOption.isPresent()) %7B%0A                            JsonNode item = itemOption.get();%0A%0A                            array.add(item);%0A                        %7D%0A                    %7D%0A                %7D%0A%0A                return Optional.of(array);%0A            %7D%0A        %7D%0A%0A        return Optional.empty();%0A    %7D%0A%0A    private static Optional%3CJsonNode%3E decodeObject(ObjectNode presentation) %7B%0A        if (presentation.has(FIELD_VALUE)) %7B%0A            JsonNode valueNode = presentation.get(FIELD_VALUE);%0A%0A            if (valueNode.isArray()) %7B%0A                ObjectNode object = new ObjectMapper().createObjectNode();%0A%0A                ArrayNode presentationOfObjectFields = (ArrayNode) valueNode;%0A%0A                for (int i = 0; i %3C presentationOfObjectFields.size(); i++) %7B%0A                    JsonNode fieldPresentation = presentationOfObjectFields.get(i);%0A%0A                    if (fieldPresentation.isObject()) %7B%0A                        if (fieldPresentation.has(FIELD_NAME)) %7B%0A                            JsonNode nameNode = fieldPresentation.get(FIELD_NAME);%0A%0A                            if (nameNode.isTextual()) %7B%0A                                String name = nameNode.asText();%0A%0A                                Optional%3CJsonNode%3E valueOption = decode(fieldPresentation);%0A%0A                                if (valueOption.isPresent()) %7B%0A                                    JsonNode value = valueOption.get();%0A%0A                                    object.set(name, value);%0A                                %7D%0A                            %7D%0A                        %7D%0A                    %7D%0A                %7D%0A%0A                return Optional.of(object);%0A            %7D%0A        %7D%0A%0A        return Optional.empty(
+DEFAULT_DECODING.decode(presentation
 );%0A 

Refactor Decoder