src/chronology/ui/DateConverter/DateConverterFormOptions.tsx
import React from 'react'
import DateConverter from 'chronology/domain/DateConverter'
import { weekDayNames, monthNames } from 'chronology/domain/DateConverterBase'
import { Field } from 'chronology/application/DateConverterFormFieldData'
import data from 'chronology/domain/dateConverterData.json'
type Edges = [number, number]
export default function getOptions({
field,
dateConverter,
}: {
field: Field
dateConverter: DateConverter
}): JSX.Element[] {
const optionsMap = {
Year: () => getYearOptions(field, dateConverter),
Month: () => getMonthOptions(field, dateConverter),
Day: () => getDayOptions(field, dateConverter),
ruler: () => getRulerOptions(dateConverter),
}
for (const key in optionsMap) {
if (field.name.includes(key)) {
return optionsMap[key]()
}
}
return []
}
const getValuesAtEdges = (
fieldName: string,
dateConverter: DateConverter
): Edges => {
return [
dateConverter.earliestDate[fieldName],
dateConverter.latestDate[fieldName],
]
}
const getAllFieldTypeEdges = (
field: Field,
dateConverter: DateConverter
): { yearEdges: Edges; monthEdges: Edges; dayEdges: Edges } => {
const prefixes = getDateFieldPrefixes(field)
const [yearEdges, monthEdges, dayEdges] = [
`${prefixes.yearPrefix}Year`,
`${prefixes.monthPrefix}Month`,
`${prefixes.dayPrefix}Day`,
].map((fieldName) => getValuesAtEdges(fieldName, dateConverter))
return { yearEdges, monthEdges, dayEdges }
}
const getFieldTypeYearAndMonth = (
field: Field,
dateConverter: DateConverter
): { year: number; month: number } => {
const prefixes = getDateFieldPrefixes(field)
const year = dateConverter.calendar[`${prefixes.yearPrefix}Year`]
const month = dateConverter.calendar[`${prefixes.yearPrefix}Month`]
return { year, month }
}
const getDateFieldPrefixes = (
field: Field
): { yearPrefix: string; monthPrefix: string; dayPrefix: string } => {
const toPlainPrefix = (prefix: string) => ({
yearPrefix: prefix,
monthPrefix: prefix,
dayPrefix: prefix,
})
if (field.name.includes('gregorian')) {
return toPlainPrefix('gregorian')
} else if (field.name.includes('julian')) {
return toPlainPrefix('julian')
} else {
return {
yearPrefix: 'seBabylonian',
monthPrefix: 'mesopotamian',
dayPrefix: 'mesopotamian',
}
}
}
function getLabelValueOptions(
options: { label: string | JSX.Element; value: number | string }[]
): JSX.Element[] {
return options.map(({ label, value }, index) => (
<option key={index} value={value}>
{label}
</option>
))
}
function getYearOptionLabel(
year: number,
era: 'western' | 'se' = 'western'
): string {
const { eraPrefix, beforeEraPrefix } = {
western: { eraPrefix: 'CE', beforeEraPrefix: 'BCE' },
se: { eraPrefix: 'SE', beforeEraPrefix: 'BSE' },
}[era]
return year < 1
? `${Math.abs(year) + 1} ${beforeEraPrefix}`
: `${year} ${eraPrefix}`
}
function getNumberRangeOptions(
from: number,
to: number,
labelFormatter?: (number) => string
): JSX.Element[] {
const numbersArray = Array.from(
{ length: to - from + 1 },
(_, index) => index + from
)
return numbersArray.map((number) => (
<option key={number} value={number}>
{labelFormatter ? labelFormatter(number) : number}
</option>
))
}
function getStringOptions(options: string[]): JSX.Element[] {
return options.map((label, index) => (
<option key={index} value={index + 1}>
{label}
</option>
))
}
const getYearOptions = (
field: Field,
dateConverter: DateConverter
): JSX.Element[] => {
const seYearLabelGetter = (number) => getYearOptionLabel(number, 'se')
const labelGetter =
field.name === 'seBabylonianYear' ? seYearLabelGetter : getYearOptionLabel
if (field.name !== 'regnalYear') {
return getNumberRangeOptions(
...getValuesAtEdges(field.name, dateConverter),
labelGetter
)
} else if (field.name === 'regnalYear') {
return getRegnalYearOptions(dateConverter)
} else {
return []
}
}
const getMonthOptions = (
field: Field,
dateConverter: DateConverter
): JSX.Element[] => {
const optionsMap: { [key: string]: () => JSX.Element[] } = {
gregorianMonth: () => getGregorianJulianMonthOptions(field, dateConverter),
julianMonth: () => getGregorianJulianMonthOptions(field, dateConverter),
mesopotamianMonth: () => getMesopotamianMonthOptions(field, dateConverter),
}
return optionsMap[field.name]?.() ?? []
}
const getDayOptions = (
field: Field,
dateConverter: DateConverter
): JSX.Element[] => {
if (field.name.includes('week')) {
return getStringOptions(weekDayNames)
}
const indexOffset = getDayOffset(field, dateConverter)
return getNumberRangeOptions(
1 + indexOffset[0],
indexOffset[1] ?? getMonthLength(field, dateConverter)
)
}
const getMonthLength = (field: Field, dateConverter: DateConverter): number => {
if (field.name.includes('mesopotamian')) {
return dateConverter.calendar.mesopotamianMonthLength ?? 30
} else {
return dateConverter.getMonthLength(field.name.includes('julian'))
}
}
const getMonthOffset = (
field: Field,
dateConverter: DateConverter
): number[] => {
let indexOffset = [0]
const { year } = getFieldTypeYearAndMonth(field, dateConverter)
const { yearEdges, monthEdges } = getAllFieldTypeEdges(field, dateConverter)
if (year === yearEdges[0]) {
indexOffset = [monthEdges[0] - 1]
} else if (year === yearEdges[1]) {
indexOffset = [0, monthEdges[1]]
}
return indexOffset
}
const getDayOffset = (field: Field, dateConverter: DateConverter): number[] => {
let indexOffset = [0]
const { year, month } = getFieldTypeYearAndMonth(field, dateConverter)
const { yearEdges, monthEdges, dayEdges } = getAllFieldTypeEdges(
field,
dateConverter
)
if (year === yearEdges[0] && month === monthEdges[0]) {
indexOffset = [dayEdges[0] - 1]
} else if (year === yearEdges[1] && month === monthEdges[1]) {
indexOffset = [0, dayEdges[1]]
}
return indexOffset
}
const getGregorianJulianMonthOptions = (
field: Field,
dateConverter: DateConverter
): JSX.Element[] => {
const indexOffset = getMonthOffset(field, dateConverter)
return getLabelValueOptions(
monthNames
.slice(...indexOffset)
.map((label) => ({ value: monthNames.indexOf(label) + 1, label }))
)
}
const getMesopotamianMonthOptions = (
field: Field,
dateConverter: DateConverter
): JSX.Element[] => {
const months = dateConverter.getMesopotamianMonthsOfSeYear(
dateConverter.calendar.seBabylonianYear
)
const indexOffset = getMonthOffset(field, dateConverter)
return getLabelValueOptions(
months.slice(...indexOffset).map(({ name, number, value }) => {
return {
label: `${number}. ${name}`,
value,
}
})
)
}
const getRegnalYearOptions = (dateConverter: DateConverter): JSX.Element[] => {
const { regnalYears } = dateConverter.calendar
return regnalYears ? getNumberRangeOptions(1, regnalYears) : []
}
const getRulerOptions = (dateConverter: DateConverter): JSX.Element[] => {
return getLabelValueOptions(
data.rulerName.map((name) => ({
value: name,
label: dateConverter.rulerToBrinkmanKings(name)?.name ?? name,
}))
)
}