/*
 * TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
package io.timelimit.android.data.model

import android.util.JsonReader
import android.util.JsonWriter
import androidx.room.*
import io.timelimit.android.data.JsonSerializable

@Entity(tableName = "config")
@TypeConverters(ConfigurationItemTypeConverter::class)
data class ConfigurationItem(
        @PrimaryKey
        @ColumnInfo(name = "id")
        val key: ConfigurationItemType,
        @ColumnInfo(name = "value")
        val value: String
): JsonSerializable {
    companion object {
        private const val KEY = "k"
        private const val VALUE = "v"

        // returns null if parsing failed
        fun parse(reader: JsonReader): ConfigurationItem? {
            var key: Int? = null
            var value: String? = null

            reader.beginObject()

            while (reader.hasNext()) {
                when (reader.nextName()) {
                    KEY -> key = reader.nextInt()
                    VALUE -> value = reader.nextString()
                    else -> reader.skipValue()
                }
            }

            reader.endObject()

            key!!
            value!!

            try {
                return ConfigurationItem(
                        key = ConfigurationItemTypeUtil.parse(key),
                        value = value
                )
            } catch (ex: Exception) {
                return null
            }
        }
    }

    override fun serialize(writer: JsonWriter) {
        writer.beginObject()

        writer.name(KEY).value(ConfigurationItemTypeUtil.serialize(key))
        writer.name(VALUE).value(value)

        writer.endObject()
    }
}

enum class ConfigurationItemType {
    OwnDeviceId,
    UserListVersion,
    DeviceListVersion,
    NextSyncSequenceNumber,
    DeviceAuthToken,
    FullVersionUntil,
    ShownHints,
    ObsoleteWasDeviceLocked,
    LastAppVersionWhichSynced,
    LastScreenOnTime,
    ServerMessage,
    CustomServerUrl,
    ForegroundAppQueryRange,
    EnableBackgroundSync,
    EnableAlternativeDurationSelection,
    ExperimentalFlags,
    DefaultHomescreen,
    HomescreenDelay,
    ParentModeKey,
    EnableUpdates,
    UpdateStatus,
    CustomOrganizationName,
    ServerApiLevel,
    AnnoyManualUnblockCounter,
    ConsentFlags,
    SigningKey,
    SignSequenceNumber,
    LastServerKeyRequestSequence,
    LastKeyResponseSequence,
    DhKey,
    DhKeyVersion,
    U2fListVersion
}

object ConfigurationItemTypeUtil {
    private const val OWN_DEVICE_ID = 1
    private const val USER_LIST_VERSION = 2
    private const val DEVICE_LIST_VERSION = 3
    private const val NEXT_SYNC_SEQUENCE_NUMBER = 4
    private const val DEVICE_AUTH_TOKEN = 5
    private const val FULL_VERSION_UNTIL = 6
    private const val SHOWN_HINTS = 7
    private const val WAS_DEVICE_LOCKED = 9
    private const val LAST_APP_VERSION_WHICH_SYNCED = 10
    private const val LAST_SCREEN_ON_TIME = 11
    private const val SERVER_MESSAGE = 12
    private const val CUSTOM_SERVER_URL = 13
    private const val FOREGROUND_APP_QUERY_RANGE = 14
    private const val ENABLE_BACKGROUND_SYNC = 15
    private const val ENABLE_ALTERNATIVE_DURATION_SELECTION = 16
    private const val EXPERIMENTAL_FLAGS = 17
    private const val DEFAULT_HOMESCREEN = 18
    private const val HOMESCREEN_DELAY = 19
    private const val PARENT_MODE_KEY = 20
    private const val ENABLE_UPDATES = 21
    private const val UPDATE_STATUS = 22
    private const val CUSTOM_ORGANIZATION_NAME = 23
    private const val SERVER_API_LEVEL = 24
    private const val ANNOY_MANUAL_UNBLOCK_COUNTER = 25
    private const val CONSENT_FLAGS = 26
    private const val SIGNING_KEY = 27
    private const val SIGN_SEQUENCE_NUMBER = 28
    private const val LAST_SERVER_KEY_REQUEST_SEQUENCE = 29
    private const val LAST_SERVER_KEY_RESPONSE_SEQUENCE = 30
    private const val DH_KEY = 31
    private const val DH_KEY_VERSION = 32
    private const val U2F_LIST_VERSION = 33

    val TYPES = listOf(
            ConfigurationItemType.OwnDeviceId,
            ConfigurationItemType.UserListVersion,
            ConfigurationItemType.DeviceListVersion,
            ConfigurationItemType.NextSyncSequenceNumber,
            ConfigurationItemType.DeviceAuthToken,
            ConfigurationItemType.FullVersionUntil,
            ConfigurationItemType.ShownHints,
            ConfigurationItemType.ObsoleteWasDeviceLocked,
            ConfigurationItemType.LastAppVersionWhichSynced,
            ConfigurationItemType.LastScreenOnTime,
            ConfigurationItemType.ServerMessage,
            ConfigurationItemType.CustomServerUrl,
            ConfigurationItemType.ForegroundAppQueryRange,
            ConfigurationItemType.EnableBackgroundSync,
            ConfigurationItemType.EnableAlternativeDurationSelection,
            ConfigurationItemType.ExperimentalFlags,
            ConfigurationItemType.DefaultHomescreen,
            ConfigurationItemType.HomescreenDelay,
            ConfigurationItemType.ParentModeKey,
            ConfigurationItemType.EnableUpdates,
            ConfigurationItemType.UpdateStatus,
            ConfigurationItemType.CustomOrganizationName,
            ConfigurationItemType.ServerApiLevel,
            ConfigurationItemType.AnnoyManualUnblockCounter,
            ConfigurationItemType.ConsentFlags,
            ConfigurationItemType.SigningKey,
            ConfigurationItemType.SignSequenceNumber,
            ConfigurationItemType.LastServerKeyRequestSequence,
            ConfigurationItemType.LastKeyResponseSequence,
            ConfigurationItemType.DhKey,
            ConfigurationItemType.DhKeyVersion,
            ConfigurationItemType.U2fListVersion
    )

    fun serialize(value: ConfigurationItemType) = when(value) {
        ConfigurationItemType.OwnDeviceId -> OWN_DEVICE_ID
        ConfigurationItemType.UserListVersion -> USER_LIST_VERSION
        ConfigurationItemType.DeviceListVersion -> DEVICE_LIST_VERSION
        ConfigurationItemType.NextSyncSequenceNumber -> NEXT_SYNC_SEQUENCE_NUMBER
        ConfigurationItemType.DeviceAuthToken -> DEVICE_AUTH_TOKEN
        ConfigurationItemType.FullVersionUntil -> FULL_VERSION_UNTIL
        ConfigurationItemType.ShownHints -> SHOWN_HINTS
        ConfigurationItemType.ObsoleteWasDeviceLocked -> WAS_DEVICE_LOCKED
        ConfigurationItemType.LastAppVersionWhichSynced -> LAST_APP_VERSION_WHICH_SYNCED
        ConfigurationItemType.LastScreenOnTime -> LAST_SCREEN_ON_TIME
        ConfigurationItemType.ServerMessage -> SERVER_MESSAGE
        ConfigurationItemType.CustomServerUrl -> CUSTOM_SERVER_URL
        ConfigurationItemType.ForegroundAppQueryRange -> FOREGROUND_APP_QUERY_RANGE
        ConfigurationItemType.EnableBackgroundSync -> ENABLE_BACKGROUND_SYNC
        ConfigurationItemType.EnableAlternativeDurationSelection -> ENABLE_ALTERNATIVE_DURATION_SELECTION
        ConfigurationItemType.ExperimentalFlags -> EXPERIMENTAL_FLAGS
        ConfigurationItemType.DefaultHomescreen -> DEFAULT_HOMESCREEN
        ConfigurationItemType.HomescreenDelay -> HOMESCREEN_DELAY
        ConfigurationItemType.ParentModeKey -> PARENT_MODE_KEY
        ConfigurationItemType.EnableUpdates -> ENABLE_UPDATES
        ConfigurationItemType.UpdateStatus -> UPDATE_STATUS
        ConfigurationItemType.CustomOrganizationName -> CUSTOM_ORGANIZATION_NAME
        ConfigurationItemType.ServerApiLevel -> SERVER_API_LEVEL
        ConfigurationItemType.AnnoyManualUnblockCounter -> ANNOY_MANUAL_UNBLOCK_COUNTER
        ConfigurationItemType.ConsentFlags -> CONSENT_FLAGS
        ConfigurationItemType.SigningKey -> SIGNING_KEY
        ConfigurationItemType.SignSequenceNumber -> SIGN_SEQUENCE_NUMBER
        ConfigurationItemType.LastServerKeyRequestSequence -> LAST_SERVER_KEY_REQUEST_SEQUENCE
        ConfigurationItemType.LastKeyResponseSequence -> LAST_SERVER_KEY_RESPONSE_SEQUENCE
        ConfigurationItemType.DhKey -> DH_KEY
        ConfigurationItemType.DhKeyVersion -> DH_KEY_VERSION
        ConfigurationItemType.U2fListVersion -> U2F_LIST_VERSION
    }

    fun parse(value: Int) = when(value) {
        OWN_DEVICE_ID -> ConfigurationItemType.OwnDeviceId
        USER_LIST_VERSION -> ConfigurationItemType.UserListVersion
        DEVICE_LIST_VERSION -> ConfigurationItemType.DeviceListVersion
        NEXT_SYNC_SEQUENCE_NUMBER -> ConfigurationItemType.NextSyncSequenceNumber
        DEVICE_AUTH_TOKEN -> ConfigurationItemType.DeviceAuthToken
        FULL_VERSION_UNTIL -> ConfigurationItemType.FullVersionUntil
        SHOWN_HINTS -> ConfigurationItemType.ShownHints
        WAS_DEVICE_LOCKED -> ConfigurationItemType.ObsoleteWasDeviceLocked
        LAST_APP_VERSION_WHICH_SYNCED -> ConfigurationItemType.LastAppVersionWhichSynced
        LAST_SCREEN_ON_TIME -> ConfigurationItemType.LastScreenOnTime
        SERVER_MESSAGE -> ConfigurationItemType.ServerMessage
        CUSTOM_SERVER_URL -> ConfigurationItemType.CustomServerUrl
        FOREGROUND_APP_QUERY_RANGE -> ConfigurationItemType.ForegroundAppQueryRange
        ENABLE_BACKGROUND_SYNC -> ConfigurationItemType.EnableBackgroundSync
        ENABLE_ALTERNATIVE_DURATION_SELECTION -> ConfigurationItemType.EnableAlternativeDurationSelection
        EXPERIMENTAL_FLAGS -> ConfigurationItemType.ExperimentalFlags
        DEFAULT_HOMESCREEN -> ConfigurationItemType.DefaultHomescreen
        HOMESCREEN_DELAY -> ConfigurationItemType.HomescreenDelay
        PARENT_MODE_KEY -> ConfigurationItemType.ParentModeKey
        ENABLE_UPDATES -> ConfigurationItemType.EnableUpdates
        UPDATE_STATUS -> ConfigurationItemType.UpdateStatus
        CUSTOM_ORGANIZATION_NAME -> ConfigurationItemType.CustomOrganizationName
        SERVER_API_LEVEL -> ConfigurationItemType.ServerApiLevel
        ANNOY_MANUAL_UNBLOCK_COUNTER -> ConfigurationItemType.AnnoyManualUnblockCounter
        CONSENT_FLAGS -> ConfigurationItemType.ConsentFlags
        SIGNING_KEY -> ConfigurationItemType.SigningKey
        SIGN_SEQUENCE_NUMBER -> ConfigurationItemType.SignSequenceNumber
        LAST_SERVER_KEY_REQUEST_SEQUENCE -> ConfigurationItemType.LastServerKeyRequestSequence
        LAST_SERVER_KEY_RESPONSE_SEQUENCE -> ConfigurationItemType.LastKeyResponseSequence
        DH_KEY -> ConfigurationItemType.DhKey
        DH_KEY_VERSION -> ConfigurationItemType.DhKeyVersion
        U2F_LIST_VERSION -> ConfigurationItemType.U2fListVersion
        else -> throw IllegalArgumentException()
    }
}

class ConfigurationItemTypeConverter {
    @TypeConverter
    fun toInt(value: ConfigurationItemType) = ConfigurationItemTypeUtil.serialize(value)

    @TypeConverter
    fun toConfigurationItemType(value: Int) = ConfigurationItemTypeUtil.parse(value)
}

object HintsToShow {
    const val OVERVIEW_INTRODUCTION = 1L
    const val DEVICE_SCREEN_INTRODUCTION = 2L
    const val CATEGORIES_INTRODUCTION = 4L
    const val TIME_LIMIT_RULE_INTRODUCTION = 8L
    const val CONTACTS_INTRO = 16L
    private const val OBSOLETE_TIMELIMIT_RULE_MUSTREAD = 32L
    private const val OBSOLETE_BLOCKED_TIME_AREAS_OBSOLETE = 64L
    const val TASKS_INTRODUCTION = 128L
    const val BLOCKED_TIME_AREAS = 1L shl 8
}

object ExperimentalFlags {
    private const val OBSOLETE_DISABLE_BLOCK_ON_MANIPULATION = 1L
    const val SYSTEM_LEVEL_BLOCKING = 2L
    private const val OBSOLETE_MANIPULATION_ANNOY_USER_ONLY = 4L
    const val IGNORE_SYSTEM_CONNECTION_STATUS = 8L
    const val CUSTOM_HOME_SCREEN = 16L
    const val CUSTOM_HOMESCREEN_DELAY = 32L
    const val NETWORKTIME_AT_SYSTEMLEVEL = 64L
    const val HIGH_MAIN_LOOP_DELAY = 128L
    // const val DISCONNECT_WHEN_SCREEN_OFF = 256L
    const val KEEP_CONNECTED_WHEN_SCREEN_OFF = 512L
    const val MULTI_APP_DETECTION = 1024L
    const val REQUIRE_SYNC_FOR_PARENT_LOGIN = 2048L
    // const val BLOCK_SPLIT_SCREEN = 4096L
    const val HIDE_MANIPULATION_WARNING = 8192L
    const val ENABLE_SOFT_BLOCKING = 16384L
    const val SYNC_RELATED_NOTIFICATIONS = 32768L
    // const val INSTANCE_ID_FG_APP_DETECTION = 65536L
    // private const val OBSOLETE_DISABLE_FG_APP_DETECTION_FALLBACK = 131072L
    const val STRICT_OVERLAY_CHECKING = 0x40000L
    // const val DISABLE_LEGACY_APP_SENDING = 0x80000L
}

object ConsentFlags {
    const val APP_LIST_SYNC = 1L
}