// SPDX-FileCopyrightText: Adam Evyčędo
//
// SPDX-License-Identifier: GPL-3.0-or-later

package xyz.apiote.bimba.czwek.api

import android.content.Context
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import xyz.apiote.bimba.czwek.R
import xyz.apiote.bimba.czwek.api.transitous.api.GeocodeApi
import xyz.apiote.bimba.czwek.api.transitous.api.MapApi
import xyz.apiote.bimba.czwek.api.transitous.model.Match
import xyz.apiote.bimba.czwek.repo.Position
import xyz.apiote.bimba.czwek.repo.Queryable
import xyz.apiote.bimba.czwek.repo.Stop
import xyz.apiote.bimba.czwek.repo.TrafficResponseException
import xyz.apiote.bimba.czwek.units.DistanceUnit
import xyz.apiote.bimba.czwek.units.Km
import xyz.apiote.bimba.czwek.units.Metre
import java.util.Locale
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.cos

val MetresPerDegreeLatitude = Metre(111320.0)

suspend fun getTransitousQueryables(query: String, context: Context): List<Queryable> {
	if (!isNetworkAvailable(context)) {
		throw TrafficResponseException(0, "", Error(0, R.string.error_offline, R.drawable.error_net))
	}

	return withContext(Dispatchers.IO) {
		GeocodeApi().geocode(query, Locale.getDefault().language).filter { it.type == Match.Type.STOP }
			.map { Stop(it) }
	}
}

suspend fun locateTransitousQueryables(
	br: Position,
	tl: Position,
	context: Context
): List<Queryable> {
	if (!isNetworkAvailable(context)) {
		throw TrafficResponseException(0, "", Error(0, R.string.error_offline, R.drawable.error_net))
	}

	val dLat = abs(br.latitude - tl.latitude) / 2
	var dLon = abs(br.longitude - tl.longitude) / 2
	val centre = Position(abs(br.latitude + tl.latitude) / 2, abs(br.longitude + tl.longitude) / 2)

	val latitudeLimit = Km(10.0).meters()/MetresPerDegreeLatitude.meters() // ~radius in degrees latitude
	val corners = if (dLat > latitudeLimit) {
		dLon = dLon * latitudeLimit / dLat
		val newBr = Position(centre.latitude-latitudeLimit, centre.longitude+dLon)
		val newTL = Position(centre.latitude+latitudeLimit, centre.longitude-dLon)
		Pair(newBr.toString(), newTL.toString())
	} else {
		Pair(br.toString(), tl.toString())
	}

	return withContext(Dispatchers.IO) {
		MapApi().stops(corners.first, corners.second).filter { it.stopId != null }.map { Stop(it) }
	}
}

suspend fun locateTransitousQueryables(
	position: Position,
	context: Context,
	radius: DistanceUnit = Metre(500.0)
): List<Queryable> {
	if (!isNetworkAvailable(context)) {
		throw TrafficResponseException(0, "", Error(0, R.string.error_offline, R.drawable.error_net))
	}

	val deltaLatitude = radius.meters() / MetresPerDegreeLatitude.meters()
	val deltaLongitude = radius.meters() / (cos(position.latitude * PI / 180) / 360 * 40075000)
	val br = Position(position.latitude - deltaLatitude, position.longitude + deltaLongitude)
	val tl = Position(position.latitude + deltaLatitude, position.longitude - deltaLongitude)
	return locateTransitousQueryables(br, tl, context).map {
		when (it) {
			is Stop -> it
			else -> null
		}
	}.filterNotNull().sortedWith(Stop.distanceComparator(position))
}
