package center.sciprog.maps.compose

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import center.sciprog.maps.coordinates.*
import center.sciprog.maps.features.*
import space.kscience.kmath.geometry.Angle
import kotlin.math.ceil


internal fun FeatureGroup<Gmc>.coordinatesOf(pair: Pair<Number, Number>) =
    GeodeticMapCoordinates.ofDegrees(pair.first.toDouble(), pair.second.toDouble())

public typealias MapFeature = Feature<Gmc>

public fun FeatureGroup<Gmc>.circle(
    centerCoordinates: Pair<Number, Number>,
    size: Dp = 5.dp,
    id: String? = null,
): FeatureRef<Gmc, CircleFeature<Gmc>> = feature(
    id, CircleFeature(space, coordinatesOf(centerCoordinates), size)
)

public fun FeatureGroup<Gmc>.rectangle(
    centerCoordinates: Pair<Number, Number>,
    size: DpSize = DpSize(5.dp, 5.dp),
    id: String? = null,
): FeatureRef<Gmc, RectangleFeature<Gmc>> = feature(
    id, RectangleFeature(space, coordinatesOf(centerCoordinates), size)
)


public fun FeatureGroup<Gmc>.draw(
    position: Pair<Number, Number>,
    id: String? = null,
    draw: DrawScope.() -> Unit,
): FeatureRef<Gmc, DrawFeature<Gmc>> = feature(
    id,
    DrawFeature(space, coordinatesOf(position), drawFeature = draw)
)


public fun FeatureGroup<Gmc>.line(
    curve: GmcCurve,
    id: String? = null,
): FeatureRef<Gmc, LineFeature<Gmc>> = feature(
    id,
    LineFeature(space, curve.forward.coordinates, curve.backward.coordinates)
)

/**
 * A segmented geodetic curve
 */
public fun FeatureGroup<Gmc>.geodeticLine(
    curve: GmcCurve,
    ellipsoid: GeoEllipsoid = GeoEllipsoid.WGS84,
    maxLineDistance: Distance = 100.kilometers,
    id: String? = null,
): FeatureRef<Gmc, Feature<Gmc>> = if (curve.distance < maxLineDistance) {
    feature(
        id,
        LineFeature(space, curve.forward.coordinates, curve.backward.coordinates)
    )
} else {
    val segments = ceil(curve.distance / maxLineDistance).toInt()
    val segmentSize = curve.distance / segments
    val points = buildList<GmcPose> {
        add(curve.forward)
        repeat(segments) {
            val segment = ellipsoid.curveInDirection(this.last(), segmentSize, 1e-2)
            add(segment.backward)
        }
    }
    multiLine(points.map { it.coordinates }, id = id)
}

public fun FeatureGroup<Gmc>.geodeticLine(
    from: Gmc,
    to: Gmc,
    ellipsoid: GeoEllipsoid = GeoEllipsoid.WGS84,
    maxLineDistance: Distance = 100.kilometers,
    id: String? = null,
): FeatureRef<Gmc, Feature<Gmc>> = geodeticLine(ellipsoid.curveBetween(from, to), ellipsoid, maxLineDistance, id)

public fun FeatureGroup<Gmc>.line(
    aCoordinates: Pair<Double, Double>,
    bCoordinates: Pair<Double, Double>,
    id: String? = null,
): FeatureRef<Gmc, LineFeature<Gmc>> = feature(
    id,
    LineFeature(space, coordinatesOf(aCoordinates), coordinatesOf(bCoordinates))
)

public fun FeatureGroup<Gmc>.arc(
    center: Pair<Double, Double>,
    radius: Distance,
    startAngle: Angle,
    arcLength: Angle,
    id: String? = null,
): FeatureRef<Gmc, ArcFeature<Gmc>> = feature(
    id,
    ArcFeature(
        space,
        oval = space.Rectangle(coordinatesOf(center), radius, radius),
        startAngle = startAngle,
        arcLength = arcLength,
    )
)

public fun FeatureGroup<Gmc>.points(
    points: List<Pair<Double, Double>>,
    id: String? = null,
): FeatureRef<Gmc, PointsFeature<Gmc>> = feature(id, PointsFeature(space, points.map(::coordinatesOf)))

public fun FeatureGroup<Gmc>.multiLine(
    points: List<Pair<Double, Double>>,
    id: String? = null,
): FeatureRef<Gmc, MultiLineFeature<Gmc>> = feature(id, MultiLineFeature(space, points.map(::coordinatesOf)))

public fun FeatureGroup<Gmc>.icon(
    position: Pair<Double, Double>,
    image: ImageVector,
    size: DpSize = DpSize(20.dp, 20.dp),
    id: String? = null,
): FeatureRef<Gmc, VectorIconFeature<Gmc>> = feature(
    id,
    VectorIconFeature(
        space,
        coordinatesOf(position),
        size,
        image,
    )
)

public fun FeatureGroup<Gmc>.text(
    position: Pair<Double, Double>,
    text: String,
    font: FeatureFont.() -> Unit = { size = 16f },
    id: String? = null,
): FeatureRef<Gmc, TextFeature<Gmc>> = feature(
    id,
    TextFeature(space, coordinatesOf(position), text, fontConfig = font)
)

public fun FeatureGroup<Gmc>.pixelMap(
    rectangle: Rectangle<Gmc>,
    latitudeDelta: Angle,
    longitudeDelta: Angle,
    id: String? = null,
    builder: (Gmc) -> Color?,
): FeatureRef<Gmc, PixelMapFeature<Gmc>> = feature(
    id,
    PixelMapFeature(
        space,
        rectangle,
        Structure2D(
            ceil(rectangle.longitudeDelta / latitudeDelta).toInt(),
            ceil(rectangle.latitudeDelta / longitudeDelta).toInt()

        ) { (i, j) ->
            val longitude = rectangle.left + longitudeDelta * i
            val latitude = rectangle.bottom + latitudeDelta * j
            builder(
                Gmc(latitude, longitude)
            )
        }
    )
)