/**
 * Copyright 2018 interactive instruments GmbH
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package de.ii.ldproxy.target.geojson;

import de.ii.ldproxy.wfs3.api.FeatureWriterGeoJson;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Instantiate;
import org.apache.felix.ipojo.annotations.Provides;

import java.io.IOException;
import java.io.Writer;
import java.util.function.Consumer;

/**
 * @author zahnen
 */
@Component
@Provides
@Instantiate
public class GeoJsonWriterGeometry implements GeoJsonWriter {

    @Override
    public GeoJsonWriterGeometry create() {
        return new GeoJsonWriterGeometry();
    }

    private int geometryNestingDepth = 0;
    private boolean geometryOpen;
    private boolean hasGeometry;

    @Override
    public int getSortPriority() {
        return 30;
    }

    private void reset() {
        this.geometryNestingDepth = 0;
        this.geometryOpen = false;
        this.hasGeometry = false;
    }

    @Override
    public void onStart(FeatureTransformationContextGeoJson transformationContext, Consumer<FeatureTransformationContextGeoJson> next) throws IOException {
        reset();

        next.accept(transformationContext);
    }

    @Override
    public void onProperty(FeatureTransformationContextGeoJson transformationContext, Consumer<FeatureTransformationContextGeoJson> next) throws IOException {

        if (!geometryOpen && !hasGeometry && !transformationContext.getState()
                                                                   .isBuffering()) {
            // buffer properties until geometry arrives
            transformationContext.startBuffering();
        }

        next.accept(transformationContext);
    }

    @Override
    public void onCoordinates(FeatureTransformationContextGeoJson transformationContext, Consumer<FeatureTransformationContextGeoJson> next) throws IOException {
        if (transformationContext.getState()
                                 .getCurrentGeometryType()
                                 .isPresent()
                && transformationContext.getState()
                                        .getCoordinatesWriterBuilder()
                                        .isPresent()
                && transformationContext.getState()
                                        .getCurrentValue()
                                        .isPresent()) {

            GeoJsonGeometryMapping.GEO_JSON_GEOMETRY_TYPE currentGeometryType = transformationContext.getState()
                                                                                                     .getCurrentGeometryType()
                                                                                                     .get();
            int currentGeometryNestingChange = currentGeometryType == GeoJsonGeometryMapping.GEO_JSON_GEOMETRY_TYPE.LINE_STRING ? 0 : transformationContext.getState()
                                                                    .getCurrentGeometryNestingChange();

            // handle nesting
            if (!geometryOpen) {
                this.geometryOpen = true;
                this.hasGeometry = true;
                //TODO: to FeatureTransformerGeoJson
                this.geometryNestingDepth = currentGeometryNestingChange;

                transformationContext.stopBuffering();

                transformationContext.getJson()
                                     .writeFieldName("geometry");
                transformationContext.getJson()
                                     .writeStartObject();
                transformationContext.getJson()
                                     .writeStringField("type", currentGeometryType.toString());
                transformationContext.getJson()
                                     .writeFieldName("coordinates");
                transformationContext.getJson()
                                     .writeStartArray();

                // TODO
                /*switch (currentGeometryType) {
                    case MULTI_POLYGON:
                        //json.writeStartArray();
                    case POLYGON:
                    case MULTI_LINE_STRING:
                        json.writeStartArray();
                }*/

                for (int i = 0; i < currentGeometryNestingChange; i++) {
                    transformationContext.getJson()
                                         .writeStartArray();
                }
            } else if (currentGeometryNestingChange > 0) {
                for (int i = 0; i < currentGeometryNestingChange; i++) {
                    transformationContext.getJson()
                                         .writeEndArray();
                }
                for (int i = 0; i < currentGeometryNestingChange; i++) {
                    transformationContext.getJson()
                                         .writeStartArray();
                }
            }

            Writer coordinatesWriter = transformationContext.getState()
                                                            .getCoordinatesWriterBuilder()
                                                            .get()
                                                            .format(new JsonCoordinateFormatter(transformationContext.getJson()))
                                                            .build();
            coordinatesWriter.write(transformationContext.getState()
                                                         .getCurrentValue()
                                                         .get());
            coordinatesWriter.close();
        }

        next.accept(transformationContext);
    }

    @Override
    public void onGeometryEnd(FeatureTransformationContextGeoJson transformationContext, Consumer<FeatureTransformationContextGeoJson> next) throws IOException {

        next.accept(transformationContext);

        // close geometry field and stop buffering
        closeGeometryIfAny(transformationContext);

    }

    @Override
    public void onFeatureEnd(FeatureTransformationContextGeoJson transformationContext, Consumer<FeatureTransformationContextGeoJson> next) throws IOException {

        // close geometry if no coordinates were written and stop buffering
        closeGeometryIfNone(transformationContext);

        // next chain for extensions
        next.accept(transformationContext);
    }

    private void closeGeometryIfAny(FeatureTransformationContextGeoJson transformationContext) throws IOException {
        if (geometryOpen) {
            this.geometryOpen = false;

            // close nesting braces
            for (int i = 0; i < geometryNestingDepth; i++) {
                transformationContext.getJson()
                                     .writeEndArray();
            }

            this.geometryNestingDepth = 0;

        /*switch (geometryType.get()) {
            case MULTI_POLYGON:
                //json.writeEndArray();
            case POLYGON:
            case MULTI_LINE_STRING:
                json.writeEndArray();
        }*/

            //close coordinates
            transformationContext.getJson()
                                 .writeEndArray();

            //close geometry object
            transformationContext.getJson()
                                 .writeEndObject();

            transformationContext.flushBuffer();
        }
    }

    private void closeGeometryIfNone(FeatureTransformationContextGeoJson transformationContext) throws IOException {

        if (!hasGeometry) {
            transformationContext.stopBuffering();

            // null geometry
            transformationContext.getJson()
                                 .writeFieldName("geometry");
            transformationContext.getJson()
                                 .writeNull();

            transformationContext.flushBuffer();
        }
        this.hasGeometry = false;
    }
}

@@ -46,16 +46,20 @@
  GmbH%0A *
+ %3Cp%3E
 %0A * This
@@ -937,16 +937,95 @@
 eometry;
+%0A    private GeoJsonGeometryMapping.GEO_JSON_GEOMETRY_TYPE currentGeometryType;
 %0A%0A    @O
@@ -2668,54 +2668,13 @@
     
-GeoJsonGeometryMapping.GEO_JSON_GEOMETRY_TYPE 
+this.
 curr
@@ -2788,49 +2788,8 @@
     
-         
-                                
 .get
@@ -2874,49 +2874,8 @@
     
-                     
-                    
 .get
@@ -3037,32 +3037,119 @@
 text.getState()%0A
+                                                                                       
                 
@@ -4044,24 +4044,126 @@
 rdinates%22);%0A
+                if (currentGeometryType != GeoJsonGeometryMapping.GEO_JSON_GEOMETRY_TYPE.POINT) %7B%0A    
             
@@ -4227,32 +4227,36 @@
                 
+    
 .writeStartArray
@@ -4250,32 +4250,50 @@
 iteStartArray();
+%0A                %7D
 %0A%0A              
@@ -7439,16 +7439,114 @@
 dinates%0A
+            if (currentGeometryType != GeoJsonGeometryMapping.GEO_JSON_GEOMETRY_TYPE.POINT) %7B%0A    
         
@@ -7606,32 +7606,36 @@
                 
+    
 .writeEndArray()
@@ -7635,16 +7635,30 @@
 Array();
+%0A            %7D
 %0A%0A      

fix geojson points