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

package xyz.apiote.bimba.czwek.units

import android.content.Context
import xyz.apiote.bimba.czwek.R
import java.text.NumberFormat
import kotlin.math.abs
import kotlin.math.pow


class TGM(base: Int) : UnitSystem(base) {
	override fun timeUnit(count: Long): TimeUnit = Tim(count)
	override fun timeUnit(other: TimeUnit): TimeUnit = Tim(other)

	override fun speedUnit(count: Double): SpeedUnit = Vlos(count)
	override fun speedUnit(other: SpeedUnit): SpeedUnit = Vlos(other)

	override fun distanceUnit(count: Double): DistanceUnit = Grafut(count)
	override fun distanceUnit(other: DistanceUnit): DistanceUnit = Grafut(other)

	override fun toString(context: Context, s: SpeedUnit): String = s.toString(context, base)
	override fun toString(context: Context, t: TimeUnit): String = t.toString(context, base)
	override fun toString(context: Context, d: DistanceUnit): String = d.toString(context, base)
}

class Tim(val tims: Long) : TimeUnit {
	constructor(other: TimeUnit) : this((other.milliseconds() * 144 / 25 / 1000))

	override fun milliseconds(): Long = tims * 25 * 1000 / 144
	override fun toString(context: Context, base: Int): String {
		val res = if (tims < 0) {
			R.string.time_in_tm_past
		} else {
			R.string.time_in_tm
		}
		val (t, m) = if (tims > base.toDouble().pow(4)) {
			Pair(tims / base.toDouble().pow(4).toInt(), "⁴")
		} else {
			Pair(tims / base.toDouble().pow(2).toInt(), "²")
		}
		return if (base == 10) {
			context.getString(
				res,
				NumberFormat.getInstance().apply { maximumFractionDigits = 2 }.format(abs(t)),
				m
			)
		} else {
			context.getString(
				res,
				abs(t).toString(12).lowercase().replace("a", "↊").replace("b", "↋"),
				m
			)
		}
	}

	override fun contentDescription(context: Context, base: Int): String {
		return if (base == 10) {
			context.resources.getQuantityString(
				R.plurals.time_in_tm_cd,
				tims.toInt(),
				tims.toInt()
			)
		} else {
			if (tims > base.toDouble().pow(4)) {
				val t = tims / base.toDouble().pow(4)
				context.resources.getQuantityString(
					R.plurals.time_in_4tm_12_cd,
					t.toInt(),
					t.toInt().toDozenalString(context)
				)
			} else {
				val t = tims / base.toDouble().pow(2)
				context.resources.getQuantityString(
					R.plurals.time_in_2tm_12_cd,
					t.toInt(),
					t.toInt().toDozenalString(context)
				)
			}
		}
	}
}

class Grafut(val grafut: Double) : DistanceUnit {
	constructor(other: DistanceUnit) : this(other.meters() / .295682912)

	override fun meters(): Double = grafut * .295682912

	override fun toString(context: Context, base: Int): String {
		val (g, m) = if (grafut > base.toDouble().pow(3)) {
			Pair(grafut / base.toDouble().pow(3), "³")
		} else {
			Pair(grafut, "")
		}

		return if (base == 10) {
			context.getString(
				R.string.distance_in_gf,
				NumberFormat.getInstance().apply { maximumFractionDigits = 2 }.format(g),
				m
			)
		} else {
			context.getString(
				R.string.distance_in_gf,
				g.toString(12, 2).lowercase().replace("a", "↊").replace("b", "↋"),
				m
			)
		}
	}

	override fun contentDescription(context: Context, base: Int): String {
		return if (base == 10) {
			context.resources.getQuantityString(
				R.plurals.distance_in_gf_cd,
				grafut.toInt(),
				grafut.toInt()
			)
		} else {
			if (grafut > base.toDouble().pow(3)) {
				val g = grafut / base.toDouble().pow(3)
				context.resources.getQuantityString(
					R.plurals.distance_in_3gf_12_cd,
					g.toInt(),
					g.toInt().toDozenalString(context)
				)
			} else {
				context.resources.getQuantityString(
					R.plurals.distance_in_gf_12_cd,
					grafut.toInt(),
					grafut.toInt().toDozenalString(context)
				)
			}
		}
	}
}

class Vlos(val vlos: Double) : SpeedUnit {
	constructor(other: SpeedUnit) : this(other.mps() / 1.703133986928105)

	override fun mps(): Double = vlos * 1.703133986928105
	override fun contentDescription(context: Context, base: Int): String {
		return if (base == 10) {
			context.resources.getQuantityString(
				R.plurals.speed_in_vl_cd,
				vlos.toInt(),
				vlos.toInt()
			)
		} else {
			context.resources.getQuantityString(
				R.plurals.speed_in_vl_12_cd,
				vlos.toInt(),
				vlos.toInt().toDozenalString(context)
			)
		}
	}

	override fun toString(context: Context, base: Int): String {
		return context.getString(R.string.speed_in_vl, vlos.toString(base, 3).lowercase())
	}
}

fun Double.toString(radix: Int, precision: Int): String {
	var x = this
	var result = ""
	if (x < 0) {
		result = "-"
		x = -x
	}
	result += x.toInt().toString(radix)
	var frac = (x - x.toInt())
	var digits = 0
	while (frac > 0 && digits < precision) {
		if (digits == 0) result += if (radix == 12) "·" else NumberFormat.getInstance().apply { maximumFractionDigits = 1 }.format(0.5).replace(Regex("[0-9]"), "")
		frac *= radix
		result += frac.toInt().toString(radix)
		frac -= frac.toInt()
		digits++
	}
	return result.lowercase().replace("a", "↊").replace("b", "↋")
}

fun Int.toDozenalString(context: Context): String {
	if (this == 0) {
		return context.resources.getStringArray(R.array.dozenal_digits)[0]
	}
	val r = StringBuilder()
	var n = this
	val digits = context.resources.getStringArray(R.array.dozenal_digits)
	val multipliers = context.resources.getStringArray(R.array.dozenal_multipliers)
	var i = 0
	while (n > 0) {
		val u = n % 12
		if (u != 0) {
			r.insert(0, " ")
			r.insert(0, multipliers[i])
			r.insert(0, " ")
			r.insert(0, digits[u])
		}
		n /= 12
		i++
	}
	return r.toString().trim()
}