package space.nstart.pcp.complan.apl

import org.locationtech.jts.geom.Coordinate
import org.locationtech.jts.geom.CoordinateFilter
import org.locationtech.jts.geom.LineSegment
import org.locationtech.jts.geom.LineString
import kotlin.collections.get

/**
 * Класс для "сдвига" линии на заданную координату по X, не меняя направление сегментов
 * Поиск значений координат на заданном отрезке по X и Y
 * */
class LineShifter {

    /**
     * Сдвигает линию на заданное значение X
     * Если линия состоит менее чем из 2-х точек, возвращает null
     * */
    fun lineShift(line : LineString, xValue : Double) : LineString? {
        if (line.coordinates.size < 2) { return null }

        val copy = line.copy() as LineString

        // Применяем фильтр, который конвертирует радианы → градусы
        copy.apply(CoordinateFilter { coord ->
            coord.x = coord.x + xValue  // долгота
            coord.y = coord.y           // широта
        })

        return copy
    }

    /**
     * Находит первую точку на полилинии, лежащую на заданной координате Y
     * Возвращает null, если значение Y лежит вне проекции ее на отрезок
     * Например отрезок LineSegment(Coordinate(x=0.0,y=0.0), Coordinate(x=10.0,y=5.0))
     * y должен принадлежать [0.0, 5.0], иначе null
     * */
    fun getPointOnLineY(line : LineString, yValue : Double) : Coordinate? {
        if (line.coordinates.size < 2) { return null }
        for (i in 1..<line.coordinates.count()) {
            val lineSegment = LineSegment(line.coordinates[i-1], line.coordinates[i])
            val needPoint = getPointOnLineY(lineSegment, yValue)
            if (needPoint != null) {
                return needPoint
            }
        }
        return null
    }
    /**
     * Находит точку на отрезке, лежащую на заданной координате Y
     * Возвращает null, если значение Y лежит вне проекции ее на отрезок
     * Например отрезок LineSegment(Coordinate(x=0.0,y=0.0), Coordinate(x=10.0,y=5.0))
     * y должен принадлежать [0.0, 5.0], иначе null
     * */
    private fun getPointOnLineY(line : LineSegment, yValue : Double) : Coordinate? {
        // Случай: отрезок горизонтальный
        if (line.p0.y == line.p1.y) {
            return if (yValue == line.p0.y) {
                // Можно вернуть среднюю X или любую в диапазоне
                Coordinate((line.p0.x + line.p1.x) / 2.0, yValue)
            } else {
                null // нет пересечения
            }
        }

        // Проверяем, лежит ли targetY между y1 и y2
        val minY = minOf(line.p0.y, line.p1.y)
        val maxY = maxOf(line.p0.y, line.p1.y)

        if (yValue !in minY..maxY) {
            return null // Y вне отрезка
        }

        // Линейная интерполяция
        val t = (yValue - line.p0.y) / (line.p1.y - line.p0.y)
        return Coordinate(line.p0.x + t * (line.p1.x - line.p0.x), yValue)
    }
}
