package net.pokeranalytics.android.calculus

import io.realm.RealmResults
import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.model.realm.SessionSet

/**
 * A sessionGroup of computable items identified by a name
 */
class ComputableGroup(name: String, computables: RealmResults<ComputableResult>, sets: RealmResults<SessionSet>, stats: List<Stat>? = null) {

    /**
     * The display name of the group
     */
    var name: String = name

    /**
     * The list of endedSessions to compute
     */
    var computables: RealmResults<ComputableResult> = computables

    /**
     * The list of endedSessions to compute
     */
    var sets: RealmResults<SessionSet> = sets

    /**
     * The list of stats to display
     */
    var stats: List<Stat>? = stats

    /**
     * A subgroup used to compute stat variation
     */
    var comparedComputables: ComputableGroup? = null

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

}

class ComputedResults(group: ComputableGroup) {

    /**
     * The session group used to computed the stats
     */
    var group: ComputableGroup = group

    // 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 allStats() : Collection<ComputedStat> {
        return this._computedStats.values
    }

    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 { _ ->
                // @todo sort
//                var evolutionValues = this._evolutionValues[stat]
//                evolutionValues.so
            }

        }
    }

    /**
     * Returns the number of computed stats
     */
    fun numberOfStats() : Int {
        return this._computedStats.size
    }

}

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

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

}