aboutsummaryrefslogtreecommitdiff
path: root/mod/src/main/kotlin/moe/nea/ledger/NumberUtil.kt
blob: fa295b09f1d4cb42521dec7dda9c1f55b67a903c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package moe.nea.ledger

import net.minecraft.event.ClickEvent
import net.minecraft.event.HoverEvent
import net.minecraft.util.ChatComponentText
import net.minecraft.util.ChatStyle
import net.minecraft.util.EnumChatFormatting
import net.minecraft.util.IChatComponent
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder
import java.time.temporal.ChronoField
import java.util.regex.Matcher
import java.util.regex.Pattern

// language=regexp
val SHORT_NUMBER_PATTERN = "[0-9]+(?:,[0-9]+)*(?:\\.[0-9]+)?[kKmMbB]?"

// language=regexp
val ROMAN_NUMBER_PATTERN = "[IVXLCDM]+"

val romanNumbers = mapOf(
	'I' to 1,
	'V' to 5,
	'X' to 10,
	'L' to 50,
	'C' to 100,
	'D' to 500,
	'M' to 1000
)

fun parseRomanNumber(string: String): Int {
	var smallestSeenSoFar = Int.MAX_VALUE
	var lastSeenOfSmallest = 0
	var amount = 0
	for (c in string) {
		val cV = romanNumbers[c]!!
		if (cV == smallestSeenSoFar) {
			lastSeenOfSmallest++
			amount += cV
		} else if (cV < smallestSeenSoFar) {
			smallestSeenSoFar = cV
			amount += cV
			lastSeenOfSmallest = 1
		} else {
			amount -= lastSeenOfSmallest * smallestSeenSoFar * 2
			smallestSeenSoFar = cV
			amount += cV
			lastSeenOfSmallest = 1
		}
	}
	return amount
}

val siScalars = mapOf(
	'k' to 1_000.0,
	'K' to 1_000.0,
	'm' to 1_000_000.0,
	'M' to 1_000_000.0,
	'b' to 1_000_000_000.0,
	'B' to 1_000_000_000.0,
)

fun parseShortNumber(string: String): Double {
	var k = string.replace(",", "")
	val scalar = k.last()
	var scalarMultiplier = siScalars[scalar]
	if (scalarMultiplier == null) {
		scalarMultiplier = 1.0
	} else {
		k = k.dropLast(1)
	}
	return k.toDouble() * scalarMultiplier
}

fun Pattern.matches(string: String): Boolean = matcher(string).matches()
inline fun <T> Pattern.useMatcher(string: String, block: Matcher.() -> T): T? =
	matcher(string).takeIf { it.matches() }?.let(block)

fun <T> String.ifDropLast(suffix: String, block: (String) -> T): T? {
	if (endsWith(suffix)) {
		return block(dropLast(suffix.length))
	}
	return null
}

fun String.unformattedString(): String = replace("§.".toRegex(), "")

val timeFormat: DateTimeFormatter = DateTimeFormatterBuilder()
	.appendValue(ChronoField.DAY_OF_MONTH, 2)
	.appendLiteral(".")
	.appendValue(ChronoField.MONTH_OF_YEAR, 2)
	.appendLiteral(".")
	.appendValue(ChronoField.YEAR, 4)
	.appendLiteral(" ")
	.appendValue(ChronoField.HOUR_OF_DAY, 2)
	.appendLiteral(":")
	.appendValue(ChronoField.MINUTE_OF_HOUR, 2)
	.appendLiteral(":")
	.appendValue(ChronoField.SECOND_OF_MINUTE, 2)
	.toFormatter()

fun Instant.formatChat(): IChatComponent {
	val text = ChatComponentText(
		LocalDateTime.ofInstant(this, ZoneId.systemDefault()).format(timeFormat)
	)
	text.setChatStyle(
		ChatStyle()
			.setChatClickEvent(
				ClickEvent(ClickEvent.Action.OPEN_URL, "https://time.is/${this.epochSecond}"))
			.setChatHoverEvent(
				HoverEvent(HoverEvent.Action.SHOW_TEXT, ChatComponentText("Click to show on time.is")))
			.setColor(EnumChatFormatting.AQUA))
	return text
}

private val formatChatDirection = run {
	fun ItemChange.ChangeDirection.formatChat0(): IChatComponent {
		val (text, color) = when (this) {
			ItemChange.ChangeDirection.GAINED -> "+" to EnumChatFormatting.GREEN
			ItemChange.ChangeDirection.TRANSFORM -> "~" to EnumChatFormatting.YELLOW
			ItemChange.ChangeDirection.SYNC -> "=" to EnumChatFormatting.BLUE
			ItemChange.ChangeDirection.CATALYST -> "*" to EnumChatFormatting.DARK_PURPLE
			ItemChange.ChangeDirection.LOST -> "-" to EnumChatFormatting.RED
		}
		return ChatComponentText(text)
			.setChatStyle(
				ChatStyle()
					.setColor(color)
					.setChatHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT,
					                              ChatComponentText(name).setChatStyle(ChatStyle().setColor(color)))))
	}
	ItemChange.ChangeDirection.entries.associateWith { it.formatChat0() }
}

fun ItemChange.ChangeDirection.formatChat(): IChatComponent {
	return formatChatDirection[this]!!
}

fun ItemChange.formatChat(): IChatComponent {
	return ChatComponentText(" ")
		.appendSibling(direction.formatChat())
		.appendText(" ")
		.appendSibling(ChatComponentText("$count").setChatStyle(ChatStyle().setColor(EnumChatFormatting.WHITE)))
		.appendSibling(ChatComponentText("x").setChatStyle(ChatStyle().setColor(EnumChatFormatting.DARK_GRAY)))
		.appendText(" ")
		.appendSibling(ChatComponentText(itemId.string).setChatStyle(ChatStyle().setParentStyle(ChatStyle().setColor(
			EnumChatFormatting.WHITE))))
}