package com.vgmlr.stave
import android.content.Context
import android.content.Intent
import java.util.Locale
data class StaveImport(val name: String, val time: String, val entries: List<StaveEntry>)
object StaveText {
fun format(trip: StaveTrip, entries: List<StaveEntry>): String {
val rows = entries.asReversed()
val idx = rows.indices.map { "${it + 1}." }
val time = rows.map { it.stavetime }
val cost = rows.map { money(it.cost) }
val cat = rows.map { it.label }
val wIdx = idx.maxOfOrNull { it.length } ?: 0
val wTime = time.maxOfOrNull { it.length } ?: 0
val wCost = cost.maxOfOrNull { it.length } ?: 0
val wCat = cat.maxOfOrNull { it.length } ?: 0
return buildString {
append("Stave: ").append(trip.name).append('\n')
append("Total: ").append(money(rows.sumOf { it.cost })).append('\n')
if (rows.isNotEmpty()) append('\n')
rows.indices.forEach { i ->
val line = buildString {
append(idx[i].padStart(wIdx)).append(GAP)
append(time[i].padEnd(wTime)).append(GAP)
append(cat[i].padEnd(wCat)).append(GAP)
append(cost[i].padStart(wCost)).append(GAP)
append(rows[i].location)
}
append(line.trimEnd()).append('\n')
}
}
}
fun fileName(trip: StaveTrip): String =
"stave_${slug(trip.name)}_${StaveOTCClock.getFileNameTimestamp()}.tsv"
// oldest first
fun toTsv(trip: StaveTrip, entries: List<StaveEntry>): String = buildString {
append(MAGIC).append('\t').append(FORMAT).append('\n')
append(clean(trip.name)).append('\t').append(clean(trip.stavetime)).append('\n')
append(TSV_HEAD).append('\n')
entries.asReversed().forEach { e ->
listOf(
clean(e.stavetime),
clean(e.category),
String.format(Locale.US, "%.2f", e.cost),
clean(e.note),
clean(e.location),
e.lat?.toString().orEmpty(),
e.lng?.toString().orEmpty()
).joinTo(this, "\t")
append('\n')
}
}
fun parseTsv(text: String): StaveImport? {
val lines = text.lineSequence().filter { it.isNotEmpty() }.toList()
if (lines.size < 2 || lines[0] != "$MAGIC\t$FORMAT") return null
val head = lines[1].split('\t')
val entries = lines.drop(2).filterNot { it.startsWith("#") }.mapNotNull { line ->
val f = line.split('\t')
val cost = f.getOrNull(2)?.toDoubleOrNull()
if (f.size < COLS || cost == null) null
else StaveEntry(
id = 0,
tripId = 0,
cost = cost,
category = f[1],
location = f[4],
stavetime = f[0],
lat = f[5].toDoubleOrNull(),
lng = f[6].toDoubleOrNull(),
note = f[3]
)
}
return StaveImport(head[0], head.getOrElse(1) { "" }, entries)
}
private fun clean(s: String): String = s.replace(TAB_BREAK, " ")
private fun slug(name: String): String =
name.trim().replace(Regex("[^A-Za-z0-9]+"), "-").trim('-').ifBlank { "trip" }
private const val GAP = " "
private const val MAGIC = "STAVE"
private const val FORMAT = 1
private const val COLS = 7
private const val TSV_HEAD = "# time\tcategory\tcost\tnote\tlocation\tlat\tlng"
private val TAB_BREAK = Regex("[\t\r\n]")
}
fun shareStave(context: Context, trip: StaveTrip, entries: List<StaveEntry>) {
val send = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_SUBJECT, trip.name)
putExtra(Intent.EXTRA_TEXT, StaveText.format(trip, entries))
}
context.startActivity(Intent.createChooser(send, null))
}