package net.pokeranalytics.android.ui.fragment

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.tabs.TabLayout
import io.realm.Realm
import kotlinx.android.synthetic.main.fragment_calendar.*
import kotlinx.android.synthetic.main.fragment_stats.recyclerView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.ComputedResults
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.comparison.Comparator
import net.pokeranalytics.android.model.comparison.combined
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.ui.activity.CalendarDetailsActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.SessionObserverFragment
import net.pokeranalytics.android.ui.view.CalendarTabs
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.util.extensions.*
import timber.log.Timber
import java.util.*
import kotlin.coroutines.CoroutineContext


class CalendarFragment : SessionObserverFragment(), CoroutineScope, StaticRowRepresentableDataSource, RowRepresentableDelegate {

	enum class TimeFilter {
		MONTH, YEAR
	}

	companion object {

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

	private lateinit var calendarAdapter: RowRepresentableAdapter

	override val coroutineContext: CoroutineContext
		get() = Dispatchers.Main


	private var rows: ArrayList<CustomizableRowRepresentable> = ArrayList()
	private var sortedMonthlyReports: SortedMap<Date, ComputedResults> = HashMap<Date, ComputedResults>().toSortedMap()
	private var sortedYearlyReports: SortedMap<Date, ComputedResults> = HashMap<Date, ComputedResults>().toSortedMap()
	private var datesForRows: HashMap<CustomizableRowRepresentable, Date> = HashMap()

	private var sessionTypeCondition: QueryCondition? = null
	private var currentTimeFilter: TimeFilter = TimeFilter.MONTH
	private var currentStat = Stat.NETRESULT

	// Life Cycle

	override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
		return inflater.inflate(net.pokeranalytics.android.R.layout.fragment_calendar, container, false)
	}

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

		launchStatComputation()
	}

	override fun adapterRows(): List<RowRepresentable>? {
		return rows
	}

	override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {

		when (currentTimeFilter) {
			TimeFilter.MONTH -> {
				val date = datesForRows[row]
				sortedMonthlyReports[datesForRows[row]]?.let {
					CalendarDetailsActivity.newInstance(requireContext(), it, sessionTypeCondition, date?.getMonthAndYear())
				}
			}
			TimeFilter.YEAR -> {
				val date = datesForRows[row]
				sortedYearlyReports[datesForRows[row]]?.let {
					CalendarDetailsActivity.newInstance(requireContext(), it, sessionTypeCondition, date?.getDateYear())
				}
			}
		}
	}

	override fun sessionsChanged() {
		launchStatComputation()
	}

	// Business

	/**
	 * Init data
	 */
	private fun initData() {
	}

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

		CalendarTabs.values().forEach {
			val tab = tabs.newTab()
			tab.text = getString(it.resId)
			tabs.addTab(tab)
		}

		tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
			override fun onTabSelected(tab: TabLayout.Tab) {
				when (tab.position) {
					0 -> currentStat = Stat.NETRESULT
					1 -> currentStat = Stat.HOURLY_RATE
					2 -> currentStat = Stat.NUMBER_OF_GAMES
					3 -> currentStat = Stat.WIN_RATIO
					4 -> currentStat = Stat.STANDARD_DEVIATION_HOURLY
					5 -> currentStat = Stat.AVERAGE
					6 -> currentStat = Stat.AVERAGE_DURATION
					7 -> currentStat = Stat.DURATION
				}
				displayData()
			}

			override fun onTabUnselected(tab: TabLayout.Tab) {
			}

			override fun onTabReselected(tab: TabLayout.Tab) {
			}
		})

		// Manage session type filter
		filterSessionAll.setOnCheckedChangeListener { _, isChecked ->
			if (isChecked) {
				sessionTypeCondition = null
				filterSessionCash.isChecked = false
				filterSessionTournament.isChecked = false
				launchStatComputation()
			} else if (sessionTypeCondition == null) {
				filterSessionAll.isChecked = true
			}
		}
		filterSessionCash.setOnCheckedChangeListener { _, isChecked ->
			if (isChecked) {
				sessionTypeCondition = QueryCondition.CASH
				filterSessionAll.isChecked = false
				filterSessionTournament.isChecked = false
				launchStatComputation()
			} else if (sessionTypeCondition == QueryCondition.CASH) {
				filterSessionCash.isChecked = true
			}
		}
		filterSessionTournament.setOnCheckedChangeListener { _, isChecked ->
			if (isChecked) {
				sessionTypeCondition = QueryCondition.TOURNAMENT
				filterSessionAll.isChecked = false
				filterSessionCash.isChecked = false
				launchStatComputation()
			} else if (sessionTypeCondition == QueryCondition.TOURNAMENT) {
				filterSessionTournament.isChecked = true
			}
		}

		// Manage time filter
		filterTimeMonth.setOnCheckedChangeListener { _, isChecked ->
			if (isChecked) {
				currentTimeFilter = TimeFilter.MONTH
				filterTimeYear.isChecked = false
				displayData()
			} else if (currentTimeFilter == TimeFilter.MONTH) {
				filterTimeMonth.isChecked = true
			}

		}
		filterTimeYear.setOnCheckedChangeListener { _, isChecked ->
			if (isChecked) {
				currentTimeFilter = TimeFilter.YEAR
				filterTimeMonth.isChecked = false
				displayData()
			} else if (currentTimeFilter == TimeFilter.YEAR) {
				filterTimeYear.isChecked = true
			}
		}

		val viewManager = LinearLayoutManager(requireContext())

		calendarAdapter = RowRepresentableAdapter(this, this)

		recyclerView.apply {
			setHasFixedSize(true)
			layoutManager = viewManager
			adapter = calendarAdapter
		}
	}


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

		GlobalScope.launch {

			val calendar = Calendar.getInstance()
			calendar.time = Date().startOfMonth()

			val startDate = Date()
			val realm = Realm.getDefaultInstance()

			val monthlyReports: HashMap<Date, ComputedResults> = HashMap()
			val yearlyReports: HashMap<Date, ComputedResults> = HashMap()

			// Compute data per YEAR and MONTH

			val monthConditions = when (sessionTypeCondition) {
				QueryCondition.CASH -> listOf(Comparator.YEAR, Comparator.MONTH_OF_YEAR, Comparator.CASH).combined()
				QueryCondition.TOURNAMENT -> listOf(Comparator.YEAR, Comparator.MONTH_OF_YEAR, Comparator.TOURNAMENT).combined()
				else -> listOf(Comparator.YEAR, Comparator.MONTH_OF_YEAR).combined()
			}

			monthConditions.forEach { conditions ->
				val report = Calculator.computeStatsWithComparators(realm, conditions = conditions, options = Calculator.Options())
				report.results.forEach { computedResults ->
					if (!computedResults.isEmpty) {
						// Set date data
						conditions.forEach { condition ->
							condition.valueMap?.get("year")?.let { year ->
								calendar.set(Calendar.YEAR, year as Int)
							}
							condition.valueMap?.get("month")?.let { month ->
								calendar.set(Calendar.MONTH, month as Int)
							}
						}

						monthlyReports[calendar.time] = computedResults
					}
				}
			}

			calendar.time = Date().startOfYear()

			// Compute data per YEAR
			val yearConditions = when (sessionTypeCondition) {
				QueryCondition.CASH -> listOf(Comparator.YEAR, Comparator.CASH).combined()
				QueryCondition.TOURNAMENT -> listOf(Comparator.YEAR, Comparator.TOURNAMENT).combined()
				else -> listOf(Comparator.YEAR).combined()
			}

			yearConditions.forEach { conditions ->
				val report = Calculator.computeStatsWithComparators(realm, conditions = conditions, options = Calculator.Options())
				report.results.forEach { computedResults ->
					if (!computedResults.isEmpty) {
						// Set date data
						conditions.forEach { condition ->
							condition.valueMap?.get("year")?.let { year ->
								calendar.set(Calendar.YEAR, year as Int)
							}
						}
						yearlyReports[calendar.time] = computedResults
					}
				}
			}

			sortedMonthlyReports = monthlyReports.toSortedMap(compareByDescending { it })
			sortedYearlyReports = yearlyReports.toSortedMap(compareByDescending { it })

			Timber.d("Computation: ${System.currentTimeMillis() - startDate.time}ms")

			// Logs
			/*
			Timber.d("========== YEAR x MONTH")
			sortedMonthlyReports.keys.forEach {
				Timber.d("$it => ${sortedMonthlyReports[it]?.computedStat(Stat.NETRESULT)?.value}")
			}

			Timber.d("========== YEARLY")
			sortedYearlyReports.keys.forEach {
				Timber.d("$it => ${sortedYearlyReports[it]?.computedStat(Stat.NETRESULT)?.value}")
			}
			*/

			GlobalScope.launch(Dispatchers.Main) {
				displayData()
			}
		}
	}

	/**
	 * Display data
	 */
	private fun displayData() {

		val startDate = Date()

		datesForRows.clear()
		rows.clear()

		when (currentTimeFilter) {

			// Create monthly reports
			TimeFilter.MONTH -> {
				val years: ArrayList<String> = ArrayList()
				sortedMonthlyReports.keys.forEach { date ->
					if (!years.contains(date.getDateYear())) {
						years.add(date.getDateYear())
						rows.add(
							CustomizableRowRepresentable(
								customViewType = RowViewType.HEADER_TITLE,
								title = date.getDateYear()
							)
						)
					}

					sortedMonthlyReports[date]?.computedStat(currentStat)?.let { computedStat ->

						val row = CustomizableRowRepresentable(
							customViewType = RowViewType.TITLE_VALUE_ARROW,
							title = date.getDateMonth(),
							computedStat = computedStat,
							isSelectable = true
						)

						rows.add(row)
						datesForRows.put(row, date)
					}
				}
			}

			// Create yearly reports
			TimeFilter.YEAR -> {
				sortedYearlyReports.keys.forEach { date ->
					sortedYearlyReports[date]?.computedStat(currentStat)?.let { computedStat ->
						val row = CustomizableRowRepresentable(
							customViewType = RowViewType.TITLE_VALUE_ARROW,
							title = date.getDateYear(),
							computedStat = computedStat,
							isSelectable = true
						)

						rows.add(row)
						datesForRows.put(row, date)
					}
				}
			}
		}

		Timber.d("Display data: ${System.currentTimeMillis() - startDate.time}ms")
		Timber.d("Rows: ${rows.size}")

		calendarAdapter.notifyDataSetChanged()

	}

}