// Path: core/src/main/java/com/github/filosganga/geogson/codec/CodecRegistry.java
// public class CodecRegistry<T, S extends Geometry<?>> implements Codec<T, S> {
// 
//     private final Map<Type, Codec<T,S>> codecsByS = new ConcurrentHashMap<>();
//     private final Map<Type, Codec<T,S>> codecsByT = new ConcurrentHashMap<>();
// 
//     public CodecRegistry() {
//         this(new ArrayList<>());
//     }
// 
//     public CodecRegistry(Iterable<Codec<? extends T, ? extends S>> codecs ) {
//         for(Codec<? extends T, ? extends S> codec : codecs) {
//             addCodec(codec);
//         }
//     }
// 
//     @SuppressWarnings("unchecked")
//     public void addCodec(Codec<? extends T, ? extends S> codec) {
// 
//         ParameterizedType type = (ParameterizedType) codec.getClass().getGenericSuperclass();
// 
//         Type t = type.getActualTypeArguments()[0];
//         Type s = type.getActualTypeArguments()[1];
// 
//         codecsByS.put(s, (Codec<T, S>) codec);
//         codecsByT.put(t, (Codec<T, S>) codec);
//     }
// 
//     @Override
//     public S toGeometry(T src) {
//         return codecsByT.get(src.getClass()).toGeometry(src);
//     }
// 
//     @Override
//     public T fromGeometry(S src) {
//         return codecsByS.get(src.getClass()).fromGeometry(src);
//     }
// }
// 
// Path: core/src/main/java/com/github/filosganga/geogson/model/Geometry.java
// public interface Geometry<P extends Positions> {
// 
//     /**
//      * Define the type of the Geometry. As defined in the GeoJson specifications.
//      */
//     enum Type {
//         POINT("Point"),
//         MULTI_POINT("MultiPoint"),
//         LINE_STRING("LineString"),
//         LINEAR_RING("LineString"),
//         MULTI_LINE_STRING("MultiLineString"),
//         POLYGON("Polygon"),
//         MULTI_POLYGON("MultiPolygon"),
//         GEOMETRY_COLLECTION("GeometryCollection");
// 
//         private final String value;
// 
//         Type(String value) {
//             this.value = value;
//         }
// 
//         public String getValue() {
//             return value;
//         }
// 
//         public static Type forValue(String value) {
//             for (Type type : values()) {
//                 if (type.getValue().equalsIgnoreCase(value)) {
//                     return type;
//                 }
//             }
//             throw new IllegalArgumentException("Cannot build a geometry for type: " + value);
//         }
//     }
// 
//     /**
//      * Returns the Geometry type.
//      *
//      * @return Type
//      */
//     Type type();
// 
//     /**
//      * Returns the Position underlying instance.
//      *
//      * @return Position
//      */
//     P positions();
// 
//     /**
//      * Returns the size of this Geometry. Depending of the type of this Geometry, it may have different meaning.
//      * eg: For a LineString it is the number of points, for a MultiPolygon it is the number of polygons.
//      *
//      * @return Int
//      */
//     int size();
// }


import com.github.filosganga.geogson.codec.CodecRegistry;
import com.github.filosganga.geogson.model.Geometry;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.locationtech.jts.geom.GeometryFactory;
import java.io.IOException;
package com.github.filosganga.geogson.jts;



/**
 * The JTS Gson TypeAdapterFactory.
 *
 * It can be built passing a GeometryFactory instance, otherwise it will instantiate a default one.
 *
 * How to use it:
 * <pre>
 *     Gson gson = new GsonBuilder()
 *         .registerTypeAdapterFactory(new JtsAdapterFactory(new GeometryFactory(new PrecisionModel(100), 4326)))
 *         .registerTypeAdapterFactory(new GeometryAdapterFactory())
 *         .create();
 * </pre>
 *
 */
public class JtsAdapterFactory implements TypeAdapterFactory {

    /**
     * {@link GeometryFactory} defining a PrecisionModel and a SRID
     */
    private final GeometryFactory geometryFactory;

    /**
     * Create an adapter for {@link org.locationtech.jts.geom.Geometry JTS
     * Geometry} with a default {@link GeometryFactory} (i.e. using the
     * {@link GeometryFactory#GeometryFactory() empty constructor})
     *
     */
    public JtsAdapterFactory() {
        this.geometryFactory = new GeometryFactory();
    }

    /**
     * Create an adapter for {@link org.locationtech.jts.geom.Geometry JTS
     * Geometry} with an optional {@link GeometryFactory}
     *
     * @param geometryFactory
     *          an optional {@link GeometryFactory} defining a PrecisionModel
     *          and a SRID
     */
    public JtsAdapterFactory(GeometryFactory geometryFactory) {
        if (geometryFactory == null) {
            this.geometryFactory = new GeometryFactory();
        } else {
            this.geometryFactory = geometryFactory;
        }
    }

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {

// Path: core/src/main/java/com/github/filosganga/geogson/codec/CodecRegistry.java
// public class CodecRegistry<T, S extends Geometry<?>> implements Codec<T, S> {
// 
//     private final Map<Type, Codec<T,S>> codecsByS = new ConcurrentHashMap<>();
//     private final Map<Type, Codec<T,S>> codecsByT = new ConcurrentHashMap<>();
// 
//     public CodecRegistry() {
//         this(new ArrayList<>());
//     }
// 
//     public CodecRegistry(Iterable<Codec<? extends T, ? extends S>> codecs ) {
//         for(Codec<? extends T, ? extends S> codec : codecs) {
//             addCodec(codec);
//         }
//     }
// 
//     @SuppressWarnings("unchecked")
//     public void addCodec(Codec<? extends T, ? extends S> codec) {
// 
//         ParameterizedType type = (ParameterizedType) codec.getClass().getGenericSuperclass();
// 
//         Type t = type.getActualTypeArguments()[0];
//         Type s = type.getActualTypeArguments()[1];
// 
//         codecsByS.put(s, (Codec<T, S>) codec);
//         codecsByT.put(t, (Codec<T, S>) codec);
//     }
// 
//     @Override
//     public S toGeometry(T src) {
//         return codecsByT.get(src.getClass()).toGeometry(src);
//     }
// 
//     @Override
//     public T fromGeometry(S src) {
//         return codecsByS.get(src.getClass()).fromGeometry(src);
//     }
// }
// 
// Path: core/src/main/java/com/github/filosganga/geogson/model/Geometry.java
// public interface Geometry<P extends Positions> {
// 
//     /**
//      * Define the type of the Geometry. As defined in the GeoJson specifications.
//      */
//     enum Type {
//         POINT("Point"),
//         MULTI_POINT("MultiPoint"),
//         LINE_STRING("LineString"),
//         LINEAR_RING("LineString"),
//         MULTI_LINE_STRING("MultiLineString"),
//         POLYGON("Polygon"),
//         MULTI_POLYGON("MultiPolygon"),
//         GEOMETRY_COLLECTION("GeometryCollection");
// 
//         private final String value;
// 
//         Type(String value) {
//             this.value = value;
//         }
// 
//         public String getValue() {
//             return value;
//         }
// 
//         public static Type forValue(String value) {
//             for (Type type : values()) {
//                 if (type.getValue().equalsIgnoreCase(value)) {
//                     return type;
//                 }
//             }
//             throw new IllegalArgumentException("Cannot build a geometry for type: " + value);
//         }
//     }
// 
//     /**
//      * Returns the Geometry type.
//      *
//      * @return Type
//      */
//     Type type();
// 
//     /**
//      * Returns the Position underlying instance.
//      *
//      * @return Position
//      */
//     P positions();
// 
//     /**
//      * Returns the size of this Geometry. Depending of the type of this Geometry, it may have different meaning.
//      * eg: For a LineString it is the number of points, for a MultiPolygon it is the number of polygons.
//      *
//      * @return Int
//      */
//     int size();
// }


// Path: jts/src/main/java/com/github/filosganga/geogson/jts/JtsAdapterFactory.java
import com.github.filosganga.geogson.codec.CodecRegistry;
import com.github.filosganga.geogson.model.Geometry;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.locationtech.jts.geom.GeometryFactory;
import java.io.IOException;

package com.github.filosganga.geogson.jts;



/**
 * The JTS Gson TypeAdapterFactory.
 *
 * It can be built passing a GeometryFactory instance, otherwise it will instantiate a default one.
 *
 * How to use it:
 * <pre>
 *     Gson gson = new GsonBuilder()
 *         .registerTypeAdapterFactory(new JtsAdapterFactory(new GeometryFactory(new PrecisionModel(100), 4326)))
 *         .registerTypeAdapterFactory(new GeometryAdapterFactory())
 *         .create();
 * </pre>
 *
 */
public class JtsAdapterFactory implements TypeAdapterFactory {

    /**
     * {@link GeometryFactory} defining a PrecisionModel and a SRID
     */
    private final GeometryFactory geometryFactory;

    /**
     * Create an adapter for {@link org.locationtech.jts.geom.Geometry JTS
     * Geometry} with a default {@link GeometryFactory} (i.e. using the
     * {@link GeometryFactory#GeometryFactory() empty constructor})
     *
     */
    public JtsAdapterFactory() {
        this.geometryFactory = new GeometryFactory();
    }

    /**
     * Create an adapter for {@link org.locationtech.jts.geom.Geometry JTS
     * Geometry} with an optional {@link GeometryFactory}
     *
     * @param geometryFactory
     *          an optional {@link GeometryFactory} defining a PrecisionModel
     *          and a SRID
     */
    public JtsAdapterFactory(GeometryFactory geometryFactory) {
        if (geometryFactory == null) {
            this.geometryFactory = new GeometryFactory();
        } else {
            this.geometryFactory = geometryFactory;
        }
    }

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {

        if(org.locationtech.jts.geom.Geometry.class.isAssignableFrom(type.getRawType())) {