package com.github.nsnjson.encoding;

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

import java.util.Optional;

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

public class DefaultEncoding implements Encoding {

    @Override
    public Optional<JsonNode> encodeNull() {
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode presentation = objectMapper.createObjectNode();
        presentation.put(FIELD_TYPE, TYPE_MARKER_NULL);

        return Optional.of(presentation);
    }

    @Override
    public Optional<JsonNode> encodeNumber(NumericNode value) {
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode presentation = objectMapper.createObjectNode();
        presentation.put(FIELD_TYPE, TYPE_MARKER_NUMBER);
        presentation.set(FIELD_VALUE, value);

        return Optional.of(presentation);
    }

    @Override
    public Optional<JsonNode> encodeString(TextNode value) {
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode presentation = objectMapper.createObjectNode();
        presentation.put(FIELD_TYPE, TYPE_MARKER_STRING);
        presentation.set(FIELD_VALUE, value);

        return Optional.of(presentation);
    }

    @Override
    public Optional<JsonNode> encodeBoolean(BooleanNode value) {
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode presentation = objectMapper.createObjectNode();
        presentation.put(FIELD_TYPE, TYPE_MARKER_BOOLEAN);
        presentation.put(FIELD_VALUE, value.asBoolean() ? BOOLEAN_TRUE : BOOLEAN_FALSE);

        return Optional.of(presentation);
    }

    @Override
    public Optional<JsonNode> encodeArray(ArrayNode array) {
        ObjectMapper objectMapper = new ObjectMapper();

        ArrayNode itemsPresentation = objectMapper.createArrayNode();

        for (int i = 0; i < array.size(); i++) {
            JsonNode item = array.get(i);

            Optional<JsonNode> itemPresentationOption = encode(item);

            if (itemPresentationOption.isPresent()) {
                JsonNode itemPresentation = itemPresentationOption.get();

                itemsPresentation.add(itemPresentation);
            }
        }

        ObjectNode presentation = objectMapper.createObjectNode();
        presentation.put(FIELD_TYPE, TYPE_MARKER_ARRAY);
        presentation.set(FIELD_VALUE, itemsPresentation);

        return Optional.of(presentation);
    }

    @Override
    public Optional<JsonNode> encodeObject(ObjectNode object) {
        ObjectMapper objectMapper = new ObjectMapper();

        ArrayNode fieldsPresentation = objectMapper.createArrayNode();

        object.fieldNames().forEachRemaining(name -> {
            JsonNode value = object.get(name);

            Optional<JsonNode> valuePresentationOption = encode(value);

            if (valuePresentationOption.isPresent()) {
                JsonNode valuePresentation = valuePresentationOption.get();

                ObjectNode fieldPresentation = objectMapper.createObjectNode();
                fieldPresentation.put(FIELD_NAME, name);

                if (valuePresentation.has(FIELD_TYPE)) {
                    fieldPresentation.set(FIELD_TYPE, valuePresentation.get(FIELD_TYPE));
                }

                if (valuePresentation.has(FIELD_VALUE)) {
                    fieldPresentation.set(FIELD_VALUE, valuePresentation.get(FIELD_VALUE));
                }

                fieldsPresentation.add(fieldPresentation);
            }
        });

        ObjectNode presentation = objectMapper.createObjectNode();
        presentation.put(FIELD_TYPE, TYPE_MARKER_OBJECT);
        presentation.set(FIELD_VALUE, fieldsPresentation);

        return Optional.of(presentation);
    }

    @Override
    public Optional<JsonNode> encode(JsonNode json) {
        switch (json.getNodeType()) {
            case NULL:
                return encodeNull();
            case NUMBER:
                return encodeNumber((NumericNode) json);
            case STRING:
                return encodeString((TextNode) json);
            case BOOLEAN:
                return encodeBoolean((BooleanNode) json);
            case ARRAY:
                return encodeArray((ArrayNode) json);
            case OBJECT:
                return encodeObject((ObjectNode) json);
            default:
                return Optional.empty();
        }
    }
}
@@ -3826,20 +3826,20 @@
 sonNode 
-json
+data
 ) %7B%0A    
@@ -3850,20 +3850,20 @@
 switch (
-json
+data
 .getNode
@@ -3894,29 +3894,16 @@
 se NULL:
-%0A            
     retu
@@ -3943,31 +3943,16 @@
  NUMBER:
-%0A              
   return
@@ -3975,28 +3975,28 @@
 umericNode) 
-json
+data
 );%0A         
@@ -4010,33 +4010,18 @@
  STRING:
-%0A             
   
- 
 return e
@@ -4039,28 +4039,28 @@
 ((TextNode) 
-json
+data
 );%0A         
@@ -4075,32 +4075,16 @@
 BOOLEAN:
-%0A               
  return 
@@ -4107,28 +4107,28 @@
 ooleanNode) 
-json
+data
 );%0A         
@@ -4141,30 +4141,16 @@
 e ARRAY:
-%0A             
    retur
@@ -4171,28 +4171,28 @@
 (ArrayNode) 
-json
+data
 );%0A         
@@ -4206,31 +4206,16 @@
  OBJECT:
-%0A              
   return
@@ -4245,35 +4245,14 @@
 de) 
-json);%0A            default:
+data);
 %0A   
@@ -4248,32 +4248,35 @@
  data);%0A        
+%7D%0A%0A
         return O
@@ -4292,25 +4292,15 @@
 mpty();%0A
-        %7D%0A
     %7D%0A%7D

Refactor { DefaultEncoding }