package net.pokeranalytics.android.calculus.bankroll

import io.realm.Realm
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.ComputableGroup
import net.pokeranalytics.android.calculus.ComputedResults
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.realm.*

class BankrollCalculator {

	companion object {

		fun computeReport(realm: Realm, setup: BankrollReportSetup) : BankrollReport {

			//val realm = Realm.getDefaultInstance()

			val report = BankrollReport(setup)

			val bankrolls: List<Bankroll> =
				if (setup.bankroll != null) listOf(setup.bankroll)
				else realm.where(Bankroll::class.java).findAll()

			var initialValue = 0.0
			var transactionNet = 0.0

			bankrolls.forEach { bankroll ->

				val rate = if (setup.virtualBankroll) bankroll.rate else 1.0

				if (setup.shouldAddInitialValue) {
					initialValue += bankroll.initialValue * rate
				}

				bankroll.transactions?.let { transactions ->
					val sum = transactions.sum("amount")
					transactionNet += rate * sum.toDouble()
				}
			}

			report.transactionsNet = transactionNet
			report.initial = initialValue

			val query = setup.query
			val transactions = Filter.queryOn<Transaction>(realm, query)
			report.addDatedItems(transactions)

			transactions.forEach {
				report.addTransaction(it)
			}

			val sessions = Filter.queryOn<Session>(realm, query)
			report.addDatedItems(sessions)

			if (setup.virtualBankroll) {

				val options = Calculator.Options(stats = listOf(Stat.NET_RESULT, Stat.HOURLY_RATE, Stat.STANDARD_DEVIATION_HOURLY))
				val group  = ComputableGroup(query)
				val result = Calculator.compute(realm, group, options)
				result.computedStat(Stat.NET_RESULT)?.let {
					report.netResult = it.value
				}
				this.computeRiskOfRuin(report, result)

			} else {

				val results = Filter.queryOn<Result>(realm, query)
				report.netResult = results.sum("net").toDouble()

			}

			val depositType = TransactionType.getByValue(TransactionType.Value.DEPOSIT, realm)
			report.transactionBuckets[depositType.id]?.let {  bucket ->
				report.depositTotal = bucket.transactions.sumByDouble { it.amount }
			}

			val withdrawalType = TransactionType.getByValue(TransactionType.Value.WITHDRAWAL, realm)
			report.transactionBuckets[withdrawalType.id]?.let {  bucket ->
				report.withdrawalTotal = bucket.transactions.sumByDouble { it.amount }
			}

			report.generateGraphPointsIfNecessary()

			//realm.close()

			return report
		}

		private fun computeRiskOfRuin(report: BankrollReport, results: ComputedResults) {

			val hourlyRate = results.computedStat(Stat.HOURLY_RATE)?.value
			val hourlyStandardDeviation = results.computedStat(Stat.STANDARD_DEVIATION_HOURLY)?.value

			if (hourlyRate != null && hourlyStandardDeviation != null) {

				report.riskOfRuin = Stat.riskOfRuin(hourlyRate, hourlyStandardDeviation, report.total)

			}

		}

	}

}