package net.pokeranalytics.android.model.filter

import android.content.Context
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.RealmResults
import io.realm.Sort
import io.realm.kotlin.where
import net.pokeranalytics.android.BuildConfig
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PokerAnalyticsException
import net.pokeranalytics.android.model.Limit
import net.pokeranalytics.android.model.TableSize
import net.pokeranalytics.android.model.TournamentType
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.UserDefaults
import net.pokeranalytics.android.util.extensions.*
import java.text.DateFormatSymbols
import java.text.NumberFormat
import java.util.*
import kotlin.collections.ArrayList

/**
 * Enum describing the way a query should be handled
 * Some queries requires a value to be checked upon through equals, in, more, less, between
 */

sealed class QueryCondition : FilterElementRow {

	companion object {
		inline fun <reified T : QueryCondition> more(): T {
			return T::class.java.newInstance().apply { this.operator = Operator.MORE }
		}

		inline fun <reified T : QueryCondition> less(): T {
			return T::class.java.newInstance().apply { this.operator = Operator.LESS }
		}

		inline fun <reified T : QueryCondition> moreOrLess(): ArrayList<T> {
			return arrayListOf(more(), less())
		}

		fun <T : QueryCondition> valueOf(name: String): T {
			val kClass = Class.forName("${QueryCondition::class.qualifiedName}$$name").kotlin
			val instance = kClass.objectInstance ?: kClass.java.newInstance()
			return instance as T
		}

		inline fun <reified T : Identifiable> getInstance(): QueryCondition {
			return when (T::class.java) {
				Bankroll::class.java -> AnyBankroll()
				Game::class.java -> AnyGame()
				Location::class.java -> AnyLocation()
				TransactionType::class.java -> AnyTransactionType()
				TournamentName::class.java -> AnyTournamentName()
				TournamentFeature::class.java -> AllTournamentFeature()
				else -> throw PokerAnalyticsException.QueryTypeUnhandled
			}
		}

		inline fun <reified T : Filterable, reified S : QueryCondition, reified U : Comparable<U>> distinct(): RealmResults<T>? {
			FilterHelper.fieldNameForQueryType<T>(S::class.java)?.let {
				val realm = Realm.getDefaultInstance()

				val distincts = when (T::class) {
					String::class, Int::class -> realm.where<T>().distinct(it).findAll().sort(it, Sort.ASCENDING)
					else -> realm.where<T>().isNotNull(it).findAll().sort(it, Sort.ASCENDING)
				}

				realm.close()
				return distincts
			}
			return null
		}
	}

	enum class Operator {
		ANY,
		ALL,
		MORE,
		LESS,
		EQUALS,
		TRUE,
		;
	}

	val baseId = this::class.simpleName ?: throw PokerAnalyticsException.FilterElementUnknownName

	val groupId: String
		get() {
			when (this.operator) {
				Operator.MORE, Operator.LESS -> return "${this.operator.name.toLowerCase().capitalize()}$baseId"
			}
			return baseId
		}

	val id: List<String>
		get() {
			when (this.operator) {
				Operator.MORE, Operator.LESS -> return listOf("$baseId+${this.operator.name}")
			}

			return when (this) {
				is SingleValue<*> -> listOf(baseId)
				is ListOfValues<*> -> {
					if (listOfValues.isEmpty()) {
						return listOf(baseId)
					}
					this.listOfValues.map { "$baseId+$it" }
				}
				else -> listOf(baseId)
			}
		}

	abstract var operator: Operator

	abstract class ListOfValues<T> : QueryCondition(), Comparable<ListOfValues<T>> where T : Comparable<T> {

		abstract var listOfValues: ArrayList<T>
		abstract fun labelForValue(value: T, context: Context): String

		open fun entityName(context: Context): String {
			return getDisplayName(context)
		}

		override fun getDisplayName(context: Context): String {
			val prefix = this.resId?.let {
				context.getString(it) + " "
			} ?: ""

			return when (listOfValues.size) {
				0 -> return NULL_TEXT
				1, 2 -> prefix + listOfValues.map { labelForValue(it, context) }.joinToString(", ")
				else -> "${listOfValues.size} $prefix ${entityName(context)}"
			}
		}

		override fun compareTo(other: ListOfValues<T>): Int {
			return listOfValues.sorted().first().compareTo(other.listOfValues.sorted().first())
		}

		fun firstValue(context: Context): String? {
			return this.listOfValues.firstOrNull()?.let { this.labelForValue(it, context) }
		}
	}

	abstract class SingleValue<T> : ListOfValues<T>() where T : Comparable<T> {
		override var listOfValues = ArrayList<T>()
		abstract var singleValue: T?
	}

	abstract class ListOfDouble : ListOfValues<Double>() {
		open var sign: Int = 1
		override var operator: Operator = Operator.ANY
		override var listOfValues: ArrayList<Double> = arrayListOf()
		override fun updateValueBy(filterCondition: FilterCondition) {
			super.updateValueBy(filterCondition)
			listOfValues = filterCondition.getValues()
		}

		override fun labelForValue(value: Double, context: Context): String {
			return value.toCurrency(UserDefaults.currency)
		}
	}

	abstract class ListOfInt : ListOfValues<Int>() {
		override var operator: Operator = Operator.ANY
		override var listOfValues: ArrayList<Int> = arrayListOf()
		override fun updateValueBy(filterCondition: FilterCondition) {
			super.updateValueBy(filterCondition)
			listOfValues = filterCondition.getValues()
		}

		override fun labelForValue(value: Int, context: Context): String {
			return value.toString()
		}
	}

	abstract class ListOfString : ListOfValues<String>() {
		override var operator: Operator = Operator.ANY
		override var listOfValues = ArrayList<String>()
		override fun labelForValue(value: String, context: Context): String {
			return value
		}

		override fun updateValueBy(filterCondition: FilterCondition) {
			super.updateValueBy(filterCondition)
			listOfValues = filterCondition.getValues()
		}
	}

	abstract class SingleDate : SingleValue<Date>() {
		override fun labelForValue(value: Date, context: Context): String {
			return value.shortDate()
		}

		override var listOfValues = ArrayList<Date>()

		override var singleValue: Date?
			get() {
				return listOfValues.firstOrNull()
			}
			set(value) {
				listOfValues.removeAll(this.listOfValues)
				value?.let { listOfValues.add(it) }
			}

		override fun updateValueBy(filterCondition: FilterCondition) {
			super.updateValueBy(filterCondition)
			singleValue = filterCondition.getValue()
		}
	}

	abstract class SingleInt : SingleValue<Int>() {
		override fun labelForValue(value: Int, context: Context): String {
			return value.toString()
		}

		override var singleValue: Int?
			get() {
				return listOfValues.firstOrNull()
			}
			set(value) {
				listOfValues.removeAll(this.listOfValues)
				value?.let { listOfValues.add(it) }
			}

		override fun updateValueBy(filterCondition: FilterCondition) {
			super.updateValueBy(filterCondition)
			singleValue = filterCondition.getValue()
		}
	}

	override fun getDisplayName(context: Context): String {
		this.resId?.let {
			return context.getString(it)
		}
		return baseId
	}

	override var filterSectionRow: FilterSectionRow = FilterSectionRow.CashOrTournament

	abstract class QueryDataCondition<T : NameManageable> : ListOfString() {
		fun setObject(dataObject: T) {
			this.listOfValues.removeAll(this.listOfValues)
			this.listOfValues.add(dataObject.id)
		}

		abstract val entity: Class<T>

		override fun getDisplayName(context: Context): String {
			val realm = Realm.getDefaultInstance()
			val entityName = entityName(realm)
			val completeLabel = when (listOfValues.size) {
				0 -> NULL_TEXT
				1, 2 -> {
					listOfValues.map { labelForValue(realm, it) }.joinToString(", ")
				}
				else -> "${listOfValues.size} $entityName"
			}
			realm.close()
			return completeLabel
		}

		open fun entityName(realm: Realm): String {
			return baseId
		}

		private fun labelForValue(realm: Realm, value: String): String {
			val query = realm.where(entity)
			return query.equalTo("id", value).findFirst()?.name ?: NULL_TEXT
		}
	}


	interface DateTime {
		val showTime: Boolean
	}

	abstract class DateQuery : SingleDate(), DateTime {
		override val showTime: Boolean = false

		override fun labelForValue(value: Date, context: Context): String {
			return singleValue?.let {
				if (showTime) {
					it.shortTime()
				} else {
					it.shortDate()
				}
			} ?: NULL_TEXT
		}
	}

	abstract class TimeQuery : DateQuery() {
		override val showTime: Boolean = true
	}

	abstract class TrueQueryCondition : QueryCondition() {
		override var operator: Operator = Operator.TRUE
	}

	object IsLive : TrueQueryCondition()

	object IsCash : TrueQueryCondition()

	object IsOnline : TrueQueryCondition()

	object IsTournament : TrueQueryCondition()

	class AnyBankroll() : QueryDataCondition<Bankroll>() {
		override var entity: Class<Bankroll> = Bankroll::class.java

		constructor(bankroll: Bankroll) : this() {
			this.setObject(bankroll)
		}
	}

	class AnyGame() : QueryDataCondition<Game>() {
		override val entity: Class<Game> = Game::class.java

		constructor(game: Game) : this() {
			this.setObject(game)
		}
	}

	class AnyTournamentName() : QueryDataCondition<TournamentName>() {
		override val entity: Class<TournamentName> = TournamentName::class.java

		constructor(tournamentName: TournamentName) : this() {
			this.setObject(tournamentName)
		}
	}

	class AnyTournamentFeature() : QueryDataCondition<TournamentFeature>() {
		override val entity: Class<TournamentFeature> = TournamentFeature::class.java

		constructor(tournamentFeature: TournamentFeature) : this() {
			this.setObject(tournamentFeature)
		}
	}

	class AllTournamentFeature() : QueryDataCondition<TournamentFeature>() {
		override var operator = Operator.ALL
		override val entity: Class<TournamentFeature> = TournamentFeature::class.java

		constructor(tournamentFeature: TournamentFeature) : this() {
			this.setObject(tournamentFeature)
		}
	}

	class AnyLocation() : QueryDataCondition<Location>() {
		override val entity: Class<Location> = Location::class.java

		constructor(location: Location) : this() {
			this.setObject(location)
		}
	}

	class AnyTransactionType() : QueryDataCondition<TransactionType>() {
		override val entity: Class<TransactionType> = TransactionType::class.java

		constructor(transactionType: TransactionType) : this() {
			this.setObject(transactionType)
		}
	}

	class AnyLimit : ListOfInt() {
		override fun labelForValue(value: Int, context: Context): String {
			return Limit.values()[value].getDisplayName(context)
		}
	}

	class AnyTableSize : ListOfInt() {
		override fun labelForValue(value: Int, context: Context): String {
			return TableSize(value).getDisplayName(context)
		}
	}

	class AnyTournamentType : ListOfInt() {
		override fun labelForValue(value: Int, context: Context): String {
			return TournamentType.values()[value].getDisplayName(context)
		}
	}

	class AnyBlind : ListOfString()

	object Last : SingleInt() {
		override var operator = Operator.EQUALS
		override fun getDisplayName(context: Context): String {
			//TODO update string  "last %i"
			return "${context.getString(R.string.last_i_records)} $singleValue"
		}
	}

	class NumberOfTable : ListOfInt() {
		override fun labelForValue(value: Int, context: Context): String {
			return value.toString() + " " + context.getString(R.string.tables)
		}

		override fun entityName(context: Context): String {
			return ""
		}
	}

	class NumberOfRebuy() : ListOfDouble() {
		constructor(operator: Operator, numberOfRebuy: Double) : this() {
			this.operator = operator
			this.listOfValues = arrayListOf(numberOfRebuy)
		}

		override fun labelForValue(value: Double, context: Context): String {
			return value.toString()
		}
	}

	class TournamentFinalPosition() : ListOfInt() {
		constructor(operator: Operator, finalPosition: Int) : this() {
			this.operator = operator
			this.listOfValues = arrayListOf(finalPosition)
		}

        override fun labelForValue(value: Int, context: Context): String {
            val suffix = when (value%10) {
                1 -> context.getString(R.string.ordinal_suffix_first)
                2 -> context.getString(R.string.ordinal_suffix_second)
                3 -> context.getString(R.string.ordinal_suffix_third)
                else -> context.getString(R.string.ordinal_suffix_default)
            }
            return "$value$suffix "+context.getString(R.string.position)
        }

		override fun entityName(context: Context): String {
			return ""
		}
	}

	open class NetAmount : ListOfDouble()

	class NetAmountWon : NetAmount()
	class NetAmountLost : NetAmount() {
		override var sign: Int = -1
	}

	class TournamentNumberOfPlayer : ListOfInt() {
		override fun labelForValue(value: Int, context: Context): String {
			return value.toString() + " " + context.getString(R.string.number_of_players)
		}

		override fun entityName(context: Context): String {
			return ""
		}
	}

	class StartedFromDate : DateQuery() {
		override var operator = Operator.MORE
	}

	class StartedToDate : DateQuery() {
		override var operator = Operator.LESS
	}

	class EndedFromDate : DateQuery() {
		override var operator = Operator.MORE
	}

	class EndedToDate : DateQuery() {
		override var operator = Operator.LESS
	}

	class AnyDayOfWeek : ListOfInt() {
		override fun labelForValue(value: Int, context: Context): String {
			return DateFormatSymbols.getInstance(Locale.getDefault()).weekdays[value].capitalize()
		}
	}

	class AnyMonthOfYear() : ListOfInt() {
		override fun labelForValue(value: Int, context: Context): String {
			return DateFormatSymbols.getInstance(Locale.getDefault()).months[value].capitalize()
		}

		constructor(month: Int) : this() {
			listOfValues = arrayListOf(month)
		}
	}

	class AnyYear() : ListOfInt() {
		override fun labelForValue(value: Int, context: Context): String {
			return "$value"
		}

		constructor(year: Int) : this() {
			listOfValues = arrayListOf(year)
		}
	}

	object IsWeekDay : TrueQueryCondition()
	object IsWeekEnd : TrueQueryCondition()
	object IsToday : TrueQueryCondition()
	object WasYesterday : TrueQueryCondition()
	object WasTodayAndYesterday : TrueQueryCondition()
	object DuringThisWeek : TrueQueryCondition()
	object DuringThisMonth : TrueQueryCondition()
	object DuringThisYear : TrueQueryCondition()

	class TournamentFee : ListOfDouble() {
		override fun labelForValue(value: Double, context: Context): String {
			return value.toCurrency(UserDefaults.currency)
		}
	}

	class PastDay : SingleInt() {
		override var operator = Operator.EQUALS
		override val viewType: Int = RowViewType.TITLE_VALUE_CHECK.ordinal

		override fun labelForValue(value: Int, context: Context): String {
			return value.toString()
		}

		override fun entityName(context: Context): String {
			return this.resId?.let {
				" " + context.getString(it)
			} ?: ""
		}
	}

	class Duration : SingleInt() {
		override var operator = Operator.EQUALS
		var minutes: Int?
			get() {
				return singleValue
			}
			set(value) {
				singleValue = value
			}

		val netDuration: Long?
			get() {
				minutes?.let {
					return (it * 60 * 1000).toLong()
				}
				return null
			}

		override val viewType: Int = RowViewType.TITLE_VALUE_CHECK.ordinal
		override val bottomSheetType: BottomSheetType = BottomSheetType.DOUBLE_EDIT_TEXT

		override fun labelForValue(value: Int, context: Context): String {
			return value.toMinutes(context)
		}
	}

	class StartedFromTime() : TimeQuery() {
		override var operator = Operator.MORE

		constructor(date: Date) : this() {
			singleValue = date
		}


	}

	class EndedToTime() : TimeQuery() {
		override var operator = Operator.LESS

		constructor(date: Date) : this() {
			singleValue = date
		}

	}

	interface CustomFieldRelated {
		var customFieldId: String

		fun customFieldName(realm: Realm): String {
			val query = realm.where(CustomField::class.java)
			val name = query.equalTo("id", customFieldId).findFirst()?.name
			return name?.let {
				"$it "
			} ?: run { "" }
		}
	}

	class CustomFieldQuery() : QueryDataCondition<CustomField>() {
		override var entity: Class<CustomField> = CustomField::class.java

		constructor(customField: CustomField) : this() {
			this.setObject(customField)
		}
	}

	open class CustomFieldNumberQuery() : ListOfDouble(), CustomFieldRelated {
		override var customFieldId: String = ""
		override var operator: Operator = Operator.EQUALS

		constructor(customFieldId: String, value: Double) : this() {
			this.listOfValues = arrayListOf(value)
			this.customFieldId = customFieldId
		}

		override fun getDisplayName(context: Context): String {
			val realm = Realm.getDefaultInstance()
			val name = customFieldName(realm)
			val prefix = this.resId?.let {
				context.getString(it) + " "
			} ?: ""

			val completeLabel = when (listOfValues.size) {
				0 -> return NULL_TEXT
				1, 2 -> {
					return name + prefix + listOfValues.map { labelForValue(it, context) }.joinToString(", ")
				}
				else -> "${listOfValues.size} $prefix $name"
			}
			realm.close()
			return completeLabel
		}

		override fun labelForValue(value: Double, context: Context): String {
			return NumberFormat.getInstance().format(value)
		}

		override fun updateValueBy(filterCondition: FilterCondition) {
			super.updateValueBy(filterCondition)
			listOfValues = filterCondition.getValues()
			customFieldId = filterCondition.stringValue ?: throw PokerAnalyticsException.QueryValueMapUnexpectedValue
		}
	}

	class CustomFieldAmountQuery : CustomFieldNumberQuery() {
		override fun labelForValue(value: Double, context: Context): String {
			return value.toCurrency(UserDefaults.currency)
		}
	}

	class CustomFieldListQuery() : QueryDataCondition<CustomFieldEntry>(), CustomFieldRelated {
		override var entity: Class<CustomFieldEntry> = CustomFieldEntry::class.java
		override var customFieldId: String = ""

		constructor(customFieldEntry: CustomFieldEntry) : this() {
			this.setObject(customFieldEntry)
			this.customFieldId = customFieldEntry.customField?.id
				?: throw PokerAnalyticsException.QueryValueMapUnexpectedValue
		}

		override fun entityName(realm: Realm): String {
			return customFieldName(realm)
		}

		override fun updateValueBy(filterCondition: FilterCondition) {
			super.updateValueBy(filterCondition)
			listOfValues = filterCondition.getValues()
			customFieldId = filterCondition.stringValue ?: throw PokerAnalyticsException.QueryValueMapUnexpectedValue
		}
	}

	/**
	 * main method of the enum
	 * providing a base RealmQuery [realmQuery], the method is able to attached the corresponding query and returns the newly formed [RealmQuery]
	 */
	inline fun <reified T : Filterable> queryWith(
		realmQuery: RealmQuery<T>,
		otherQueryCondition: QueryCondition? = null
	): RealmQuery<T> {

		val fieldName = FilterHelper.fieldNameForQueryType<T>(this::class.java)
		if (BuildConfig.DEBUG) {
			fieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown
		}
		fieldName ?: return realmQuery

		when (this) {
			//is Between -> realmQuery.between(fieldName, leftValue, rightValue)
			//is BetweenLeftExclusive -> realmQuery.greaterThan(fieldName, leftValue).and().lessThanOrEqualTo(fieldName, rightValue)
			//is BetweenRightExclusive -> realmQuery.greaterThanOrEqualTo(fieldName, leftValue).and().lessThan(fieldName, rightValue)
			is IsLive, is IsOnline -> return realmQuery.equalTo(fieldName, this == IsLive)
			is IsCash -> return realmQuery.equalTo(fieldName, Session.Type.CASH_GAME.ordinal)
			is IsTournament -> return realmQuery.equalTo(fieldName, Session.Type.TOURNAMENT.ordinal)
			is IsWeekEnd, is IsWeekDay -> {
				var query = realmQuery
				if (this == IsWeekDay) {
					query = realmQuery.not()
				}
				return query.`in`(fieldName, arrayOf(Calendar.SATURDAY, Calendar.SUNDAY))
			}
			is IsToday -> {
				val startDate = Date()
				return realmQuery.greaterThanOrEqualTo(fieldName, startDate.startOfDay()).and()
					.lessThanOrEqualTo(fieldName, startDate.endOfDay())
			}
			is WasTodayAndYesterday -> {
				val startDate = Date()
				val calendar = Calendar.getInstance()
				calendar.time = startDate
				calendar.add(Calendar.HOUR_OF_DAY, -24)
				return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and()
					.lessThanOrEqualTo(fieldName, startDate.endOfDay())
			}
			is WasYesterday -> {
				val calendar = Calendar.getInstance()
				calendar.time = Date()
				calendar.add(Calendar.HOUR_OF_DAY, -24)
				return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and()
					.lessThanOrEqualTo(fieldName, calendar.time.endOfDay())
			}
			is PastDay -> {
				singleValue?.let {
					val startDate = Date()
					val calendar = Calendar.getInstance()
					calendar.time = startDate
					calendar.add(Calendar.DAY_OF_YEAR, -it)
					return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and()
						.lessThanOrEqualTo(fieldName, startDate.endOfDay())
				}
				return realmQuery
			}
			is DuringThisWeek -> {
				val startDate = Date()
				val calendar = Calendar.getInstance()
				calendar.time = startDate
				calendar.set(Calendar.DAY_OF_WEEK_IN_MONTH, Calendar.SUNDAY)
				return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and()
					.lessThanOrEqualTo(fieldName, startDate.endOfDay())
			}
			is DuringThisMonth -> {
				val startDate = Date()
				val calendar = Calendar.getInstance()
				calendar.time = startDate
				calendar.set(Calendar.DAY_OF_MONTH, 1)
				return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and()
					.lessThanOrEqualTo(fieldName, startDate.endOfDay())
			}
			is DuringThisYear -> {
				val startDate = Date()
				val calendar = Calendar.getInstance()
				calendar.time = startDate
				calendar.set(Calendar.DAY_OF_YEAR, 1)
				return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and()
					.lessThanOrEqualTo(fieldName, startDate.endOfDay())
			}
			is StartedFromTime -> {
				val calendar = Calendar.getInstance()
				singleValue?.let {
					calendar.time = it
					realmQuery.greaterThanOrEqualTo(fieldName, calendar.hourMinute())
					if (otherQueryCondition is EndedToTime) {
						otherQueryCondition.singleValue?.let { endTime ->
							calendar.time = endTime
							realmQuery.lessThanOrEqualTo(fieldName, calendar.hourMinute())
						}
					}
				}
				return realmQuery
			}
			is EndedToTime -> {
				val calendar = Calendar.getInstance()
				singleValue?.let {
					calendar.time = singleValue
					realmQuery.lessThanOrEqualTo(fieldName, calendar.hourMinute())
					if (otherQueryCondition is StartedFromTime) {
						otherQueryCondition.singleValue?.let { startTime ->
							calendar.time = startTime
							realmQuery.greaterThanOrEqualTo(fieldName, calendar.hourMinute())
						}
					}
				}
				return realmQuery
			}
		}

		if (this is CustomFieldRelated) {
			FilterHelper.fieldNameForQueryType<T>(CustomFieldQuery::class.java)?.let {
				realmQuery.equalTo(it, customFieldId)
			}
		}

		if (this is ListOfValues<*>) {
			if (this.listOfValues.isEmpty()) {
				if (BuildConfig.DEBUG) {
					throw PokerAnalyticsException.FilterElementExpectedValueMissing
				}
				return realmQuery
			}
		}

		return when (operator) {
			Operator.EQUALS -> {
				when (this) {
					is SingleDate -> realmQuery.equalTo(
						fieldName,
						singleValue ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
					)
					is SingleInt -> realmQuery.equalTo(
						fieldName,
						singleValue ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
					)
					is ListOfInt -> realmQuery.equalTo(fieldName, listOfValues.first())
					is ListOfDouble -> realmQuery.equalTo(fieldName, listOfValues.first() * sign)
					is ListOfString -> realmQuery.equalTo(fieldName, listOfValues.first())
					else -> realmQuery
				}
			}
			Operator.MORE -> {
				when (this) {
					is SingleDate -> realmQuery.greaterThanOrEqualTo(
						fieldName,
						singleValue?.startOfDay() ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
					)
					is Duration -> realmQuery.greaterThan(
						fieldName,
						netDuration ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
					)
					is TournamentFinalPosition -> realmQuery.greaterThanOrEqualTo(fieldName, listOfValues.first())
					is TournamentNumberOfPlayer -> realmQuery.greaterThanOrEqualTo(fieldName, listOfValues.first())
					is SingleInt -> realmQuery.greaterThan(
						fieldName,
						singleValue ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
					)
					is ListOfInt -> realmQuery.greaterThan(fieldName, listOfValues.first())
					is NetAmountLost -> realmQuery.lessThan(fieldName, listOfValues.first() * -1)
					is ListOfDouble -> realmQuery.greaterThan(fieldName, listOfValues.first() * sign)
					else -> realmQuery
				}
			}
			Operator.LESS -> {
				when (this) {
					is SingleDate -> realmQuery.lessThanOrEqualTo(
						fieldName,
						singleValue?.endOfDay() ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
					)
					is Duration -> realmQuery.lessThan(
						fieldName,
						netDuration ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
					)
					is TournamentFinalPosition -> realmQuery.lessThanOrEqualTo(fieldName, listOfValues.first())
					is TournamentNumberOfPlayer -> realmQuery.lessThanOrEqualTo(fieldName, listOfValues.first())
					is SingleInt -> realmQuery.lessThan(
						fieldName,
						singleValue ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
					)
					is ListOfInt -> realmQuery.lessThan(fieldName, listOfValues.first())
					is NetAmountLost -> {
						realmQuery.greaterThan(fieldName, listOfValues.first() * -1)
						realmQuery.lessThan(fieldName, 0.0)
					}
					is NetAmountWon -> {
						realmQuery.lessThan(fieldName, listOfValues.first())
						realmQuery.greaterThan(fieldName, 0.0)
					}
					is ListOfDouble -> realmQuery.lessThan(fieldName, listOfValues.first() * sign)
					else -> realmQuery
				}
			}
			Operator.ALL -> {
				when (this) {
					is ListOfInt -> {
						listOfValues.forEach { realmQuery.equalTo(fieldName, it) }
						realmQuery
					}
					is ListOfDouble -> {
						listOfValues.forEach { realmQuery.equalTo(fieldName, it * sign) }
						realmQuery
					}
					is ListOfString -> {
						listOfValues.forEach { realmQuery.equalTo(fieldName, it) }
						realmQuery
					}
					else -> realmQuery
				}
			}
			Operator.ANY -> {
				when (this) {
					is ListOfInt -> realmQuery.`in`(fieldName, listOfValues.toTypedArray())
					is ListOfDouble -> realmQuery.`in`(fieldName, listOfValues.toTypedArray())
					is ListOfString -> realmQuery.`in`(fieldName, listOfValues.toTypedArray())
					else -> realmQuery
				}
			}
			else -> realmQuery
		}
	}

	open fun updateValueBy(filterCondition: FilterCondition) {
		filterCondition.operator?.let {
			this.operator = Operator.values()[it]
		}
	}

	override val viewType: Int
		get() {
			return when (this) {
				is PastDay -> RowViewType.TITLE_VALUE_CHECK.ordinal
				else -> {
					when (this.operator) {
						Operator.MORE -> RowViewType.TITLE_VALUE_CHECK.ordinal
						Operator.LESS -> RowViewType.TITLE_VALUE_CHECK.ordinal
						else -> RowViewType.TITLE_CHECK.ordinal
					}
				}
			}
		}

	override val bottomSheetType: BottomSheetType
		get() {
			return when (this) {
				is PastDay -> BottomSheetType.EDIT_TEXT
				else -> {
					when (this.operator) {
						Operator.MORE -> BottomSheetType.EDIT_TEXT
						Operator.LESS -> BottomSheetType.EDIT_TEXT
						else -> BottomSheetType.NONE
					}
				}
			}
		}

	override val resId: Int?
		get() {
			return when (this) {
				is IsCash -> R.string.cash_game
				is IsTournament -> R.string.tournament
				is IsToday -> R.string.today
				is WasYesterday -> R.string.yesterday
				is WasTodayAndYesterday -> R.string.yesterday_and_today
				is DuringThisWeek -> R.string.current_week
				is DuringThisMonth -> R.string.current_month
				is DuringThisYear -> R.string.current_year
				is StartedFromDate -> R.string.from
				is StartedFromTime -> R.string.from_time
				is EndedToDate -> R.string.to
				is EndedToTime -> R.string.to_time
				is IsLive -> R.string.live
				is IsOnline -> R.string.online
				is IsWeekDay -> R.string.week_days
				is IsWeekEnd -> R.string.weekend
				is PastDay -> R.string.period_in_days
				is TournamentNumberOfPlayer -> {
					when (this.operator) {
						Operator.MORE -> R.string.minimum
						Operator.LESS -> R.string.maximum
						else -> null
					}
				}
				is NetAmountWon -> {
					when (this.operator) {
						Operator.MORE -> R.string.won_amount_more_than
						Operator.LESS -> R.string.won_amount_less_than
						else -> null
					}
				}
				is NetAmountLost -> {
					when (this.operator) {
						Operator.MORE -> R.string.lost_amount_more_than
						Operator.LESS -> R.string.lost_amount_less_than
						else -> null
					}
				}
				is TournamentFinalPosition -> {
					when (this.operator) {
						Operator.MORE -> R.string.minimum
						Operator.LESS -> R.string.maximum
						else -> null
					}
				}
				else -> {
					when (this.operator) {
						Operator.MORE -> R.string.more_than
						Operator.LESS -> R.string.less_than
						else -> null
					}
				}
			}
		}

}