package net.taehui.twilight

import com.fasterxml.jackson.annotation.JsonValue

class Component {
    var millisLoopUnit = 0.0
    var levyingHeight: Double
    var logicalYMillis = 0.0
    private var logicalYLoopUnit = 0.0
    var logicalYMeter = 0.0
    var millisMeter = 0.0

    constructor(bpm: Double, loopUnit: Int) {
        millisLoopUnit = 1000.0 / loopUnit
        setBPM(bpm)
        levyingHeight = STANDARD_HEIGHT - logicalYMillis * LEVYING_WAIT
    }

    constructor(bpm: Double) {
        setBPM(bpm)
        levyingHeight = STANDARD_HEIGHT - logicalYMillis * LEVYING_WAIT
    }

    fun setBPM(bpm: Double) {
        logicalYMeter = 192.0
        millisMeter = 240000.0 / bpm
        logicalYMillis = logicalYMeter / millisMeter
        logicalYLoopUnit = millisLoopUnit * logicalYMillis
    }

    enum class JudgmentAssist {
        DEFAULT, LONG_NOTE_UP
    }

    enum class InputMode(@JvmField val value: Int) {
        INPUT_MODE_4(4),
        INPUT_MODE_5(5),
        INPUT_MODE_6(6),
        INPUT_MODE_7(7),
        INPUT_MODE_8(8),
        INPUT_MODE_9(9),
        INPUT_MODE_5_1(10),
        INPUT_MODE_7_1(11),
        INPUT_MODE_10_2(12),
        INPUT_MODE_14_2(13),
        INPUT_MODE_10(14),
        INPUT_MODE_24_2(15),
        INPUT_MODE_48_4(16);

        @JsonValue
        fun getValue(): Int {
            return value
        }
    }

    enum class Judged(@JvmField val value: Int) {
        NOT(-1),
        HIGHEST(0),
        HIGHER(1),
        HIGH(2),
        LOW(3),
        LOWER(4),
        LOWEST(5);

        @JsonValue
        fun getValue(): Int {
            return value
        }
    }

    enum class NoteVariety(@JvmField val value: Int) {
        BMS(1),
        BMSON(3),
        EVENT_NOTE(5);

        @JsonValue
        fun getValue(): Int {
            return value
        }
    }

    companion object {
        const val STANDARD_HEIGHT = 720.0
        const val STANDARD_BPM = 130.0
        const val STANDARD_METER = 192
        const val LEVYING_WAIT = 3000.0
        const val QUIT_WAIT = 2000.0
        private val JUDGMENT_MAP = arrayOfNulls<DoubleArray>(6)
        private val IS_IN_2P = arrayOfNulls<BooleanArray>(17)
        val STAND_MAP = DoubleArray(6)
        val POINT_MAP = DoubleArray(6)
        val HIT_POINTS_MAP = DoubleArray(6)
        val INPUT_COUNTS = IntArray(17)
        val AUTOABLE_INPUTS = arrayOfNulls<IntArray>(17)
        val NET_DRAWINGS = arrayOfNulls<IntArray>(17)

        init {
            HIT_POINTS_MAP[Judged.HIGHEST.value] = 1.0
            HIT_POINTS_MAP[Judged.HIGHER.value] = 0.2 / 3
            HIT_POINTS_MAP[Judged.HIGH.value] = 0.1 / 3
            HIT_POINTS_MAP[Judged.LOW.value] = 0.1
            HIT_POINTS_MAP[Judged.LOWER.value] = 0.0
            HIT_POINTS_MAP[Judged.LOWEST.value] = -0.1 / 3
            JUDGMENT_MAP[Judged.HIGHEST.value] = doubleArrayOf(-25.0, 25.0)
            JUDGMENT_MAP[Judged.HIGHER.value] = doubleArrayOf(-60.0, 60.0)
            JUDGMENT_MAP[Judged.HIGH.value] = doubleArrayOf(-95.0, 95.0)
            JUDGMENT_MAP[Judged.LOW.value] = doubleArrayOf(-130.0, 130.0)
            JUDGMENT_MAP[Judged.LOWER.value] = doubleArrayOf(-165.0, 165.0)
            JUDGMENT_MAP[Judged.LOWEST.value] = doubleArrayOf(-200.0, 200.0)
            POINT_MAP[Judged.HIGHEST.value] = 1.0
            POINT_MAP[Judged.HIGHER.value] = 1.0
            POINT_MAP[Judged.HIGH.value] = 0.7
            POINT_MAP[Judged.LOW.value] = 0.5
            POINT_MAP[Judged.LOWER.value] = 0.3
            POINT_MAP[Judged.LOWEST.value] = 0.0
            STAND_MAP[Judged.HIGHEST.value] = 1.0
            STAND_MAP[Judged.HIGHER.value] = 0.9
            STAND_MAP[Judged.HIGH.value] = 0.1
            STAND_MAP[Judged.LOW.value] = 0.01
            STAND_MAP[Judged.LOWER.value] = 0.0
            STAND_MAP[Judged.LOWEST.value] = 0.0
            INPUT_COUNTS[InputMode.INPUT_MODE_4.value] = 4
            INPUT_COUNTS[InputMode.INPUT_MODE_5.value] = 5
            INPUT_COUNTS[InputMode.INPUT_MODE_6.value] = 6
            INPUT_COUNTS[InputMode.INPUT_MODE_7.value] = 7
            INPUT_COUNTS[InputMode.INPUT_MODE_8.value] = 8
            INPUT_COUNTS[InputMode.INPUT_MODE_9.value] = 9
            INPUT_COUNTS[InputMode.INPUT_MODE_10.value] = 10
            INPUT_COUNTS[InputMode.INPUT_MODE_5_1.value] = 6
            INPUT_COUNTS[InputMode.INPUT_MODE_7_1.value] = 8
            INPUT_COUNTS[InputMode.INPUT_MODE_10_2.value] = 12
            INPUT_COUNTS[InputMode.INPUT_MODE_14_2.value] = 16
            INPUT_COUNTS[InputMode.INPUT_MODE_24_2.value] = 26
            INPUT_COUNTS[InputMode.INPUT_MODE_48_4.value] = 52
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_4.value] = IntArray(0)
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_5.value] = IntArray(0)
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_6.value] = IntArray(0)
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_7.value] = IntArray(0)
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_8.value] = IntArray(0)
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_9.value] = IntArray(0)
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_10.value] = IntArray(0)
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_5_1.value] = intArrayOf(1)
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_7_1.value] = intArrayOf(1)
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_10_2.value] = intArrayOf(1, 12)
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_14_2.value] = intArrayOf(1, 16)
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_24_2.value] = intArrayOf(1, 26)
            AUTOABLE_INPUTS[InputMode.INPUT_MODE_48_4.value] = intArrayOf(1, 2, 51, 52)
            IS_IN_2P[InputMode.INPUT_MODE_4.value] = BooleanArray(5)
            IS_IN_2P[InputMode.INPUT_MODE_5.value] = BooleanArray(6)
            IS_IN_2P[InputMode.INPUT_MODE_6.value] = BooleanArray(7)
            IS_IN_2P[InputMode.INPUT_MODE_7.value] = BooleanArray(8)
            IS_IN_2P[InputMode.INPUT_MODE_8.value] = BooleanArray(9)
            IS_IN_2P[InputMode.INPUT_MODE_9.value] = BooleanArray(10)
            IS_IN_2P[InputMode.INPUT_MODE_5_1.value] = BooleanArray(7)
            IS_IN_2P[InputMode.INPUT_MODE_7_1.value] = BooleanArray(9)
            IS_IN_2P[InputMode.INPUT_MODE_10_2.value] =
                booleanArrayOf(false, false, false, false, false, false, false, true, true, true, true, true, true)
            IS_IN_2P[InputMode.INPUT_MODE_14_2.value] = booleanArrayOf(
                false,
                false,
                false,
                false,
                false,
                false,
                false,
                false,
                false,
                true,
                true,
                true,
                true,
                true,
                true,
                true,
                true
            )
            IS_IN_2P[InputMode.INPUT_MODE_24_2.value] = BooleanArray(27)
            IS_IN_2P[InputMode.INPUT_MODE_48_4.value] = BooleanArray(53)
            val averageColor1 = 65535
            val averageColor2 = 4294967295.toInt()
            val averageColor3 = 4294902015.toInt()
            val averageColor4 = 16777215
            NET_DRAWINGS[InputMode.INPUT_MODE_4.value] =
                intArrayOf(0, averageColor2, averageColor3, averageColor3, averageColor2)
            NET_DRAWINGS[InputMode.INPUT_MODE_5.value] =
                intArrayOf(0, averageColor2, averageColor3, averageColor4, averageColor3, averageColor2)
            NET_DRAWINGS[InputMode.INPUT_MODE_6.value] =
                intArrayOf(0, averageColor2, averageColor3, averageColor2, averageColor2, averageColor3, averageColor2)
            NET_DRAWINGS[InputMode.INPUT_MODE_7.value] = intArrayOf(
                0,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor4,
                averageColor2,
                averageColor3,
                averageColor2
            )
            NET_DRAWINGS[InputMode.INPUT_MODE_8.value] =
                intArrayOf(
                    0,
                    averageColor2,
                    averageColor3,
                    averageColor2,
                    averageColor3,
                    averageColor3,
                    averageColor2,
                    averageColor3,
                    averageColor2
                )
            NET_DRAWINGS[InputMode.INPUT_MODE_9.value] =
                intArrayOf(
                    0,
                    averageColor2,
                    averageColor3,
                    averageColor2,
                    averageColor3,
                    averageColor4,
                    averageColor3,
                    averageColor2,
                    averageColor3,
                    averageColor2
                )
            NET_DRAWINGS[InputMode.INPUT_MODE_10.value] =
                intArrayOf(
                    0,
                    averageColor2,
                    averageColor3,
                    averageColor2,
                    averageColor3,
                    averageColor2,
                    averageColor2,
                    averageColor3,
                    averageColor2,
                    averageColor3,
                    averageColor2
                )
            NET_DRAWINGS[InputMode.INPUT_MODE_5_1.value] =
                intArrayOf(0, averageColor1, averageColor2, averageColor3, averageColor4, averageColor3, averageColor2)
            NET_DRAWINGS[InputMode.INPUT_MODE_7_1.value] =
                intArrayOf(
                    0,
                    averageColor1,
                    averageColor2,
                    averageColor3,
                    averageColor2,
                    averageColor4,
                    averageColor2,
                    averageColor3,
                    averageColor2
                )
            NET_DRAWINGS[InputMode.INPUT_MODE_10_2.value] = intArrayOf(
                0,
                averageColor1,
                averageColor2,
                averageColor3,
                averageColor4,
                averageColor3,
                averageColor2,
                averageColor2,
                averageColor3,
                averageColor4,
                averageColor3,
                averageColor2,
                averageColor1
            )
            NET_DRAWINGS[InputMode.INPUT_MODE_14_2.value] = intArrayOf(
                0,
                averageColor1,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor4,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor4,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor1
            )
            NET_DRAWINGS[InputMode.INPUT_MODE_24_2.value] = intArrayOf(
                0,
                averageColor1,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor1
            )
            NET_DRAWINGS[InputMode.INPUT_MODE_48_4.value] = intArrayOf(
                0,
                averageColor1,
                averageColor1,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor3,
                averageColor2,
                averageColor1,
                averageColor1
            )
        }

        fun getJudgmentMillis(judged: Judged, judgmentStage: Double, i: Int, judgmentAssist: JudgmentAssist): Double {
            var judgmentMillis = JUDGMENT_MAP[judged.value]!![i] * 10 / (judgmentStage + 10)
            when (judgmentAssist) {
                JudgmentAssist.DEFAULT -> Unit
                JudgmentAssist.LONG_NOTE_UP -> judgmentMillis *= 1.5
            }
            return judgmentMillis
        }

        fun getIsJudgment(
            loopingCounter: Double,
            judged: Judged,
            judgmentStage: Double,
            judgmentAssist: JudgmentAssist
        ): Boolean {
            return getJudgmentMillis(
                judged,
                judgmentStage,
                0,
                judgmentAssist
            ) <= loopingCounter && loopingCounter <= getJudgmentMillis(judged, judgmentStage, 1, judgmentAssist)
        }

        fun getJudged(judgmentMeter: Double, judgmentStage: Double, judgmentAssist: JudgmentAssist): Judged {
            if (getIsJudgment(judgmentMeter, Judged.HIGHEST, judgmentStage, judgmentAssist)) {
                return Judged.HIGHEST
            } else if (getIsJudgment(judgmentMeter, Judged.HIGHER, judgmentStage, judgmentAssist)) {
                return Judged.HIGHER
            } else if (getIsJudgment(judgmentMeter, Judged.HIGH, judgmentStage, judgmentAssist)) {
                return Judged.HIGH
            } else if (getIsJudgment(judgmentMeter, Judged.LOW, judgmentStage, judgmentAssist)) {
                return Judged.LOW
            } else if (getIsJudgment(judgmentMeter, Judged.LOWER, judgmentStage, judgmentAssist)) {
                return Judged.LOWER
            } else if (getIsJudgment(judgmentMeter, Judged.LOWEST, judgmentStage, judgmentAssist)) {
                return Judged.LOWEST
            }
            return Judged.NOT
        }
    }
}