package com.mapbox.rctmgl.components.styles.sources;

import android.content.Context;
import android.util.SparseArray;
import android.view.View;

import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.MapBuilder;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.style.sources.Source;
import com.mapbox.rctmgl.components.AbstractMapFeature;
import com.mapbox.rctmgl.components.mapview.RCTMGLMapView;
import com.mapbox.rctmgl.components.styles.layers.RCTLayer;
import com.mapbox.services.commons.geojson.Feature;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by nickitaliano on 9/7/17.
 */

public abstract class RCTSource<T extends Source> extends AbstractMapFeature {
    public static final String DEFAULT_ID = "composite";

    public static final double DEFAULT_HITBOX_WIDTH = 44.0;
    public static final double DEFAULT_HITBOX_HEIGHT = 44.0;

    protected RCTMGLMapView mMapView;
    protected MapboxMap mMap;

    protected String mID;
    protected T mSource;
    protected boolean mHasPressListener;
    protected Map<String, Double> mTouchHitbox;

    protected List<RCTLayer> mLayers;
    private SparseArray<RCTLayer> mQueuedLayers;

    public RCTSource(Context context) {
        super(context);
        mLayers = new ArrayList<>();
        mQueuedLayers = new SparseArray<>();
    }

    public String getID() {
        return mID;
    }

    public String[] getLayerIDs() {
        List<String> layerIDs = new ArrayList<>();

        for (int i = 0; i < mLayers.size(); i++) {
            RCTLayer layer = mLayers.get(i);
            layerIDs.add(layer.getID());
        }

        return layerIDs.toArray(new String[layerIDs.size()]);
    }

    public boolean hasPressListener() {
        return mHasPressListener;
    }

    public void setHasPressListener (boolean hasPressListener) {
        mHasPressListener = hasPressListener;
    }

    public void setHitbox(ReadableMap map) {
        Map<String, Double> hitbox = new HashMap<>();
        hitbox.put("width", map.getDouble("width"));
        hitbox.put("height", map.getDouble("height"));
        mTouchHitbox = hitbox;
    }

    public void setID(String id) {
        mID = id;
    }

    public void setSource(T source) {
        mSource = source;
    }

    public Map<String, Double> getTouchHitbox() {
        if (!hasPressListener()) {
            return null;
        }

        if (mTouchHitbox == null) {
            return MapBuilder.<String, Double>builder()
                    .put("width", DEFAULT_HITBOX_WIDTH)
                    .put("height", DEFAULT_HITBOX_HEIGHT)
                    .build();
        }

        return mTouchHitbox;
    }

    public int getLayerCount () {
        int totalCount = 0;

        if (mQueuedLayers != null) {
            totalCount = mQueuedLayers.size();
        }

        totalCount += mLayers.size();
        return totalCount;
    }

    @Override
    public void addToMap(RCTMGLMapView mapView) {
        mMapView = mapView;
        mMap = mapView.getMapboxMap();

        T existingSource = mMap.<T>getSourceAs(mID);
        if (existingSource != null) {
            mSource = existingSource;
        } else {
            mSource = makeSource();
            mMap.addSource(mSource);
        }

        if (mQueuedLayers != null && mQueuedLayers.size() > 0) { // first load
            for (int i = 0; i < mQueuedLayers.size(); i++) {
                int childPosition = mQueuedLayers.keyAt(i);
                addLayerToMap(mQueuedLayers.get(childPosition), childPosition);
            }
            mQueuedLayers = null;
        } else if (mLayers.size() > 0) { // handles the case of switching style url, but keeping layers on map
            for (int i = 0; i < mLayers.size(); i++) {
                addLayerToMap(mLayers.get(i), i);
            }
        }
    }

    @Override
    public void removeFromMap(RCTMGLMapView mapView) {
        if (mLayers.size() > 0) {
            for (int i = 0; i < mLayers.size(); i++) {
                RCTLayer layer = mLayers.get(i);
                layer.removeFromMap(mMapView);
            }
        }
        mMap.removeSource(mSource);
    }

    public void addLayer(View childView, int childPosition) {
        if (!(childView instanceof RCTLayer)) {
            return;
        }

        RCTLayer layer = (RCTLayer) childView;
        if (mMap == null) {
            mQueuedLayers.put(childPosition, layer);
        } else {
            addLayerToMap(layer, childPosition);
        }
    }

    public void removeLayer(int childPosition) {
        if (childPosition >= mLayers.size()) {
            return;
        }
        removeLayerFromMap(mLayers.get(childPosition), childPosition);
    }

    public RCTLayer getLayerAt(int childPosition) {
        return mLayers.get(childPosition);
    }

    protected void addLayerToMap(RCTLayer layer, int childPosition) {
        if (mMapView == null || layer == null) {
            return;
        }

        layer.addToMap(mMapView);
        if (!mLayers.contains(layer)) {
            mLayers.add(childPosition, layer);
        }
    }

    protected void removeLayerFromMap(RCTLayer layer, int childPosition) {
        if (mMapView == null || layer == null) {
            return;
        }
        layer.removeFromMap(mMapView);
        mLayers.remove(childPosition);
    }

    public abstract T makeSource();
    public abstract void onPress(Feature feature);

    public static boolean isDefaultSource(String sourceID) {
        return DEFAULT_ID.equals(sourceID);
    }
}

@@ -1218,27 +1218,20 @@
 private 
-SparseArray
+List
 %3CRCTLaye
@@ -1378,27 +1378,25 @@
 s = new 
-Sparse
 Array
+List
 %3C%3E();%0A  
@@ -3475,68 +3475,8 @@
 ) %7B%0A
-                int childPosition = mQueuedLayers.keyAt(i);%0A
     
@@ -3523,37 +3523,13 @@
 get(
-childPosition), childPosition
+i), i
 );%0A 
@@ -4385,19 +4385,19 @@
 dLayers.
-put
+add
 (childPo

Update ADT for child layers in sources to be a List instead of SparseArray