package net.pokeranalytics.android.calculus

import net.pokeranalytics.android.model.realm.SessionSet

/**
 * An interface to describe objects that can be summed
 */
interface Summable {
    var value: Double
}

/**
 * An interface describing some class that can be computed
 */
interface SessionInterface : Summable {
    var sessionSet: SessionSet?
    var estimatedHands: Double
    var bbNetResult: Double
    var bigBlindSessionCount: Int // 0 or 1
    var buyin: Double
}

/**
 * A sessionGroup of computable items identified by a name
 */
class SessionGroup(name: String, sessions: List<SessionInterface>) {

    var name: String = name
    var sessions: List<SessionInterface> = sessions

    // A subgroup used to compute stat variation
    var comparedSessions: SessionGroup? = null

    // The computed stats of the comparable sessionGroup
    var comparedComputedResults: ComputedResults? = null

}

class ComputedResults() {

    // The computed stats of the sessionGroup
    private var _computedStats: MutableMap<Stat, ComputedStat> = mutableMapOf()

    // The map containing all evolution values for all stats
    private var _evolutionValues: MutableMap<Stat, MutableList<Point>> = mutableMapOf()

    fun addEvolutionValue(value: Double, stat: Stat) {
        this._addEvolutionValue(Point(value), stat = stat)
    }

    fun addEvolutionValue(value: Double, duration: Double, stat: Stat) {
        this._addEvolutionValue(Point(value, y = duration), stat = stat)
    }

    private fun _addEvolutionValue(point: Point, stat: Stat) {
        var evolutionValues = this._evolutionValues[stat]
        if (evolutionValues != null) {
            evolutionValues.add(point)
        } else {
            var values: MutableList<Point> = mutableListOf(point)
            this._evolutionValues[stat] = values
        }
    }

    fun addStats(computedStats: Set<ComputedStat>) {
        computedStats.forEach {
            this._computedStats[it.stat] = it
        }
    }

    fun computedStat(stat: Stat) : ComputedStat? {
        return this._computedStats[stat]
    }

    fun computeStatVariations(resultsToCompare: ComputedResults) {
        this._computedStats.keys.forEach { stat ->
            var computedStat = this.computedStat(stat)
            val comparedStat = resultsToCompare.computedStat(stat)
            if (computedStat != null && comparedStat != null) {
                computedStat.variation = (computedStat.value - comparedStat.value) / comparedStat.value
            }
        }
    }

    fun finalize(options: Calculator.Options) {
        if (options.evolutionValues != Calculator.Options.EvolutionValues.NONE) {

            // Sort points as a distribution
            this._computedStats.keys.filter { it.hasDistributionSorting() }.forEach { stat ->
                // @todo sort
//                var evolutionValues = this._evolutionValues[stat]
//                evolutionValues.so
            }

        }
    }

}

class Point(x: Double, y: Double) {
    val x: Double = x
    val y: Double = y

    constructor(x: Double) : this(x, 1.0)

}