package net.pokeranalytics.android.ui.fragment.report

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.view.isVisible
import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.LineDataSet
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import io.realm.Realm
import kotlinx.android.synthetic.main.fragment_progress_report.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.AggregationType
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.Report
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.combined
import net.pokeranalytics.android.ui.extensions.ChipGroupExtension
import net.pokeranalytics.android.ui.extensions.hideWithAnimation
import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.extensions.showWithAnimation
import net.pokeranalytics.android.ui.fragment.GraphFragment
import timber.log.Timber
import java.util.*


class ProgressReportFragment : AbstractReportFragment() {

	companion object {

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

	private lateinit var graphFragment: GraphFragment

	private var reports: MutableMap<AggregationType, Report> = hashMapOf()
	private var stat: Stat = Stat.NET_RESULT
	private var displayAggregationChoices: Boolean = true

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

	override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
		super.onViewCreated(view, savedInstanceState)
		initUI()
	}

	/**
	 * Set data
	 */
	fun setData(report: Report, stat: Stat, displayAggregationChoices: Boolean, title: String) {
		this.stat = stat
		this.setReport(report)
		this.displayAggregationChoices = displayAggregationChoices
		this.reportTitle = title

		stat.aggregationTypes.firstOrNull()?.let {
			reports[it] = report
		}
	}

	/**
	 * Init UI
	 */
	private fun initUI() {

		val fragmentManager = parentActivity?.supportFragmentManager
		val fragmentTransaction = fragmentManager?.beginTransaction()
		graphFragment = GraphFragment()

		fragmentTransaction?.add(R.id.graphContainer, graphFragment)
		fragmentTransaction?.commit()

		stat.aggregationTypes.firstOrNull()?.let { aggregationType ->
			reports[aggregationType]?.let { report ->
				setGraphData(report, aggregationType)
			}
		}

		val aggregationTypes = stat.aggregationTypes

		aggregationTypes.forEachIndexed { index, type ->
			val chip = Chip(requireContext())
			chip.id = index
			chip.text = requireContext().getString(type.resId)
			chip.chipStartPadding = 8f.px
			chip.chipEndPadding = 8f.px
			this.chipGroup.addView(chip)
		}

		this.chipGroup.isVisible = displayAggregationChoices
		this.chipGroup.isSingleSelection = true
		this.chipGroup.check(0)

		this.chipGroup.setOnCheckedChangeListener(object : ChipGroupExtension.SingleSelectionOnCheckedListener() {
			override fun onCheckedChanged(group: ChipGroup, checkedId: Int) {
				super.onCheckedChanged(group, checkedId)

				@SuppressWarnings
				if (checkedId < 0) { // when unchecked, checkedId returns -1, causing a crash
					return
				}

				val aggregationType = aggregationTypes[checkedId]

				when (aggregationType) {
					AggregationType.MONTH, AggregationType.YEAR -> {

						if (aggregationType.criterias.combined().size < 2) {
							Toast.makeText(context, R.string.less_then_2_values_for_display, Toast.LENGTH_LONG).show()
							return
						}
					}
					else -> {}
				}

				reports[aggregationType]?.let { report ->
					setGraphData(report, aggregationType)
				} ?: run {
					launchStatComputation(aggregationType)
				}

			}
		})
	}

	/**
	 * Launch stat computation
	 */
	private fun launchStatComputation(aggregationType: AggregationType) {

		graphContainer.hideWithAnimation()
		progressBar.showWithAnimation()

		GlobalScope.launch {

			val s = Date()
			Timber.d(">>> start...")

			val realm = Realm.getDefaultInstance()

			val group = selectedReport.results.first().group

			val report = Calculator.computeStatsWithEvolutionByAggregationType(realm, stat, group, aggregationType)
			reports[aggregationType] = report

			realm.close()

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

			launch(Dispatchers.Main) {
				setGraphData(report, aggregationType)
				progressBar.hideWithAnimation()
				graphContainer.showWithAnimation()
			}
		}
	}

	/**
	 * Set the graph data set
	 */
	private fun setGraphData(report: Report, aggregationType: AggregationType) {

		val dataSet = when (aggregationType) {
			AggregationType.SESSION -> report.results.firstOrNull()?.defaultStatEntries(stat, requireContext())
			AggregationType.DURATION -> {
				report.results.firstOrNull()?.durationEntries(stat, requireContext())
			}
			AggregationType.MONTH, AggregationType.YEAR -> {
				when (this.stat) {
					Stat.NUMBER_OF_GAMES, Stat.NUMBER_OF_SETS -> report.barEntries(this.stat, requireContext())
					else -> report.lineEntries(this.stat, requireContext())
				}
			}
		}

		dataSet?.let { ds ->
			if (ds is LineDataSet) {
				graphFragment.setLineData(listOf(ds), stat, aggregationType.axisFormatting)
			}
			if (ds is BarDataSet) {
				graphFragment.setBarData(listOf(ds), stat, aggregationType.axisFormatting)
			}

		}

	}

}