package net.pokeranalytics.android.ui.view

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.AppCompatTextView
import androidx.appcompat.widget.SwitchCompat
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.widget.ContentLoadingProgressBar
import androidx.recyclerview.widget.RecyclerView
import com.github.mikephil.charting.charts.BarChart
import com.github.mikephil.charting.charts.LineChart
import com.github.mikephil.charting.data.BarData
import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import kotlinx.android.synthetic.main.row_feed_session.view.*
import kotlinx.android.synthetic.main.row_transaction.view.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.ComputedStat
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.bankroll.BankrollReport
import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.extensions.ChipGroupExtension
import net.pokeranalytics.android.ui.extensions.addCircleRipple
import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.extensions.setTextFormat
import net.pokeranalytics.android.ui.graph.AxisFormatting
import net.pokeranalytics.android.ui.graph.setStyle
import net.pokeranalytics.android.ui.view.rowrepresentable.*

/**
 * An interface used to factor the configuration of RecyclerView.ViewHolder
 */
interface BindableHolder {
	fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {
	}
}

/**
 * Row View Type for recyclerView's adapter
 * [layoutRes] is the resource layout to display
 */
enum class RowViewType(private var layoutRes: Int) {

	// Header
	HEADER_TITLE(R.layout.row_header_title),
	HEADER_TITLE_VALUE(R.layout.row_header_title_value),
	HEADER_TITLE_AMOUNT(R.layout.row_header_title_amount),
	HEADER_TITLE_AMOUNT_BIG(R.layout.row_header_title_amount_big),
	LOCATION_TITLE(R.layout.row_title),
	INFO(R.layout.row_info),

	// Row
	TITLE(R.layout.row_title),
	TITLE_ARROW(R.layout.row_title_arrow),
	TITLE_ICON_ARROW(R.layout.row_title_icon_arrow),
	TITLE_VALUE(R.layout.row_title_value),
	TITLE_VALUE_ARROW(R.layout.row_title_value_arrow),
	TITLE_VALUE_ACTION(R.layout.row_title_value_action),
	TITLE_SWITCH(R.layout.row_title_switch),
	TITLE_GRID(R.layout.row_bottom_sheet_grid_title),
	DATA(R.layout.row_title),
	BOTTOM_SHEET_DATA(R.layout.row_bottom_sheet_title),
	TITLE_CHECK(R.layout.row_title_check),
	TITLE_VALUE_CHECK(R.layout.row_title_value_check),
	LOADER(R.layout.row_loader),

	// Custom row
	ROW_SESSION(R.layout.row_feed_session),
	ROW_TRANSACTION(R.layout.row_transaction),
	ROW_BUTTON(R.layout.row_button),
	ROW_FOLLOW_US(R.layout.row_follow_us),
	STATS(R.layout.row_stats_title_value),
	STATS_DOUBLE(R.layout.row_stats_double),
	GRAPH(R.layout.row_graph),
	LEGEND_DEFAULT(R.layout.row_legend_default),
	LIST(R.layout.row_list),

	// Separator
	SEPARATOR(R.layout.row_separator);

	/**
	 * View holder
	 */
	fun viewHolder(parent: ViewGroup): RecyclerView.ViewHolder {

		val layout = LayoutInflater.from(parent.context).inflate(layoutRes, parent, false)

		return when (this) {

			// Row View Holder
			HEADER_TITLE, HEADER_TITLE_VALUE, HEADER_TITLE_AMOUNT, HEADER_TITLE_AMOUNT_BIG, LOCATION_TITLE,
			INFO, TITLE, TITLE_ARROW, TITLE_ICON_ARROW, TITLE_VALUE, TITLE_VALUE_ARROW, TITLE_VALUE_ACTION, TITLE_GRID,
			TITLE_SWITCH, TITLE_CHECK, TITLE_VALUE_CHECK,
			DATA, BOTTOM_SHEET_DATA, LOADER -> RowViewHolder(layout)

			// Row Session
			ROW_SESSION -> RowSessionViewHolder(layout)

			// Row Transaction
			ROW_TRANSACTION -> RowTransactionViewHolder(layout)

			// Row Button
			ROW_BUTTON -> RowButtonViewHolder(layout)

			// Row Follow Us
			ROW_FOLLOW_US -> RowFollowUsViewHolder(layout)

			// Row List - ChipGroup
			LIST -> ListViewHolder(layout)

			// Row Stats
			STATS -> StatsTitleValueViewHolder(layout)
			STATS_DOUBLE -> StatsDoubleViewHolder(layout)
			GRAPH -> GraphViewHolder(layout)
			LEGEND_DEFAULT -> LegendDefaultViewHolder(layout)

			// Separator
			SEPARATOR -> SeparatorViewHolder(layout)

			//else -> throw Exception("Undefined rowViewType's holder")
		}
	}

	/**
	 * Display a generic row (titleResId, value, container)
	 */
	@SuppressWarnings("ResourceType")
	inner class RowViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
		override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {

			if (row is CustomizableRowRepresentable) {

				// Customizable Row

				// Title
				itemView.findViewById<AppCompatTextView>(R.id.title)?.let {
					it.text = row.localizedTitle(itemView.context)
				}

				// Value
				itemView.findViewById<AppCompatTextView?>(R.id.value)?.let {
					if (row.computedStat != null) {
						val format = row.computedStat!!.format()
						it.setTextFormat(format, itemView.context)
					} else if (row.value != null) {
						it.text = row.value
					}
				}

				// Listener
				row.isSelectable?.let { isSelectable ->
					if (isSelectable) {
						val listener = View.OnClickListener {
							adapter.delegate?.onRowSelected(position, row)
						}
						itemView.findViewById<View?>(R.id.container)?.setOnClickListener(listener)
					}
				}

			} else {

				// Classic row

				// Title
				itemView.findViewById<AppCompatTextView?>(R.id.title)?.let {
					if (row.resId != null) {
						it.text = row.localizedTitle(itemView.context)
					} else {
						it.text = row.getDisplayName(itemView.context)
					}
				}

				// Value
				itemView.findViewById<AppCompatTextView?>(R.id.value)?.let {
					it.text = adapter.dataSource.stringForRow(row, itemView.context)
				}

				// Icon
				itemView.findViewById<AppCompatImageView?>(R.id.icon)?.let { imageView ->
					imageView.setImageDrawable(null)
					row.imageRes?.let { imageRes ->
						imageView.setImageResource(imageRes)
					}
				}

				// Action
				itemView.findViewById<AppCompatImageView>(R.id.action)?.let { imageView ->
					imageView.setImageDrawable(null)
					row.imageRes?.let { imageRes ->
						imageView.visibility = View.VISIBLE
						imageView.setImageResource(imageRes)
					}
					row.imageTint?.let { color ->
						imageView.setColorFilter(ContextCompat.getColor(imageView.context, color))
					}
					if (row.imageClickable == true) {
						imageView.addCircleRipple()
						imageView.setOnClickListener {
							adapter.delegate?.onRowSelected(position, row, true)
						}
					} else {
						imageView.setBackgroundResource(0)
					}
				}

				// Listener
				val listener = View.OnClickListener {
					itemView.findViewById<SwitchCompat?>(R.id.switchView)?.let {
						if (adapter.dataSource.isEnabled(row)) {
							it.isChecked = !it.isChecked
						}
					} ?: run {
						adapter.delegate?.onRowSelected(position, row)
					}
				}

				itemView.findViewById<View?>(R.id.container)?.setOnClickListener(listener)
			}

			// Switch
			itemView.findViewById<SwitchCompat?>(R.id.switchView)?.let {
				it.isChecked = adapter.dataSource.boolForRow(row)
				it.isEnabled = adapter.dataSource.isEnabled(row)
				it.setOnCheckedChangeListener { _, isChecked ->
					adapter.delegate?.onRowValueChanged(isChecked, row)
				}
			}

			// Selected row
			itemView.findViewById<AppCompatImageView?>(R.id.check)?.let {
				it.isSelected = adapter.dataSource.isSelected(row)
			}


		}
	}

	/**
	 * Display a follow us row
	 */
	inner class RowFollowUsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
		override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {
			itemView.findViewById<AppCompatImageView?>(R.id.icon1)?.setOnClickListener {
				adapter.delegate?.onRowSelected(0, row)
			}
			itemView.findViewById<AppCompatImageView?>(R.id.icon2)?.setOnClickListener {
				adapter.delegate?.onRowSelected(1, row)
			}
			itemView.findViewById<AppCompatImageView?>(R.id.icon3)?.setOnClickListener {
				adapter.delegate?.onRowSelected(2, row)
			}
			itemView.findViewById<AppCompatImageView?>(R.id.icon4)?.setOnClickListener {
				adapter.delegate?.onRowSelected(3, row)
			}
		}
	}

	/**
	 * Display a stat
	 */
	inner class StatsTitleValueViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
		BindableHolder {
		override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {

			// Title
			itemView.findViewById<AppCompatTextView?>(R.id.title)?.text = row.localizedTitle(itemView.context)

			// Value
			itemView.findViewById<AppCompatTextView?>(R.id.value)?.let { view ->
				adapter.dataSource.contentDescriptorForRow(row)?.textFormat?.let {
					view.setTextFormat(it, itemView.context)
				}
			}

			if (row is StatRow) {
				itemView.findViewById<AppCompatImageView?>(R.id.nextArrow)?.isVisible = row.stat.hasProgressGraph
			}

			// Listener
			val listener = View.OnClickListener {
				adapter.delegate?.onRowSelected(position, row)
			}
			itemView.findViewById<View?>(R.id.container)?.setOnClickListener(listener)
		}
	}

	/**
	 * Display a stat
	 */
	inner class StatsDoubleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
		BindableHolder {
		override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {

			if (row is StatDoubleRow) {

				// Stat 1
				itemView.findViewById<AppCompatTextView?>(R.id.stat1Name)?.let { view ->
					view.text = ""
					row.computedStat1?.stat?.resId?.let {
						view.text = view.context.getString(it)
					}
				}

				itemView.findViewById<AppCompatTextView?>(R.id.stat1Value)?.let { view ->
					view.text = ""
					row.computedStat1?.format()?.let {
						view.setTextFormat(it, itemView.context)
					}
				}

				// Stat 2
				itemView.findViewById<AppCompatTextView?>(R.id.stat2Name)?.let { view ->
					view.text = ""
					row.computedStat2?.stat?.resId?.let {
						view.text = view.context.getString(it)
					}
				}

				itemView.findViewById<AppCompatTextView?>(R.id.stat2Value)?.let { view ->
					view.text = ""
					row.computedStat2?.format()?.let {
						view.setTextFormat(it, itemView.context)
					}
				}
			}

			// Listener
			val listener = View.OnClickListener {
				adapter.delegate?.onRowSelected(position, row)
			}
			itemView.findViewById<View?>(R.id.container)?.setOnClickListener(listener)
		}
	}

	/**
	 * Display a graph
	 */
	inner class GraphViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
		BindableHolder {
		override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {

			if (row is GraphRow) {

				row.dataSet?.let { dataSet ->

					val context = itemView.context

					val chartView = when (dataSet) {
						is LineDataSet -> {
							val lineChart = LineChart(context)
							lineChart.data = LineData(dataSet)
							lineChart
						}
						is BarDataSet -> {
							val barChart = BarChart(context)
							barChart.data = BarData(dataSet)
							barChart
						}
						else -> null
					}

					itemView.findViewById<FrameLayout?>(R.id.chartContainer)?.let {
						it.removeAllViews()
						it.addView(chartView)
					}

					chartView?.let {
						chartView.setStyle(true, AxisFormatting.DEFAULT, context)
						chartView.setTouchEnabled(false)
					}

//					chartView.highlightValue((entries.size - 1).toFloat(), 0)
				}

			}

			// Listener
			val listener = View.OnClickListener {
				adapter.delegate?.onRowSelected(position, row)
			}
			itemView.findViewById<FrameLayout?>(R.id.chartContainer)?.setOnClickListener(listener)
		}
	}

	/**
	 * Display a legend
	 */
	inner class LegendDefaultViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
		BindableHolder {
		override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {

			if (row is BankrollReport) {
				itemView.findViewById<AppCompatTextView>(R.id.stat1Name)?.let {
					it.text = itemView.context.getString(R.string.total)
				}
				itemView.findViewById<AppCompatTextView>(R.id.stat1Value)?.let {
					val formattedStat = ComputedStat(Stat.NET_RESULT, row.total).format()
					it.setTextFormat(formattedStat, itemView.context)
				}
				itemView.findViewById<AppCompatTextView>(R.id.stat2Name)?.let {
					it.text = itemView.context.getString(R.string.risk_of_ruin)
				}
				itemView.findViewById<AppCompatTextView>(R.id.stat2Value)?.let {
					val riskOfRuin = row.riskOfRuin ?: 0.0
					val formattedStat = ComputedStat(Stat.RISK_OF_RUIN, riskOfRuin).format()
					it.setTextFormat(formattedStat, itemView.context)
				}
				val listener = View.OnClickListener {
					adapter.delegate?.onRowSelected(position, row)
				}
				itemView.findViewById<ConstraintLayout>(R.id.container)?.setOnClickListener(listener)

			}
		}
	}

	/**
	 * Display a list of chips
	 */
	inner class ListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
		BindableHolder {
		override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {

			if (row is CustomFieldRow) {

				itemView.findViewById<ChipGroup>(R.id.chipGroup)?.let { chipGroup ->

					chipGroup.removeAllViews()
					chipGroup.setOnCheckedChangeListener(null)

					val currentChoice = adapter.dataSource.intForRow(row)
					val isUpdating = adapter.dataSource.boolForRow(row)

					row.getChoices(currentChoice, isUpdating)?.forEach { type ->
						val chip = Chip(itemView.context)
						chip.id = type.ordinal
						chip.text = itemView.context.getString(type.resId)
						chip.chipStartPadding = 8f.px
						chip.chipEndPadding = 8f.px
						chip.isEnabled = type.isEnabled
						chip.alpha = if (type.isEnabled) 1f else 0.5f

						chip.isChecked = adapter.dataSource.intForRow(row) == type.ordinal
						chipGroup.addView(chip)
					}

					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
							}

							adapter.delegate?.onRowValueChanged(CustomField.Type.values()[checkedId], row)
						}
					})
				}
			}
		}
	}

	/**
	 * Display a button in a row
	 */
	inner class RowButtonViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
		override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {
			itemView.findViewById<AppCompatTextView>(R.id.title).text = row.localizedTitle(itemView.context)
			itemView.findViewById<AppCompatTextView>(R.id.title).isVisible = !adapter.dataSource.boolForRow(row)
			itemView.findViewById<ContentLoadingProgressBar>(R.id.progressBar).isVisible = adapter.dataSource.boolForRow(row)
			val listener = View.OnClickListener {
				adapter.delegate?.onRowSelected(position, row)
			}
			itemView.findViewById<ConstraintLayout>(R.id.container).setOnClickListener(listener)
		}
	}

	/**
	 * Display a session view
	 */
	inner class RowSessionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
		override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {
			itemView.sessionRow.setData(row as Session)
			val listener = View.OnClickListener {
				adapter.delegate?.onRowSelected(position, row)
			}
			itemView.sessionRow.setOnClickListener(listener)
		}
	}

	/**
	 * Display a transaction view
	 */
	inner class RowTransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
		override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {
			itemView.transactionRow.setData(row as Transaction)
			val listener = View.OnClickListener {
				adapter.delegate?.onRowSelected(position, row)
			}
			itemView.transactionRow.setOnClickListener(listener)
		}
	}

	/**
	 * Display a separator
	 */
	inner class SeparatorViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
		override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) {
		}
	}

}