package net.pokeranalytics.android.model import io.realm.Realm import io.realm.Sort import io.realm.kotlin.where import net.pokeranalytics.android.R import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.exceptions.PokerAnalyticsException import net.pokeranalytics.android.model.Criteria.Bankrolls.comparison import net.pokeranalytics.android.model.Criteria.Blinds.comparison import net.pokeranalytics.android.model.Criteria.Games.comparison import net.pokeranalytics.android.model.Criteria.Limits.comparison import net.pokeranalytics.android.model.Criteria.Locations.comparison import net.pokeranalytics.android.model.Criteria.TableSizes.comparison import net.pokeranalytics.android.model.Criteria.TournamentFeatures.comparison import net.pokeranalytics.android.model.Criteria.TournamentFees.comparison import net.pokeranalytics.android.model.Criteria.TournamentNames.comparison import net.pokeranalytics.android.model.Criteria.TournamentTypes.comparison import net.pokeranalytics.android.model.Criteria.TransactionTypes.comparison import net.pokeranalytics.android.model.filter.Query import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.interfaces.NameManageable import net.pokeranalytics.android.model.realm.* import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.util.enumerations.IntIdentifiable import net.pokeranalytics.android.util.enumerations.IntSearchable import net.pokeranalytics.android.util.extensions.findById fun List.combined(): List { val comparatorList = ArrayList>() this.forEach { criteria -> comparatorList.add(criteria.queries) } return getCombinations(comparatorList) } fun getCombinations(queries: List>): List { if (queries.isEmpty()) { return listOf() } val mutableQueries = queries.toMutableList() var combinations = mutableQueries.removeAt(0) for (queryList in mutableQueries) { val newCombinations = mutableListOf() combinations.forEach { combinedQuery -> queryList.forEach { queryToAdd -> val nq = Query().merge(combinedQuery).merge(queryToAdd) newCombinations.add(nq) } } combinations = newCombinations } return combinations } sealed class Criteria(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepresentable { abstract class RealmCriteria(uniqueIdentifier: Int) : Criteria(uniqueIdentifier) { inline fun comparison(): List { if (this is ListCustomFields) { val objects = mutableListOf() val realm = Realm.getDefaultInstance() realm.findById(CustomField::class.java, this.customFieldId)?.entries?.forEach { objects.add(QueryCondition.CustomFieldListQuery(it)) } objects.sorted() realm.close() return objects.map { Query(it) } } return compare, T>() } } abstract class SimpleCriteria(private val conditions: List, uniqueIdentifier: Int) : Criteria(uniqueIdentifier) { fun comparison(): List { return conditions.map { Query(it) } } } abstract class ListCriteria(uniqueIdentifier: Int) : Criteria(uniqueIdentifier) { inline fun , reified S : Comparable> comparison(): List { if (this is ValueCustomFields) { val realm = Realm.getDefaultInstance() val distincts = realm.where().equalTo("customFields.id", this.customFieldId).findAll().sort("numericValue", Sort.ASCENDING) realm.close() val objects = mutableListOf() distincts.mapNotNull { it.numericValue }.distinct().forEach {value -> val condition: QueryCondition.CustomFieldNumberQuery = when (this.customFieldType(realm)) { CustomField.Type.AMOUNT.uniqueIdentifier -> QueryCondition.CustomFieldAmountQuery() CustomField.Type.NUMBER.uniqueIdentifier -> QueryCondition.CustomFieldNumberQuery() else -> throw PokerAnalyticsException.QueryValueMapUnexpectedValue }.apply { this.customFieldId = this@ListCriteria.customFieldId listOfValues = arrayListOf(value) } objects.add(condition) } objects.sorted() return objects.map { Query(it) } } QueryCondition.distinct()?.let { val values = it.mapNotNull { session -> when (this) { is Limits -> if (session.limit is S) { session.limit as S } else throw PokerAnalyticsException.QueryValueMapUnexpectedValue is TournamentTypes -> if (session.tournamentType is S) { session.tournamentType as S } else throw PokerAnalyticsException.QueryValueMapUnexpectedValue is TableSizes -> if (session.tableSize is S) { session.tableSize as S } else throw PokerAnalyticsException.QueryValueMapUnexpectedValue is TournamentFees -> if (session.tournamentEntryFee is S) { session.tournamentEntryFee as S } else throw PokerAnalyticsException.QueryValueMapUnexpectedValue is Blinds -> if (session.blinds is S) { session.blinds as S } else throw PokerAnalyticsException.QueryValueMapUnexpectedValue else -> null } }.distinct() return compareList(values = values) } return listOf() } } object Bankrolls : RealmCriteria(1) object Games : RealmCriteria(2) object TournamentNames : RealmCriteria(3) object Locations : RealmCriteria(4) object TournamentFeatures : RealmCriteria(5) object TransactionTypes : RealmCriteria(6) object Limits : ListCriteria(7) object TableSizes : ListCriteria(8) object TournamentTypes : ListCriteria(9) object MonthsOfYear : SimpleCriteria(List(12) { index -> QueryCondition.AnyMonthOfYear().apply { listOfValues = arrayListOf(index) } }, 10) object DaysOfWeek : SimpleCriteria(List(7) { index -> QueryCondition.AnyDayOfWeek().apply { listOfValues = arrayListOf(index + 1) } }, 11) object SessionTypes : SimpleCriteria(listOf(QueryCondition.IsCash, QueryCondition.IsTournament), 12) object BankrollTypes : SimpleCriteria(listOf(QueryCondition.IsLive, QueryCondition.IsOnline), 13) object DayPeriods : SimpleCriteria(listOf(QueryCondition.IsWeekDay, QueryCondition.IsWeekEnd), 14) object Years : ListCriteria(15) object AllMonthsUpToNow : ListCriteria(16) object Blinds : ListCriteria(17) object TournamentFees : ListCriteria(18) object Cash : SimpleCriteria(listOf(QueryCondition.IsCash), 19) object Tournament : SimpleCriteria(listOf(QueryCondition.IsTournament), 20) data class ListCustomFields(override var customFieldId: String) : RealmCriteria(21), CustomFieldCriteria data class ValueCustomFields(override var customFieldId: String) : ListCriteria(22), CustomFieldCriteria val queries: List get() { return when (this) { is AllMonthsUpToNow -> { val realm = Realm.getDefaultInstance() val firstSession = realm.where().isNotNull("startDate").sort("startDate", Sort.ASCENDING).findFirst() val lastSession = realm.where().isNotNull("startDate").sort("startDate", Sort.DESCENDING).findFirst() realm.close() val years: ArrayList = arrayListOf() val firstYear = firstSession?.year ?: return years val firstMonth = firstSession.month ?: return years val lastYear = lastSession?.year ?: return years val lastMonth = lastSession.month ?: return years for (year in firstYear..lastYear) { val currentYear = QueryCondition.AnyYear(year) for (month in 0..11) { if (year == firstYear && month < firstMonth) { continue } if (year == lastYear && month > lastMonth) { continue } val currentMonth = QueryCondition.AnyMonthOfYear(month) val query = Query(currentMonth, currentYear) years.add(query) } } years } else -> { return this.queryConditions } } } val queryConditions: List get() { return when (this) { is Bankrolls -> comparison() is Games -> comparison() is TournamentFeatures -> comparison() is TournamentNames -> comparison() is Locations -> comparison() is TransactionTypes -> comparison() is SimpleCriteria -> comparison() is Limits -> comparison() is TournamentTypes -> comparison() is TableSizes -> comparison() is TournamentFees -> comparison() is Years -> { val years = arrayListOf() val realm = Realm.getDefaultInstance() val lastSession = realm.where().isNotNull("startDate").sort("startDate", Sort.DESCENDING).findFirst() val yearNow = lastSession?.year ?: return years realm.where().isNotNull("startDate").sort("year", Sort.ASCENDING).findFirst()?.year?.let { for (index in 0..(yearNow - it)) { val yearCondition = QueryCondition.AnyYear().apply { listOfValues = arrayListOf(it + index) } years.add(Query(yearCondition)) } } realm.close() years } is Blinds -> comparison() is ListCustomFields -> comparison() is ValueCustomFields -> { val realm = Realm.getDefaultInstance() val queries = when (this.customFieldType(realm)) { CustomField.Type.AMOUNT.uniqueIdentifier -> comparison() CustomField.Type.NUMBER.uniqueIdentifier -> comparison() else -> throw PokerAnalyticsException.QueryTypeUnhandled } realm.close() queries } else -> throw PokerAnalyticsException.QueryTypeUnhandled } } override val resId: Int? get() { return when (this) { Bankrolls -> R.string.bankroll Games -> R.string.game TournamentNames -> R.string.tournament_name Locations -> R.string.location TournamentFeatures -> R.string.tournament_feature Limits -> R.string.limit TableSizes -> R.string.table_size TournamentTypes -> R.string.tournament_type MonthsOfYear -> R.string.month_of_the_year DaysOfWeek -> R.string.day_of_the_week SessionTypes -> R.string.cash_or_tournament BankrollTypes -> R.string.live_or_online DayPeriods -> R.string.weekdays_or_weekend Years -> R.string.year AllMonthsUpToNow -> R.string.month Blinds -> R.string.blind TournamentFees -> R.string.entry_fees // is ListCustomFields -> this.customField.resId // is ValueCustomFields -> this.customField.resId else -> null } } companion object : IntSearchable { inline fun , reified T : NameManageable> compare(): List { val objects = mutableListOf() val realm = Realm.getDefaultInstance() realm.where().findAll().forEach { val condition = (QueryCondition.getInstance() as S).apply { setObject(it) } objects.add(condition) } objects.sorted() realm.close() return objects.map { Query(it) } } inline fun , T : Any> compareList(values: List): List { val objects = mutableListOf() values.forEach { val condition = (S::class.java.newInstance()).apply { listOfValues = arrayListOf(it) } objects.add(condition) } objects.sorted() return objects.map { Query(it) } } // SavableEnum override fun valuesInternal(): Array { return all.toTypedArray() } val all: List get() { return listOf( Bankrolls, Games, TournamentNames, Locations, TournamentFeatures, Limits, TableSizes, TournamentTypes, MonthsOfYear, DaysOfWeek, SessionTypes, BankrollTypes, DayPeriods, Years, AllMonthsUpToNow, Blinds, TournamentFees ) } } } interface CustomFieldCriteria { var customFieldId: String fun customField(realm: Realm) : CustomField { return realm.findById(this.customFieldId) ?: throw PAIllegalStateException("Custom field not found") } fun customFieldType(realm: Realm): Int { return this.customField(realm).type } }