package net.pokeranalytics.android.model.realm

import io.realm.RealmList
import io.realm.RealmObject
import net.pokeranalytics.android.exceptions.PokerAnalyticsException
import net.pokeranalytics.android.model.filter.QueryCondition
import java.util.*
import kotlin.collections.ArrayList

open class FilterCondition() : RealmObject() {

    private constructor(filterName:String, sectionName:String) : this() {
        this.filterName = filterName
        this.sectionName = sectionName
    }

    constructor(filterElementRows: ArrayList<QueryCondition>) : this(filterElementRows.first().baseId, filterElementRows.first().filterSectionRow.name) {
        val row = filterElementRows.first()
        this.filterName ?: throw PokerAnalyticsException.FilterElementUnknownName
        this.operator = row.operator.ordinal
        if (row is QueryCondition.CustomFieldRelated) {
            this.stringValue = row.customFieldId
        }
        when (row) {
            is QueryCondition.SingleInt -> this.setValue(row.singleValue?:throw PokerAnalyticsException.FilterElementExpectedValueMissing)
            is QueryCondition.SingleDate -> this.setValue(row.singleValue?:throw PokerAnalyticsException.FilterElementExpectedValueMissing)
            is QueryCondition.ListOfDouble -> this.setValues(filterElementRows.flatMap { (it as QueryCondition.ListOfDouble).listOfValues })
            is QueryCondition.ListOfInt -> this.setValues(filterElementRows.flatMap { (it as QueryCondition.ListOfInt).listOfValues })
            is QueryCondition.ListOfString -> this.setValues(filterElementRows.flatMap { (it as QueryCondition.ListOfString).listOfValues })
        }
    }

	var filterName: String? = null
	var sectionName: String? = null

    val queryCondition : QueryCondition
        get() = QueryCondition.valueOf<QueryCondition>(this.filterName ?: throw PokerAnalyticsException.FilterElementUnknownName)
            .apply {
                this.updateValueBy(this@FilterCondition)
            }

    var doubleValues: RealmList<Double>? = null
    var intValues: RealmList<Int>? = null
    var stringValues: RealmList<String>? = null
    var dateValue: Date? = null
    var doubleValue: Double? = null
    var intValue: Int? = null
    var stringValue: String? = null
    var operator: Int? = null

    inline fun <reified T:Any > getValues(): ArrayList < T > {
        return when (T::class) {
            Int::class -> ArrayList<T>().apply { intValues?.map { add(it as T) } }
            Double::class -> ArrayList<T>().apply { doubleValues?.map { add(it as T) } }
            String::class -> ArrayList<T>().apply { stringValues?.map { add(it as T) } }
            else -> throw PokerAnalyticsException.QueryValueMapUnexpectedValue
        }
    }

    inline fun <reified T:Any > getValue(): T {
        return when (T::class) {
            Int::class -> intValue ?: 0
            Double::class -> doubleValue?: 0.0
            Date::class -> dateValue ?: Date()
            String::class -> stringValue ?: ""
            else -> throw PokerAnalyticsException.QueryValueMapUnexpectedValue
        } as T
    }

    private inline fun <reified T> setValues(values:List<T>) {
        when (T::class) {
            Int::class -> intValues = RealmList<Int>().apply { values.map { it as Int }.forEach { add(it) } }
            Double::class -> doubleValues = RealmList<Double>().apply { values.map { it as Double }.forEach { add(it) } }
            String::class -> stringValues = RealmList<String>().apply { values.map { it as String }.forEach { add(it) } }
            else -> throw PokerAnalyticsException.QueryValueMapUnexpectedValue
        }
    }

    fun setValue(value:Double) {
        doubleValue = value
    }

    fun setValue(value:Date) {
        dateValue = value
    }

    fun setValue(value:Int) {
        intValue= value
    }

    fun setValue(value:String) {
        stringValue = value
    }
}
