package net.pokeranalytics.android.calculus

import android.content.Context
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.FormattingException
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.CurrencyUtils
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.extensions.formatted
import net.pokeranalytics.android.util.extensions.formattedHourlyDuration
import java.util.*

/**
 * An enum representing all the types of Session statistics
 */
enum class Stat : RowRepresentable {

    NETRESULT,
    HOURLY_RATE,
    AVERAGE,
    NUMBER_OF_SETS,
    NUMBER_OF_GAMES,
    DURATION,
    AVERAGE_DURATION,
    NET_BB_PER_100_HANDS,
    HOURLY_RATE_BB,
    AVERAGE_NET_BB,
    WIN_RATIO,
    AVERAGE_BUYIN,
    ROI,
    STANDARD_DEVIATION,
    STANDARD_DEVIATION_HOURLY,
    STANDARD_DEVIATION_BB_PER_100_HANDS,
    HANDS_PLAYED;

    /**
     * Returns whether the stat evolution values requires a distribution sorting
     */
    fun hasDistributionSorting() : Boolean {
        return when (this) {
            STANDARD_DEVIATION, STANDARD_DEVIATION_HOURLY, STANDARD_DEVIATION_BB_PER_100_HANDS -> true
            else -> false
        }
    }

    companion object {

        fun returnOnInvestment(netResult: Double, buyin: Double) : Double? {
            if (buyin == 0.0) {
                return null
            }
            return netResult / buyin
        }

        fun netBBPer100Hands(netBB: Double, numberOfHands: Double) : Double? {
            if (numberOfHands == 0.0) {
                return null
            }
            return netBB / numberOfHands * 100
        }

    }

    override val resId: Int?
        get() {
            return when (this) {
                NETRESULT -> R.string.net_result
                HOURLY_RATE -> R.string.average_hour_rate
                AVERAGE -> R.string.average
                NUMBER_OF_SETS -> R.string.number_of_groups
                NUMBER_OF_GAMES -> R.string.number_of_games
                DURATION -> R.string.duration
                AVERAGE_DURATION -> R.string.average_duration
                NET_BB_PER_100_HANDS -> R.string.net_bb_per_100_hands
                HOURLY_RATE_BB -> R.string.hourly_rate_bb
                AVERAGE_NET_BB -> R.string.average_net_bb
                WIN_RATIO -> R.string.win_ratio
                AVERAGE_BUYIN -> R.string.average_buyin
                ROI -> R.string.roi
                STANDARD_DEVIATION -> R.string.standard_deviation
                STANDARD_DEVIATION_HOURLY -> R.string.standard_deviation_hourly
                STANDARD_DEVIATION_BB_PER_100_HANDS -> R.string.standard_deviation_bb_per_100_hands
                HANDS_PLAYED -> R.string.hands_played
            }
        }

    val threshold: Double
        get() {
            return when (this) {
                WIN_RATIO -> 50.0
                else -> 0.0
            }

        }

    override val viewType: Int = RowViewType.TITLE_VALUE.ordinal
}

/**
 * ComputedStat contains a [stat] and their associated [value]
 */
class ComputedStat(var stat: Stat, var value: Double, var currency: Currency? = null) {

    constructor(stat: Stat, value: Double, previousValue: Double?) : this(stat, value) {
        if (previousValue != null) {
            this.variation = (value - previousValue) / previousValue
        }
    }

    /**
     * The variation of the stat
     */
    var variation: Double? = null

    /**
     * Formats the value of the stat to be suitable for display
     */
    fun format(context: Context): TextFormat {

        if (this.value.isNaN()) {
            return TextFormat(NULL_TEXT, R.color.white)
        }

        when (this.stat) {
            // Amounts + red/green
            Stat.NETRESULT, Stat.HOURLY_RATE, Stat.AVERAGE, Stat.NET_BB_PER_100_HANDS, Stat.HOURLY_RATE_BB,
            Stat.AVERAGE_NET_BB -> {
                val numberFormat= CurrencyUtils.getCurrencyFormatter(context, currency)
                val color = if (this.value >= this.stat.threshold) R.color.green else R.color.red
                return TextFormat(numberFormat.format(this.value), color)
            } // white integers
            Stat.NUMBER_OF_SETS, Stat.NUMBER_OF_GAMES, Stat.HANDS_PLAYED -> {
                return TextFormat("${value.toInt()}")
            } // white durations
            Stat.DURATION, Stat.AVERAGE_DURATION -> {
                return TextFormat(value.formattedHourlyDuration())
            } // red/green percentages
            Stat.WIN_RATIO, Stat.ROI -> {
                val color = if (value * 100 >= this.stat.threshold) R.color.green else R.color.red
                return TextFormat("${(value * 100).formatted()}%", color)
            } // white amountsr
            Stat.AVERAGE_BUYIN, Stat.STANDARD_DEVIATION, Stat.STANDARD_DEVIATION_HOURLY,
            Stat.STANDARD_DEVIATION_BB_PER_100_HANDS -> {
                val numberFormat= CurrencyUtils.getCurrencyFormatter(context, currency)
                return TextFormat(numberFormat.format(this.value))
            }
            else -> throw FormattingException("Stat formatting of ${this.stat.name} not handled")
        }
    }

    /**
     * Returns a TextFormat instance for an evolution value located at the specified [index]
     */
    fun evolutionValueFormat(index: Int) : TextFormat {
        return TextFormat("undef ${index}")
    }

}
