package net.pokeranalytics.android.model.realm

import android.content.Context
import io.realm.Realm
import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.StatFormattingException
import net.pokeranalytics.android.util.TextFormat
import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.Timed
import net.pokeranalytics.android.ui.graph.ObjectIdentifier
import net.pokeranalytics.android.util.NULL_TEXT
import java.text.DateFormat
import java.util.*


open class SessionSet() : RealmObject(), Timed, Filterable {

	@PrimaryKey
	override var id = UUID.randomUUID().toString()

	var startDate: Date = Date()
        set(value) {
            field = value
            this.computeNetDuration()
        }

    var endDate: Date = Date()
        set(value) {
            field = value
            this.computeNetDuration()
        }

	override fun startDate(): Date? {
		return this.startDate
	}

    override fun endDate(): Date {
        return this.endDate
    }

    override var breakDuration: Long = 0L
        set(value) {
            field = value
            this.computeNetDuration()
        }

	/**
	 * The start date of the break
	 */
	override var pauseDate: Date? = null

	/**
	 * the net duration of the set (READONLY)
	 */
    override var netDuration: Long = 0L

	fun computeStats() {
		this.ratedNet = this.sessions?.sumByDouble { it.computableResult?.ratedNet ?: 0.0 } ?: 0.0
		this.estimatedHands = this.sessions?.sumByDouble { it.estimatedHands } ?: 0.0
		this.bbNet = this.sessions?.sumByDouble { it.bbNet } ?: 0.0
		this.breakDuration =  this.sessions?.max("breakDuration")?.toLong() ?: 0L
	}

    /**
     * The list of endedSessions associated with this set
     */
    @LinkingObjects("sessionSet")
    val sessions: RealmResults<Session>? = null

    var ratedNet: Double = 0.0

    val hourlyRate: Double
		get() {
			return this.ratedNet / this.hourlyDuration
		}

    var estimatedHands: Double = 0.0

    var bbNet: BB = 0.0

	val bbHourlyRate: BB
		get() {
			return this.bbNet / this.hourlyDuration
		}

	enum class Field(val identifier: String) {
		START_DATE("startDate"),
		RATED_NET("ratedNet"),
		HOURLY_RATE("hourlyRate"),
		BB_NET("bbNet"),
		ESTIMATED_HANDS("estimatedHands"),
		NET_DURATION("netDuration")
	}

	companion object {

		fun newInstance(realm: Realm) : SessionSet {
			val sessionSet = SessionSet()
			return realm.copyToRealm(sessionSet)
		}

		fun fieldNameForQueryType(queryCondition: Class < out QueryCondition >): String? {
            Session.fieldNameForQueryType(queryCondition)?.let {
                return "sessions.$it"
            }
            return null
		}

	}

	// Stat Base

	override fun entryTitle(context: Context): String {
		return DateFormat.getDateInstance(DateFormat.SHORT).format(this.startDate)
	}

	override fun formattedValue(stat: Stat) : TextFormat {
		return when (stat) {
			Stat.NET_RESULT, Stat.AVERAGE -> stat.format(this.ratedNet, currency = null)
			Stat.HOURLY_DURATION, Stat.AVERAGE_HOURLY_DURATION -> stat.format(this.hourlyDuration, currency = null)
			Stat.HOURLY_RATE -> stat.format(this.hourlyRate, currency = null)
			Stat.HANDS_PLAYED -> stat.format(this.estimatedHands, currency = null)
			Stat.HOURLY_RATE_BB -> stat.format(this.bbHourlyRate, currency = null)
			Stat.NET_BB_PER_100_HANDS, Stat.STANDARD_DEVIATION_BB_PER_100_HANDS -> {
				val netBBPer100Hands = Stat.netBBPer100Hands(this.bbNet, this.estimatedHands)
				if (netBBPer100Hands != null) {
					return stat.format(this.estimatedHands, currency = null)
				} else {
					return TextFormat(NULL_TEXT)
				}
			}
			else -> throw StatFormattingException("format undefined for stat ${stat.name}")
		}
	}

	// Timed

	override val objectIdentifier: ObjectIdentifier
		get() = ObjectIdentifier(this.id, SessionSet::class.java)

}

