@file:JvmName("FinanceJSONSupport")

package net.corda.finance.plugin

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParseException
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer
import com.fasterxml.jackson.databind.module.SimpleModule
import net.corda.finance.contracts.BusinessCalendar
import java.time.LocalDate

fun registerFinanceJSONMappers(objectMapper: ObjectMapper): Unit {
    val financeModule = SimpleModule("finance").apply {
        addSerializer(BusinessCalendar::class.java, CalendarSerializer)
        addDeserializer(BusinessCalendar::class.java, CalendarDeserializer)
    }
    objectMapper.registerModule(financeModule)
}

data class BusinessCalendarWrapper(val holidayDates: List<LocalDate>) {
    fun toCalendar() = BusinessCalendar(holidayDates)
}

object CalendarSerializer : JsonSerializer<BusinessCalendar>() {
    override fun serialize(obj: BusinessCalendar, generator: JsonGenerator, context: SerializerProvider) {
        val calendarName = BusinessCalendar.calendars.find { BusinessCalendar.getInstance(it) == obj }
        if (calendarName != null) {
            generator.writeString(calendarName)
        } else {
            generator.writeObject(BusinessCalendarWrapper(obj.holidayDates))
        }
    }
}

object CalendarDeserializer : JsonDeserializer<BusinessCalendar>() {
    override fun deserialize(parser: JsonParser, context: DeserializationContext): BusinessCalendar {
        return try {
            try {
                val array = StringArrayDeserializer.instance.deserialize(parser, context)
                BusinessCalendar.getInstance(*array)
            } catch (e: Exception) {
                parser.readValueAs(BusinessCalendarWrapper::class.java).toCalendar()
            }
        } catch (e: Exception) {
            throw JsonParseException(parser, "Invalid calendar(s) ${parser.text}: ${e.message}")
        }
    }
}
