package app.furmeet.events.system.schedule.abstractDataTypes.managed

import app.furmeet.events.system.helper.TextualJoiner
import app.furmeet.events.system.helper.UncomplicatedObservable
import app.furmeet.events.system.helper.getString
import app.furmeet.events.system.helper.solved
import app.furmeet.events.system.schedule.abstractDataTypes.GregorianCalendarRange
import app.furmeet.events.system.schedule.abstractDataTypes.convention.Convention
import app.furmeet.events.system.schedule.abstractDataTypes.convention.Event
import java.io.Serializable

class AttendeeConFavorite(
        argConvention: Convention,
        argEvent: Event
) : UncomplicatedObservable<Any?>(), Serializable {

    private var internalName: String? = argEvent.titles.solved ?: ""
    private var internalRoomName: String? = TextualJoiner()[argEvent.places.map {
        it.names.solved ?: ""
    }]
    private var internalDone: Boolean? = false
    private var internalKind: Actions.Intention? = if (argEvent.isCustom) Actions.Intention.CUSTOM else Actions.Intention.SELECTION
    private var internalTimeRange: GregorianCalendarRange = argEvent.timeRange
    private var internalEdition: String? = argConvention.id
    private var internalUuid: String? = argEvent.uuid

    var name: String
        get() = internalName ?: ""
        set(value) = value.let { internalName = it; notifyObservers() }
    var roomName: String
        get() = internalRoomName ?: ""
        set(value) = value.let { internalRoomName = it; notifyObservers() }
    var kind: Actions.Intention
        get() = internalKind ?: Actions.Intention.CUSTOM
        set(value) = value.let { internalKind = it; notifyObservers() }
    var timeRange: GregorianCalendarRange
        get() = internalTimeRange
        set(value) = value.let { internalTimeRange = it; notifyObservers() }
    var edition: String
        get() = internalEdition ?: ""
        set(value) = value.let { internalEdition = it; notifyObservers() }
    var uuid: String
        get() = internalUuid ?: ""
        set(value) = value.let { internalUuid = it; notifyObservers() }
    var notified: Boolean
        get() = internalDone ?: false
        set(value) = value.let { internalDone = it; notifyObservers() }

    fun findInConvention(convention: Convention) = convention.events.firstOrNull { this.relatesTo(it) }
    private val kindStringInt
        get() = Actions.actionString(kind)
    private val doesNameAttributeMatter
        get() = kind in listOf(Actions.Intention.CUSTOM, Actions.Intention.SELECTION, Actions.Intention.ROOMPARTY)
    val isCustom
        get() = uuid.isEmpty()
    val isSecondaryStringPlace
        get() = kind == Actions.Intention.ROOMPARTY
    val isShareable
        get() = kind in listOf(Actions.Intention.ROOMPARTY)

    fun getString() =
            kindStringInt.let { if (doesNameAttributeMatter) name else it.getString() }

    fun getSecondaryString() =
            Actions.descriptionString(kind)?.getString() ?: roomName

    fun getSecondaryEditString() =
            Actions.descriptionEditString(kind)?.getString() ?: roomName


    override fun equals(other: Any?): Boolean {
        return when {
            other == null -> false
            AttendeeConFavorite::class.isInstance(other) -> {
                val that = other as AttendeeConFavorite
                if (
                        (this.uuid == that.uuid)
                        and
                        (this.edition == that.edition)
                        and
                        (this.timeRange == that.timeRange)
                        and
                        (this.kind == that.kind)
                )
                // if kind is custom, names must be the same to the object be the same
                    ((kind !in listOf(Actions.Intention.CUSTOM, Actions.Intention.ROOMPARTY)) or (this.name == that.name))
                else
                    false
            }
            else -> false
        }
    }

    fun relatesTo(that: Event): Boolean =
            (this.kind == Actions.Intention.SELECTION) and
                    (this.uuid == that.uuid) and
                    (this.timeRange == that.timeRange)

    override fun hashCode(): Int {
        var result = internalName.hashCode()
        result = 31 * result + internalRoomName.hashCode()
        result = 31 * result + internalDone.hashCode()
        result = 31 * result + internalKind.hashCode()
        result = 31 * result + internalTimeRange.hashCode()
        result = 31 * result + internalEdition.hashCode()
        result = 31 * result + internalUuid.hashCode()
        return result
    }
}