package com.isidroid.ui.maps.map_algorithm
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.model.LatLng
import com.google.maps.android.clustering.Cluster
import com.google.maps.android.clustering.ClusterItem
import com.google.maps.android.clustering.algo.Algorithm
import com.google.maps.android.clustering.algo.StaticCluster
import com.google.maps.android.geometry.Bounds
import com.google.maps.android.geometry.Point
import com.google.maps.android.projection.SphericalMercatorProjection
import com.google.maps.android.quadtree.PointQuadTree
import timber.log.Timber
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.collections.HashSet
import kotlin.math.pow

//class YNonHierarchicalDistanceBasedAlgorithm<T : ClusterItem?>(val maxDistance: Int, googleMap: GoogleMap) : Algorithm<T> {
//
//    /**
//     * Any modifications should be synchronized on mQuadTree.
//     */
//    private val mItems: MutableCollection<QuadItem<T>> = HashSet()
//
//    /**
//     * Any modifications should be synchronized on mQuadTree.
//     */
//    private val mQuadTree = PointQuadTree<QuadItem<T>>(0.0, 1.0, 0.0, 1.0)
//
//    init {
//        googleMap.setOnCameraIdleListener {
//            // When the camera becomes idle (after zoom or pan), recalculate clusters
//            val zoom = googleMap.cameraPosition.zoom.toDouble()
//            getClusters(zoom)
//        }
//    }
//
//    override fun addItem(item: T) {
//        val quadItem = QuadItem(item)
//        synchronized(mQuadTree) {
//            mItems.add(quadItem)
//            mQuadTree.add(quadItem)
//        }
//    }
//
//    override fun addItems(items: Collection<T>) {
//        for (item in items) {
//            addItem(item)
//        }
//    }
//
//    override fun clearItems() {
//        synchronized(mQuadTree) {
//            mItems.clear()
//            mQuadTree.clear()
//        }
//    }
//
//    override fun removeItem(item: T) {
//        // QuadItem delegates hashcode() and equals() to its item so,
//        // removing any QuadItem to that item will remove the item
//        val quadItem = QuadItem(item)
//        synchronized(mQuadTree) {
//            mItems.remove(quadItem)
//            mQuadTree.remove(quadItem)
//        }
//    }
//
//    override fun getClusters(zoom: Double): Set<Cluster<T>> {
//        val discreteZoom = zoom.toInt()
//        val zoomSpecificSpan: Double = maxDistance / 2.0.pow(discreteZoom.toDouble()) / 256
//
//        val visitedCandidates: MutableSet<QuadItem<T>> = HashSet()
//        val results: MutableSet<Cluster<T>> = HashSet()
//        val distanceToCluster: MutableMap<QuadItem<T>, Double> = HashMap()
//        val itemToCluster: MutableMap<QuadItem<T>, StaticCluster<T>> = HashMap()
//
//        synchronized(mQuadTree) {
//            for (candidate in getClusteringItems(mQuadTree, discreteZoom)) {
//                if (visitedCandidates.contains(candidate)) {
//                    // Candidate is already part of another cluster.
//                    continue
//                }
//
//                val searchBounds = createBoundsFromSpan(candidate.point, zoomSpecificSpan)
//                val clusterItems = mQuadTree.search(searchBounds)
//                if (clusterItems.size == 1) {
//                    // Only the current marker is in range. Just add the single item to the results.
//                    results.add(candidate)
//                    visitedCandidates.add(candidate)
//                    distanceToCluster[candidate] = 0.0
//                    continue
//                }
//                val cluster = StaticCluster<T>(candidate.mClusterItem!!.position)
//                results.add(cluster)
//
//                for (clusterItem in clusterItems) {
//                    val existingDistance = distanceToCluster[clusterItem]
//                    val distance = distanceSquared(clusterItem.point, candidate.point)
//                    if (existingDistance != null) {
//                        // Item already belongs to another cluster. Check if it's closer to this cluster.
//                        if (existingDistance < distance) {
//                            continue
//                        }
//                        // Move item to the closer cluster.
//                        itemToCluster[clusterItem]!!.remove(clusterItem.mClusterItem)
//                    }
//                    distanceToCluster[clusterItem] = distance
//                    cluster.add(clusterItem.mClusterItem)
//                    itemToCluster[clusterItem] = cluster
//                }
//                visitedCandidates.addAll(clusterItems)
//            }
//        }
//
//        Timber.i("Clusters: $results")
//        return results
//    }
//
//    private fun getClusteringItems(
//        quadTree: PointQuadTree<QuadItem<T>>?,
//        discreteZoom: Int
//    ): Collection<QuadItem<T>> {
//        return mItems
//    }
//
//    override fun getItems(): Collection<T> {
//        val items: MutableList<T> = ArrayList()
//        synchronized(mQuadTree) {
//            for (quadItem in mItems) {
//                items.add(quadItem.mClusterItem)
//            }
//        }
//        return items
//    }
//
//    private fun distanceSquared(a: Point, b: Point): Double {
//        return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)
//    }
//
//    private fun createBoundsFromSpan(p: Point, span: Double): Bounds {
//        // TODO: Use a span that takes into account the visual size of the marker, not just its
//        // LatLng.
//        val halfSpan = span / 2
//        return Bounds(
//            p.x - halfSpan, p.x + halfSpan,
//            p.y - halfSpan, p.y + halfSpan
//        )
//    }
//
//    class QuadItem<T : ClusterItem?>(val mClusterItem: T) : PointQuadTree.Item, Cluster<T> {
//        private val mPoint: Point
//        private val mPosition: LatLng = mClusterItem!!.position
//        private val singletonSet: Set<T>
//
//        init {
//            mPoint = PROJECTION.toPoint(mPosition)
//            singletonSet = setOf(mClusterItem)
//        }
//
//        override fun getPoint(): Point {
//            return mPoint
//        }
//
//        override fun getPosition(): LatLng {
//            return mPosition
//        }
//
//        override fun getItems(): Set<T> {
//            return singletonSet
//        }
//
//        override fun getSize(): Int {
//            return 1
//        }
//
//        override fun hashCode(): Int {
//            return mClusterItem.hashCode()
//        }
//
//        override fun equals(other: Any?): Boolean {
//            if (other !is QuadItem<*>) {
//                return false
//            }
//
//            return other.mClusterItem == mClusterItem
//        }
//    }
//
//    companion object {
//        private const val DEFAULT_MAX_DISTANCE_AT_ZOOM = 100 // essentially 100 dp.
//        private val PROJECTION = SphericalMercatorProjection(1.0)
//    }
//}
