package ch.dissem.yaep.domain

enum class Animal(symbol: String) : ItemClass<Animal> {
    ANT("🐜"),
    DOG("🐕"),
    GOAT("🐐"),
    OCTOPUS("🐙"),
    SLOTH("🦥"),
    SNAIL("🐌"),
    ZEBRA("🦓");

    override val symbols: Array<String> = arrayOf(symbol)

    override val companion: Animal.Companion
        get() = Animal

    companion object : ItemClassCompanion<Animal> {
        override val items: List<Animal> = entries
    }
}

enum class Nationality(symbol: String) : ItemClass<Nationality> {
    CANADA("🇨🇦"),
    JAPAN("🇯🇵"),
    NORWAY("🇳🇴"),
    SPAIN("🇪🇸"),
    SWEDEN("🇸🇪"),
    SWITZERLAND("🇨🇭"),
    UNITED_KINGDOM("🇬🇧"),
    UKRAINE("🇺🇦");

    override val symbols: Array<String> = arrayOf(symbol)

    override val companion: Nationality.Companion
        get() = Nationality

    companion object : ItemClassCompanion<Nationality> {
        override val items: List<Nationality> = entries
    }
}

enum class Drink(symbol: String) : ItemClass<Drink> {
    BEVERAGE("🧃"),
    BUBBLE_TEA("🧋"),
    COFFEE("☕"),
    MATE("🧉"),
    MILK("🥛"),
    TEA("🍵"),
    WATER("🥤");

    override val symbols: Array<String> = arrayOf(symbol)

    override val companion: Drink.Companion
        get() = Drink

    companion object : ItemClassCompanion<Drink> {
        override val items: List<Drink> = entries
    }
}

enum class Profession(symbol: String) : ItemClass<Profession> {
    ASTRONAUT("\u200D\uD83D\uDE80"),
    FARMER("\u200D\uD83C\uDF3E"),
    FIREFIGHTER("\u200D\uD83D\uDE92"),
    HEALTH_WORKER("\u200D⚕\uFE0F"),
    ROCK_STAR("\u200D\uD83C\uDFA4"),
    SCIENTIST("\u200D\uD83D\uDD2C"),
    SOFTWARE_DEV("\u200D\uD83D\uDCBB"),
    TEACHER("\u200D\uD83C\uDFEB");

    override val symbols: Array<String> = idic(symbol)

    override val companion: Profession.Companion
        get() = Profession

    companion object : ItemClassCompanion<Profession> {
        override val items: List<Profession> = entries
    }
}

enum class Fruit(symbol: String) : ItemClass<Fruit> {
    BANANA("🍌"),
    CHERRIES("🍒"),
    GRAPES("🍇"),
    KIWI("🥝"),
    LEMON("🍋"),
    MANGO("🥭"),
    PEAR("🍐"),
    PINEAPPLE("🍍"),
    STRAWBERRY("🍓"),
    WATERMELON("🍉");

    override val symbols: Array<String> = arrayOf(symbol)

    override val companion: Fruit.Companion
        get() = Fruit

    companion object : ItemClassCompanion<Fruit> {
        override val items: List<Fruit> = entries
    }
}

enum class Dessert(symbol: String) : ItemClass<Dessert> {
    CAKE("🍰"),
    CHOCOLATE("🍫"),
    COOKIE("🍪"),
    CUPCAKE("🧁"),
    CUSTARD("🍮"),
    DOUGHNUT("🍩"),
    ICE_CREAM("🍨"),
    LOLLIPOP("🍭"),
    PIE("🥧");

    override val symbols: Array<String> = arrayOf(symbol)

    override val companion: Dessert.Companion
        get() = Dessert

    companion object : ItemClassCompanion<Dessert> {
        override val items: List<Dessert> = entries
    }
}

enum class Transportation(symbol: String) : ItemClass<Transportation> {
    BICYCLE("🚲"),
    BUS("🚌"),
    LOCOMOTIVE("🚂"),
    MOTOR_SCOOTER("🛵"),
    SKATEBOARD("🛹"),
    TAXI("🚕"),
    TRAM_CAR("🚋");

    override val symbols: Array<String> = arrayOf(symbol)

    override val companion: Transportation.Companion
        get() = Transportation

    companion object : ItemClassCompanion<Transportation> {
        override val items: List<Transportation> = entries
    }
}

private val GENDERS = arrayOf("\uD83E\uDDD1", "\uD83D\uDC68", "\uD83D\uDC69")
private val SKIN_TONES =
    arrayOf("\uD83C\uDFFB", "\uD83C\uDFFC", "\uD83C\uDFFD", "\uD83C\uDFFE", "\uD83C\uDFFF")

private fun idic(symbol: String): Array<String> = Array(GENDERS.size * SKIN_TONES.size) { i ->
    val g = GENDERS[i % GENDERS.size]
    val t = SKIN_TONES[i / GENDERS.size]
    g + t + symbol
}

sealed interface ItemClass<out SELF : ItemClass<SELF>> : Comparable<ItemClass<*>> {
    val symbols: Array<String>

    val name: String

    val companion: ItemClassCompanion<SELF>

    val ordinal: Int

    override fun compareTo(other: ItemClass<*>): Int = ordinal.compareTo(other.ordinal)

    companion object {
        val classes: List<ItemClassCompanion<*>> = listOf(
            Nationality,
            Profession,
            Drink,
            Dessert,
            Fruit,
            Transportation,
            Animal
        )

        fun randomClasses(n: Int): List<ItemClassCompanion<*>> {
            return classes.shuffled().take(n).sortedBy { classes.indexOf(it) }
        }

        fun parse(name: String): ItemClass<*> {
            return classes.mapNotNull { it.parse(name) }.single()
        }
    }
}

sealed interface ItemClassCompanion<out C : ItemClass<C>> {
    val items: List<C>

    fun randomItems(n: Int): List<C> {
        return items.shuffled().take(n)
    }

    fun parse(name: String): C? {
        return items.firstOrNull { it.name == name }
    }
}
