package net.pokeranalytics.android.ui.view.rowrepresentable

import android.content.Context
import android.text.InputType
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PokerAnalyticsException
import net.pokeranalytics.android.model.interfaces.Manageable
import net.pokeranalytics.android.model.realm.FilterCondition
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.RowViewType
import java.text.DateFormatSymbols
import java.util.*

sealed class FilterElementRow : RowRepresentable {
	interface Duration {
		var minutes: Int
	}

	interface Operator
	interface MoreOperator : Operator
	interface LessOperator : Operator

	open class BoolFilterElementRow : FilterElementRow()

	open class DateFilterElementRow(var dateValue: Date = Date(), var showTime: Boolean = false) : FilterElementRow()
	open class NumericFilterElementRow(open val doubleValue: Double = 0.0) : FilterElementRow()
	open class StringFilterElementRow(val stringValue: String = "") : FilterElementRow()

	// Subclasses
	open class SingleValueFilterElementRow(open val intValue: Int) : NumericFilterElementRow() {
		override val doubleValue: Double
			get() {
				return intValue.toDouble()
			}
	}

	open class DataFilterElementRow(data: Manageable) : StringFilterElementRow(data.id) {
		val objectId: String = data.id
		val name: String = (data as RowRepresentable).getDisplayName()
    }

	open class StaticDataFilterElementRow(val row: RowRepresentable, val objectId: Int) : NumericFilterElementRow(objectId.toDouble()) {

		override val resId: Int? = row.resId
		val name: String = row.getDisplayName()

		fun getDataLocalizedTitle(context: Context): String {
			return row.localizedTitle(context)
		}
	}

	open class DurationFilterElement : NumericFilterElementRow(), Duration {
		override var minutes: Int = 0
		override val doubleValue: Double
			get() {
				return minutes.toDouble()
			}
	}

	open class AmountFilterElement : NumericFilterElementRow() {
		var amount: Double = 0.0
		override val doubleValue: Double
			get() {
				return amount
			}
	}

	object Cash : BoolFilterElementRow()
	object Tournament : BoolFilterElementRow()
	object Live : BoolFilterElementRow()
	object Online : BoolFilterElementRow()
	object Today : BoolFilterElementRow()
	object Yesterday : BoolFilterElementRow()
	object TodayAndYesterday : BoolFilterElementRow()
	object CurrentWeek : BoolFilterElementRow()
	object CurrentMonth : BoolFilterElementRow()
	object CurrentYear : BoolFilterElementRow()
	object Weekday : BoolFilterElementRow()
	object Weekend : BoolFilterElementRow()
	object From : DateFilterElementRow()
	object To : DateFilterElementRow()

	object FromTime : DateFilterElementRow(showTime = true)
	object ToTime : DateFilterElementRow(showTime = true)


	// Data classes - holding value
	object ResultMoreThan : AmountFilterElement(), MoreOperator

	object ResultLessThan : AmountFilterElement(), LessOperator
	object DurationMoreThan : DurationFilterElement(), MoreOperator
	object DurationLessThan : DurationFilterElement(), LessOperator
	object ReBuyMoreThan : AmountFilterElement(), MoreOperator
	object ReBuyLessThan : AmountFilterElement(), LessOperator


	data class Year(val year: Int) : SingleValueFilterElementRow(year)
	data class Month(val month: Int) : SingleValueFilterElementRow(month)
	data class Day(val day: Int) : SingleValueFilterElementRow(day)

	data class Blind(val blind: String, val hasDefaultCurrency: Boolean) : StringFilterElementRow(blind)

	//TODO: Refactor?
	data class PastDays(var lastDays: Int = 0) : SingleValueFilterElementRow(lastDays) {
		override val intValue: Int
			get() {
				return lastDays
			}
	}

	data class LastGames(var lastGames: Int) : SingleValueFilterElementRow(lastGames) {
		override val intValue: Int
			get() {
				return lastGames
			}
	}

	data class LastSessions(var lastSessions: Int) : SingleValueFilterElementRow(lastSessions) {
		override val intValue: Int
			get() {
				return lastSessions
			}
	}

	data class Limit(val limit: net.pokeranalytics.android.model.Limit) : StaticDataFilterElementRow(limit, limit.ordinal)
	data class TableSize(val tableSize: net.pokeranalytics.android.model.TableSize) : StaticDataFilterElementRow(tableSize, tableSize.numberOfPlayer)

	data class Bankroll(val bankroll: Manageable) : DataFilterElementRow(bankroll)
	data class Game(val game: Manageable) : DataFilterElementRow(game)
	data class Location(val location: Manageable) : DataFilterElementRow(location)
	data class TournamentName(val tournamentName: Manageable) : DataFilterElementRow(tournamentName)
	data class AllTournamentFeature(val tournamentFeature: Manageable) : DataFilterElementRow(tournamentFeature)
	data class AnyTournamentFeature(val tournamentFeature: Manageable) : DataFilterElementRow(tournamentFeature)

	lateinit var filterSectionRow: FilterSectionRow

    val id: String = this.criteria.name

    private val criteria: QueryCriteria
		get() {
			return when (this) {
				is Cash -> QueryCriteria.CASH
				is Tournament -> QueryCriteria.TOURNAMENT
				is Blind -> QueryCriteria.BLIND
				is From -> QueryCriteria.STARTED_FROM_DATE
				is To -> QueryCriteria.ENDED_TO_DATE
				is FromTime -> QueryCriteria.STARTED_FROM_TIME
				is ToTime -> QueryCriteria.ENDED_TO_TIME
				is Month -> QueryCriteria.MONTH
				is Day -> QueryCriteria.DAY_OF_WEEK
				is Year -> QueryCriteria.YEAR
				is Live -> QueryCriteria.LIVE
				is Online -> QueryCriteria.ONLINE
				is Weekday -> QueryCriteria.WEEK_DAY
				is Weekend -> QueryCriteria.WEEK_END
				is Today -> QueryCriteria.TODAY
				is Yesterday -> QueryCriteria.YESTERDAY
				is TodayAndYesterday -> QueryCriteria.TODAY_AND_YESTERDAY
				is CurrentWeek -> QueryCriteria.THIS_WEEK
				is CurrentMonth -> QueryCriteria.THIS_MONTH
				is CurrentYear -> QueryCriteria.THIS_YEAR
				is PastDays -> QueryCriteria.PAST_DAYS
				is Limit -> QueryCriteria.LIMIT
				is TableSize -> QueryCriteria.TABLE_SIZE
				is Game -> QueryCriteria.GAME
				is Bankroll -> QueryCriteria.BANKROLL
				is Location -> QueryCriteria.LOCATION
				is TournamentName -> QueryCriteria.TOURNAMENT_NAME
				is AnyTournamentFeature -> QueryCriteria.ANY_TOURNAMENT_FEATURES
				is AllTournamentFeature -> QueryCriteria.ALL_TOURNAMENT_FEATURES
				is ResultMoreThan -> QueryCriteria.MORE_THAN_NET_RESULT
				is ResultLessThan -> QueryCriteria.LESS_THAN_NET_RESULT
				is DurationMoreThan -> QueryCriteria.MORE_THAN_DURATION
				is DurationLessThan -> QueryCriteria.LESS_THAN_DURATION

				//TODO: Check the conditions
				is LastGames -> QueryCriteria.LAST_GAMES
				is LastSessions -> QueryCriteria.LAST_SESSIONS
				is ReBuyMoreThan -> QueryCriteria.MIN_RE_BUY
				is ReBuyLessThan -> QueryCriteria.MAX_RE_BUY

				else -> throw PokerAnalyticsException.UnknownQueryTypeForRow(this)
			}
		}

	fun contains(filterConditions: List<FilterCondition>): Boolean {
		return when (this) {
			is SingleValueFilterElementRow -> filterConditions.any {
				it.values.contains(this.intValue)
			}
			is DataFilterElementRow -> filterConditions.any {
				it.ids.contains(this.objectId)
			}
			else -> true
		}
	}


	override val resId: Int?
		get() {
			return when (this) {
				is Cash -> R.string.cash_game
				is Tournament -> R.string.tournament
				is Today -> R.string.today
				is Yesterday -> R.string.yesterday
				is TodayAndYesterday -> R.string.yesterday_and_today
				is CurrentWeek -> R.string.current_week
				is CurrentMonth -> R.string.current_month
				is CurrentYear -> R.string.current_year
				is From, FromTime -> R.string.from
				is To, ToTime -> R.string.to
				is Live -> R.string.live
				is Online -> R.string.online
				is Weekday -> R.string.week_days
				is Weekend -> R.string.weekend
				is PastDays -> R.string.period_in_days
				is LastGames -> R.string.last_records
				is LastSessions -> R.string.last_sessions
				is ReBuyMoreThan -> R.string.minimum
				is ReBuyLessThan -> R.string.maximum
				is MoreOperator -> R.string.more_than
				is LessOperator -> R.string.less_than
				else -> null
			}
		}

	override val viewType: Int
		get() {
			return when (this) {
				is PastDays, is From, is To, is FromTime, is ToTime, is LastGames, is LastSessions, is ReBuyMoreThan, is ReBuyLessThan,
				is DurationMoreThan, is DurationLessThan -> RowViewType.TITLE_VALUE_CHECK.ordinal
				else -> RowViewType.TITLE_CHECK.ordinal
			}
		}

	override val bottomSheetType: BottomSheetType
		get() {
			return when (this) {
				is PastDays, is LastGames, is LastSessions, is ReBuyMoreThan, is ReBuyLessThan -> BottomSheetType.EDIT_TEXT
				is DurationMoreThan, is DurationLessThan -> BottomSheetType.DOUBLE_EDIT_TEXT
				else -> BottomSheetType.NONE
			}
		}

	override fun editingDescriptors(map: Map<String, Any?>): ArrayList<RowRepresentableEditDescriptor>? {
		return when (this) {
			is PastDays -> {
				val pastDays: String? by map
				arrayListOf(
					RowRepresentableEditDescriptor(pastDays, R.string.period_in_days, inputType = InputType.TYPE_CLASS_NUMBER)
				)
			}
			is LastGames -> {
				val lastGames: String? by map
				arrayListOf(
					RowRepresentableEditDescriptor(lastGames, R.string.last_records, inputType = InputType.TYPE_CLASS_NUMBER)
				)
			}
			is LastSessions -> {
				val lastSessions: String? by map
				arrayListOf(
					RowRepresentableEditDescriptor(lastSessions, R.string.last_sessions, inputType = InputType.TYPE_CLASS_NUMBER)
				)
			}

			//TODO: Refactor that
			is AmountFilterElement -> {
				val amount: String? by map
				arrayListOf(
					RowRepresentableEditDescriptor(amount, R.string.amount, inputType = InputType.TYPE_CLASS_NUMBER)
				)
			}

			is DurationFilterElement -> {
				val hours: String? by map
				val minutes: String? by map
				arrayListOf(
					RowRepresentableEditDescriptor(hours, R.string.hour, inputType = InputType.TYPE_CLASS_NUMBER),
					RowRepresentableEditDescriptor(minutes, R.string.minute, inputType = InputType.TYPE_CLASS_NUMBER)
				)
			}
			else -> super.editingDescriptors(map)
		}
	}

	override fun getDisplayName(): String {
		return when (this) {
			is SingleValueFilterElementRow -> {
				when (this) {
					is Day -> DateFormatSymbols.getInstance(Locale.getDefault()).weekdays[this.intValue]
					is Month -> DateFormatSymbols.getInstance(Locale.getDefault()).months[this.intValue]
					else -> "${this.intValue}"
				}
			}
			is DataFilterElementRow -> this.name
			is StaticDataFilterElementRow -> this.name
            is Blind -> this.blind
            else -> super.getDisplayName()
		}
	}

	override fun localizedTitle(context: Context): String {
		return when (this) {
			is StaticDataFilterElementRow -> this.getDataLocalizedTitle(context)
			else -> super.localizedTitle(context)
		}
	}

	val sectionToExclude: List<FilterSectionRow>?
		get() {
			val excluded = arrayListOf<FilterSectionRow>()
			if (!this.filterSectionRow.allowMultiSelection) {
				excluded.add(this.filterSectionRow)
			}
			this.filterSectionRow.exclusiveWith?.let { exclusives ->
				excluded.addAll(exclusives)
			}

			if (excluded.size > 0) {
				return excluded
			}
			return null
		}


    private enum class QueryCriteria {
        LIVE,
        CASH,
        ONLINE,
        TOURNAMENT,
        BANKROLL,
        GAME,
        TOURNAMENT_NAME,
        ANY_TOURNAMENT_FEATURES,
        ALL_TOURNAMENT_FEATURES,
        LOCATION,
        LIMIT,
        TABLE_SIZE,
        TOURNAMENT_TYPE,
        BLIND,
        LAST_GAMES,
        LAST_SESSIONS,
        MORE_NUMBER_OF_TABLE,
        LESS_NUMBER_OF_TABLE,
        BETWEEN_NUMBER_OF_TABLE,
        MORE_THAN_NET_RESULT,
        LESS_THAN_NET_RESULT,
        MORE_THAN_BUY_IN,
        LESS_THAN_BUY_IN,
        MORE_THAN_CASH_OUT,
        LESS_THAN_CASH_OUT,
        MORE_THAN_TIPS,
        LESS_THAN_TIPS,
        MORE_THAN_NUMBER_OF_PLAYER,
        LESS_THAN_NUMBER_OF_PLAYER,
        BETWEEN_NUMBER_OF_PLAYER,
        MORE_THAN_TOURNAMENT_FEE,
        LESS_THAN_TOURNAMENT_FEE,
        BETWEEN_TOURNAMENT_FEE,
        MIN_RE_BUY,
        MAX_RE_BUY,

        // Dates
        STARTED_FROM_DATE,
        STARTED_TO_DATE,
        ENDED_FROM_DATE,
        ENDED_TO_DATE,
        DAY_OF_WEEK,
        MONTH,
        YEAR,
        WEEK_DAY,
        WEEK_END,
        TODAY,
        YESTERDAY,
        TODAY_AND_YESTERDAY,
        THIS_WEEK,
        THIS_MONTH,
        THIS_YEAR,
        PAST_DAYS,
        MORE_THAN_DURATION,
        LESS_THAN_DURATION,
        STARTED_FROM_TIME,
        ENDED_TO_TIME,

        COMMENT,

        ;
    }
}