package net.pokeranalytics.android.ui.fragment

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import io.realm.Realm
import io.realm.RealmModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.ComputableGroup
import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.ui.activity.FiltersActivity
import net.pokeranalytics.android.ui.fragment.components.FilterableFragment
import net.pokeranalytics.android.ui.fragment.report.ComposableTableReportFragment
import net.pokeranalytics.android.ui.interfaces.FilterActivityRequestCode
import net.pokeranalytics.android.ui.interfaces.FilterableType
import timber.log.Timber
import java.util.*
import kotlin.coroutines.CoroutineContext

class StatisticsFragment : FilterableFragment() {

	val coroutineContext: CoroutineContext
		get() = Dispatchers.Main

	private lateinit var tableReportFragment: ComposableTableReportFragment

	companion object {

		/**
		 * Create new instance
		 */
		fun newInstance(): StatisticsFragment {
			val fragment = StatisticsFragment()
			val bundle = Bundle()
			fragment.arguments = bundle
			return fragment
		}
	}

	// Life Cycle

	override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
		super.onCreateView(inflater, container, savedInstanceState)
		return inflater.inflate(R.layout.fragment_stats, container, false)
	}

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
	    initUI()
        this.currentFilterable = FilterableType.SESSION
	    applyFilter()
    }

	private fun initUI() {
		val fragmentTransaction = requireFragmentManager().beginTransaction()
		val fragment = ComposableTableReportFragment.newInstance(null)
		fragmentTransaction.add(R.id.tableContainer, fragment)
		fragmentTransaction.commit()
		this.tableReportFragment = fragment
	}

	override val observedEntities: List<Class<out RealmModel>> = listOf(ComputableResult::class.java)

	override fun entitiesChanged(clazz: Class<out RealmModel>) {
		this.launchStatComputation()
	}

//	override fun convertReportIntoRepresentables(report: Report): ArrayList<RowRepresentable> {
//		val rows: ArrayList<RowRepresentable> = ArrayList()
//		report.results.forEach { result ->
//			rows.add(CustomizableRowRepresentable(title = result.group.name))
//			result.group.stats?.forEach { stat ->
//				rows.add(StatRow(stat, result.computedStat(stat), result.group.name))
//			}
//		}
//		return rows
//	}

	override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
		super.onActivityResult(requestCode, resultCode, data)
		if (requestCode == FilterActivityRequestCode.CREATE_FILTER.ordinal && resultCode == Activity.RESULT_OK) {
			data?.let {
				this.saveFilter(this.requireContext(), it.getStringExtra(FiltersActivity.IntentKey.FILTER_ID.keyName))
			}
		}
	}

	// Filter Handler

	override fun applyFilter() {
		super.applyFilter()
		this.launchStatComputation()
	}

	override fun removeFilter() {
		super.removeFilter()
		this.launchStatComputation()
	}

	// Business

	/**
	 * Launch stat computation
	 */
	private fun launchStatComputation() {

		GlobalScope.launch(coroutineContext) {

			val test = GlobalScope.async {
				val s = Date()
				Timber.d(">>> start...")

				val realm = Realm.getDefaultInstance()

				val report = createSessionGroupsAndStartCompute(realm)
				tableReportFragment.report = report

				realm.close()

				val e = Date()
				val duration = (e.time - s.time) / 1000.0
				Timber.d(">>> ended in $duration seconds")

			}
			test.await()

			if (!isDetached) {
				tableReportFragment.showResults()
			}
		}
	}

	/**
	 * Create session groups and start computations
	 */
	private fun createSessionGroupsAndStartCompute(realm: Realm): Report {

		val filter: Filter? = this.currentFilter(this.requireContext(), realm)?.let {
            if (it.filterableType == currentFilterable) { it } else { null }
        }

		val allStats: List<Stat> = listOf(
			Stat.NET_RESULT,
			Stat.HOURLY_RATE,
			Stat.AVERAGE,
			Stat.NUMBER_OF_SETS,
			Stat.AVERAGE_HOURLY_DURATION,
			Stat.HOURLY_DURATION
		)

		val query = filter?.query ?: Query()
		val allSessionGroup = ComputableGroup(query, allStats)

		val cgStats: List<Stat> = listOf(
			Stat.NET_RESULT,
			Stat.HOURLY_RATE,
			Stat.NET_BB_PER_100_HANDS,
			Stat.HOURLY_RATE_BB,
			Stat.AVERAGE,
			Stat.STANDARD_DEVIATION_HOURLY,
			Stat.WIN_RATIO,
			Stat.NUMBER_OF_GAMES,
			Stat.AVERAGE_BUYIN
		)
		val cgSessionGroup = ComputableGroup(Query(QueryCondition.IsCash).merge(query), cgStats)
		val tStats: List<Stat> =
			listOf(
				Stat.NET_RESULT,
				Stat.HOURLY_RATE,
				Stat.ROI,
				Stat.WIN_RATIO,
				Stat.NUMBER_OF_GAMES,
				Stat.AVERAGE_BUYIN
			)
		val tSessionGroup = ComputableGroup(Query(QueryCondition.IsTournament).merge(query), tStats)

		Timber.d(">>>>> Start computations...")

		val options = Calculator.Options()
		val computedStats = mutableListOf<Stat>()
		computedStats.addAll(allStats)
		computedStats.addAll(cgStats)
		computedStats.addAll(tStats)
		options.stats = computedStats

		return Calculator.computeGroups(realm, listOf(allSessionGroup, cgSessionGroup, tSessionGroup), options)
	}

}