package com.vgmlr.wedge
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
object WedgeHeader {
private data class Tok(val text: String, val literal: Boolean)
private fun tokenize(fmt: String): List<Tok> {
val out = mutableListOf<Tok>()
var i = 0
while (i < fmt.length) {
if (fmt.regionMatches(i, "otcDay", 0, 6)) { out.add(Tok("otcDay", false)); i += 6; continue }
if (fmt.regionMatches(i, "otcYear", 0, 7)) { out.add(Tok("otcYear", false)); i += 7; continue }
val c = fmt[i]
var j = i
if (c.isLetter()) {
while (j < fmt.length && fmt[j] == c) j++
out.add(Tok(fmt.substring(i, j), false))
} else {
while (j < fmt.length && !fmt[j].isLetter()) j++
out.add(Tok(fmt.substring(i, j), true))
}
i = j
}
return out
}
private fun isDayTok(t: String) = t.equals("dd", true) || t.equals("d", true)
private fun fmtDate(pattern: String, timestamp: Long): String =
SimpleDateFormat(pattern, Locale.US).format(Date(timestamp))
private fun tokValue(t: String, timestamp: Long): String {
val parts = WedgeDate.getOtcParts(timestamp)
return when {
t == "otcDay" -> parts.first.toString()
t == "otcYear" -> "0" + String.format(Locale.US, "%02d", parts.second)
t == "EEEE" -> fmtDate("EEEE", timestamp).lowercase(Locale.US)
t == "EEE" -> fmtDate("EEE", timestamp).lowercase(Locale.US)
t == "MMMM" -> fmtDate("MMMM", timestamp).lowercase(Locale.US)
t == "MMM" -> fmtDate("MMM", timestamp).lowercase(Locale.US)
t == "MM" -> fmtDate("MM", timestamp)
t == "M" -> fmtDate("M", timestamp)
t.equals("dd", true) -> fmtDate("dd", timestamp)
t.equals("d", true) -> fmtDate("d", timestamp)
t == "yyyy" -> fmtDate("yyyy", timestamp)
t == "yy" -> fmtDate("yy", timestamp)
else -> t
}
}
private fun litPattern(s: String): String {
val sb = StringBuilder()
var i = 0
while (i < s.length) {
var j = i
if (s[i].isWhitespace()) {
while (j < s.length && s[j].isWhitespace()) j++
sb.append("\\s+")
} else {
while (j < s.length && !s[j].isWhitespace()) j++
sb.append(Regex.escape(s.substring(i, j)))
}
i = j
}
return sb.toString()
}
private fun tokPattern(t: String): String = when {
t == "otcDay" -> "(?<otc>\\d+)"
t == "otcYear" -> "\\d{1,4}"
t.startsWith("E") || t == "MMM" || t == "MMMM" -> "[a-zA-Z]{3,}"
t == "MM" || t == "M" -> "\\d{1,2}"
isDayTok(t) -> "0?\\d{1,2}"
t == "yyyy" -> "\\d{4}"
t == "yy" -> "\\d{2}"
else -> Regex.escape(t)
}
fun render(format: String, timestamp: Long): String {
val sb = StringBuilder()
for (t in tokenize(format)) sb.append(if (t.literal) t.text else tokValue(t.text, timestamp))
return sb.toString()
}
fun matchRegex(format: String): Regex {
val toks = tokenize(format)
val sb = StringBuilder("^")
for (i in toks.indices) {
val t = toks[i]
if (!t.literal) { sb.append(tokPattern(t.text)); continue }
val lp = litPattern(t.text)
sb.append(if (i == toks.size - 1) "(?:$lp)?" else lp)
}
sb.append(".*")
return Regex(sb.toString(), setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE))
}
fun dateRegex(format: String, timestamp: Long): Regex {
val toks = tokenize(format)
val sb = StringBuilder("^")
for (i in toks.indices) {
val t = toks[i]
when {
t.literal -> {
val lp = litPattern(t.text)
sb.append(if (i == toks.size - 1) "(?:$lp)?" else lp)
}
isDayTok(t.text) -> {
val cal = Calendar.getInstance().apply { timeInMillis = timestamp }
sb.append("0?" + Regex.escape(cal.get(Calendar.DAY_OF_MONTH).toString()))
}
else -> sb.append(Regex.escape(tokValue(t.text, timestamp)))
}
}
sb.append(".*")
return Regex(sb.toString(), setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE))
}
fun otcDayFromMatch(match: MatchResult): Int? = match.groups["otc"]?.value?.toIntOrNull()
}