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

package xyz.apiote.bimba.czwek.repo

import android.content.Context
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.os.Parcelable
import android.text.Annotation
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.SpannedString
import android.text.style.StyleSpan
import androidx.preference.PreferenceManager
import kotlinx.parcelize.Parcelize
import xyz.apiote.bimba.czwek.R
import xyz.apiote.bimba.czwek.RoundedBackgroundSpan
import xyz.apiote.bimba.czwek.api.StopV1
import xyz.apiote.bimba.czwek.api.StopV2
import xyz.apiote.bimba.czwek.api.StopV3
import xyz.apiote.bimba.czwek.api.transitous.model.Match
import xyz.apiote.bimba.czwek.api.transitous.model.Place


@Parcelize
data class Stop(
	val code: String,
	val name: String,
	val nodeName: String,
	val zone: String,
	val feedID: String?,
	val position: Position,
	val changeOptions: List<ChangeOption>,
	val description: String?
) : Queryable, Locatable, StopAbstract, Parcelable {
	companion object {
		fun distanceComparator(centre: Position) = object : Comparator<Stop> {
			override fun compare(
				o1: Stop?,
				o2: Stop?
			): Int {
				return Position.comparator(centre).compare(o1?.location(), o2?.location())
			}

		}
	}

	override fun icon(context: Context, scale: Float): Drawable {
		return super.icon(context, nodeName, scale)
	}

	override fun id(): String = code

	override fun location(): Position = position

	constructor(s: StopV1) : this(
		s.code,
		s.name,
		s.name,
		s.zone,
		null,
		Position(s.position),
		s.changeOptions.map { ChangeOption(it) },
		null
	)

	constructor(s: StopV2) : this(
		s.code,
		s.name,
		s.nodeName,
		s.zone,
		s.feedID,
		Position(s.position),
		s.changeOptions.map { ChangeOption(it) },
		null
	)

	constructor(s: StopV3) : this(
		s.code,
		s.name,
		s.nodeName,
		s.zone,
		s.feedID,
		Position(s.position),
		s.changeOptions.map { ChangeOption(it) },
		null
	)

	constructor(s: Match) : this(
		s.id,
		s.name,
		s.areas.sortedBy { it.adminLevel }.map { it.name }.distinct().joinToString() + s.name,
		"",
		"transitous",
		Position(s.lat.toDouble(), s.lon.toDouble()),
		emptyList(),
		s.areas.sortedBy { it.adminLevel }.map { it.name }.distinct().joinToString()
	)

	constructor(s: Place) : this(
		s.stopId ?: "",
		s.name,
		s.name,
		"",
		"transitous",
		Position(s.lat.toDouble(), s.lon.toDouble()),
		emptyList(),
		""
	)

	fun changeOptions(context: Context, decoration: LineDecoration): Pair<Spannable, String> {
		return Pair(changeOptions.groupBy { it.line }
			.map {
				Pair(
					it.key,
					it.value.flatMap { co -> co.headsigns }.sortedBy { headsign -> headsign }.joinToString()
				)
			}.fold(SpannableStringBuilder("")) { acc, p ->
				if (acc.toString() != "") {
					acc.append("; ")
				}
				var str = SpannableStringBuilder(
					context.getText(
						R.string.vehicle_headsign
					) as SpannedString
				)
				str = applyAnnotations(str, decoration, p.first, p.first.name, p.second)
				str = applyAnnotations(str, decoration, p.first)
				acc.append(str)
				acc
			},
			changeOptions.groupBy { it.line }
				.map {
					Pair(
						it.key,
						it.value.flatMap { co -> co.headsigns }.sortedBy { headsign -> headsign }.joinToString()
					)
				}.joinToString {
					context.getString(
						R.string.vehicle_headsign_content_description, it.first, it.second
					)
				})
	}

	private fun applyAnnotations(
		s: SpannableStringBuilder,
		decoration: LineDecoration,
		line: LineStub,
		vararg args: Any
	): SpannableStringBuilder {
		val str = SpannableStringBuilder(s)
		val annotations = str.getSpans(0, str.length, Annotation::class.java)
		annotations.forEach {
			when (it.key) {
				"arg" -> {
					if (args.isEmpty()) {
						return@forEach
					}
					val argIndex = Integer.parseInt(it.value)
					str.replace(str.getSpanStart(it), str.getSpanEnd(it), args[argIndex] as String)
				}

				"decoration" -> {
					if (args.isNotEmpty()) {
						return@forEach
					}
					// TODO rounded corners/padding
					val background = RoundedBackgroundSpan(line.colour.toInt(), line.textColour(line.colour))
					val ital = StyleSpan(Typeface.ITALIC)
					when (decoration) {
						LineDecoration.ITALICS -> str.setSpan(
							ital,
							str.getSpanStart(it),
							str.getSpanEnd(it),
							Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
						)

						LineDecoration.COLOUR -> {
							str.setSpan(
								background,
								str.getSpanStart(it),
								str.getSpanEnd(it),
								Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
							)
							// str.setSpan(foreground, str.getSpanStart(it), str.getSpanEnd(it), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
						}

						LineDecoration.NONE -> {}
					}
				}
			}
		}
		return str
	}

	fun changeOptionsString(): String = changeOptions.groupBy { it.line }
		.map {
			Pair(
				it.key,
				it.value.flatMap { co -> co.headsigns }.sortedBy { headsign -> headsign }.joinToString()
			)
		}.joinToString("; ")

	override fun toString(): String {
		return "$name ($code) [$zone] $position\n${changeOptionsString()}"
	}

	enum class LineDecoration {
		NONE, ITALICS, COLOUR;

		companion object {
			fun fromPreferences(context: Context) =
				when (PreferenceManager.getDefaultSharedPreferences(context)
					.getString("line_decoration", "italics")) {
					"italics" -> Stop.LineDecoration.ITALICS
					"colour" -> Stop.LineDecoration.COLOUR
					"none" -> Stop.LineDecoration.NONE
					else -> Stop.LineDecoration.ITALICS
				}
		}
	}
}

