package net.pokeranalytics.android.ui.fragment

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.github.mikephil.charting.charts.BarChart
import com.github.mikephil.charting.charts.BarLineChartBase
import com.github.mikephil.charting.charts.LineChart
import com.github.mikephil.charting.data.*
import com.github.mikephil.charting.highlight.Highlight
import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet
import com.github.mikephil.charting.listener.OnChartValueSelectedListener
import kotlinx.android.synthetic.main.fragment_graph.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.graph.AxisFormatting
import net.pokeranalytics.android.ui.graph.GraphUnderlyingEntry
import net.pokeranalytics.android.ui.graph.ObjectIdentifier
import net.pokeranalytics.android.ui.graph.setStyle
import net.pokeranalytics.android.ui.view.LegendView
import net.pokeranalytics.android.ui.view.MultiLineLegendView


class GraphFragment : RealmFragment(), OnChartValueSelectedListener {

	enum class Style {
		LINE,
		BAR,
		MULTILINE,
	}

	companion object {

		/**
		 * Create new instance
		 */
		fun newInstance(lineDataSets: List<LineDataSet>? = null, barDataSets: List<BarDataSet>? = null, style: Style = Style.LINE): GraphFragment {
			val fragment = GraphFragment()
			fragment.style = style
			fragment.lineDataSetList = lineDataSets
			fragment.barDataSetList = barDataSets
			val bundle = Bundle()
			fragment.arguments = bundle
			return fragment
		}

	}

	private var style: Style = Style.LINE
	private lateinit var legendView: LegendView

	private var lineDataSetList: List<LineDataSet>? = null
	private var barDataSetList: List<BarDataSet>? = null

	private var chartView: BarLineChartBase<*>? = null

	private var stat: Stat = Stat.NET_RESULT
	private var axisFormatting: AxisFormatting = AxisFormatting.DEFAULT

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

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

	/**
	 * Set data
	 */
	fun setLineData(lineDataSets: List<LineDataSet>, stat: Stat, axisFormatting: AxisFormatting = AxisFormatting.DEFAULT) {
		this.lineDataSetList = lineDataSets
		this.stat = stat
		this.axisFormatting = axisFormatting

		if (isAdded && !isDetached) {
			loadGraph()
		}
	}

	fun setBarData(barDataSets: List<BarDataSet>, stat: Stat, axisFormatting: AxisFormatting = AxisFormatting.DEFAULT) {
		this.barDataSetList = barDataSets
		this.stat = stat
		this.axisFormatting = axisFormatting

		if (isAdded && !isDetached) {
			loadGraph()
		}
	}

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

		this.legendView = when (this.style) {
			Style.MULTILINE -> MultiLineLegendView(requireContext())
			else -> LegendView(requireContext())
		}

		this.legendContainer.addView(this.legendView)
	}

	/**
	 * Load graph
	 */
	private fun loadGraph() {

		this.chartContainer.removeAllViews()

		this.lineDataSetList?.let { dataSets ->

			val lineChart = LineChart(context)
			lineChart.setOnChartValueSelectedListener(this)

			val lineData = LineData(dataSets)
			lineChart.data = lineData

			this.chartView = lineChart

			dataSets.firstOrNull()?.let { dataSet ->
				this.legendView.prepareWithStat(this.stat, dataSet.entryCount, this.style)
				if (dataSet.entryCount > 0) {
					val entry = dataSet.getEntryForIndex(dataSet.entryCount - 1)
					this.selectValue(entry, dataSet)
				}
			}
		}

		this.barDataSetList?.let { dataSets ->

			this.legendView.prepareWithStat(this.stat, style = this.style)
			val barChart = BarChart(context)
			barChart.setOnChartValueSelectedListener(this)

			if (stat.graphShowsXAxisZero) {
				barChart.xAxis.axisMinimum = 0.0f
			}
			if (stat.graphShowsYAxisZero) {
				barChart.axisLeft.axisMinimum = 0.0f
			}
			this.chartView = barChart

			val barData = BarData(dataSets)
			barChart.data = barData

			dataSets.firstOrNull()?.let {
				if (it.entryCount > 0) {
					val entry = it.getEntryForIndex(0)
					this.chartView?.highlightValue(entry.x, 0)
				}
			}
		}

		this.chartContainer.addView(this.chartView)

		this.chartView?.setStyle(false, axisFormatting, requireContext())

	}

	// OnChartValueSelectedListener
	override fun onNothingSelected() {
		// nothing to do
	}

	override fun onValueSelected(e: Entry?, h: Highlight?) {

		val dataSet = this.chartView?.data?.getDataSetForEntry(e)
		e?.let { entry ->
			this.selectValue(entry, dataSet)
		}
	}

	private fun selectValue(entry: Entry, dataSet: IBarLineScatterCandleBubbleDataSet<out Entry>? = null) {

		val statEntry = when (entry.data) {
			is ObjectIdentifier -> {
				val identifier = entry.data as ObjectIdentifier
				getRealm().where(identifier.clazz).equalTo("id", identifier.id).findAll().firstOrNull()
			}
			is GraphUnderlyingEntry -> entry.data as GraphUnderlyingEntry?
			else -> null
		}

		statEntry?.let {
			val groupName = dataSet?.label ?: ""
			val color = dataSet?.color
			val legendValue = it.legendValues(stat, entry, this.style, groupName, requireContext())
			this.legendView.setItemData(legendValue, color)
		}
	}

}