package com.dowstats.service.w40k

import com.dowstats.Metadata.Requirements
import com.dowstats.data.dto.AreaEffect
import com.dowstats.data.dto.BuildCost
import com.dowstats.data.dto.ResourceIncome
import com.dowstats.data.dto.UiInfo
import com.dowstats.data.entities.ArmorType
import com.dowstats.data.entities.Mod
import com.dowstats.data.entities.ability.Ability
import com.dowstats.data.rgd.RgdData
import com.dowstats.data.rgd.RgdDataUtil.getBooleanByName
import com.dowstats.data.rgd.RgdDataUtil.getDoubleByName
import com.dowstats.data.rgd.RgdDataUtil.getIntByName
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
import com.dowstats.data.rgd.RgdDataUtil.getStringByName
import org.slf4j.Logger
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

@Service
class CommonParseRgdService @Autowired constructor(
    private val modAttribPathService: ModAttribPathService,
    private val iconsService: IconsService,
) {


    fun getRequirementReference(ref: String?, rTable: List<RgdData>, fileName: String, log: Logger): String? =
        when (ref?.getPureReference()) {
        Requirements.REFERENCE_REQUIREMENT_POP -> rTable.getDoubleByName("population_required").toString()
        Requirements.REFERENCE_REQUIREMENT_ADDON -> rTable.getStringByName("addon_name")
        Requirements.REFERENCE_GLOBAL_REQUIREMENT_ADDON -> rTable.getStringByName("global_addon_name")
        Requirements.REFERENCE_REQUIREMENT_STRUCTURE_EITHER -> rTable.getStringByName("structure_name_or") + ";" +
                rTable.getStringByName("structure_name_either")
        Requirements.REFERENCE_REQUIREMENT_STRUCTURE -> rTable.getStringByName("structure_name")
        Requirements.REFERENCE_REQUIREMENT_RESEARCH -> rTable.getStringByName("research_name") + ";" +
                rTable.getBooleanByName("research_must_not_be_complete")
        Requirements.REFERENCE_REQUIRED_RESEARCH_EITHER -> rTable.getStringByName("research_name_either") + ";" +
                rTable.getStringByName("research_name_or")
        Requirements.REFERENCE_REQUIREMENT_OWNERSHIP -> rTable.getStringByName("own_name")
        Requirements.REFERENCE_REQUIREMENT_CAP -> rTable.getIntByName("max_cap").toString()
        Requirements.REFERENCE_REQUIREMENT_SQUAD_CAP -> rTable.getIntByName("max_squad_cap").toString()
        Requirements.REFERENCE_REQUIREMENT_CUM_SQUAD_CAP -> rTable.getIntByName("max_cumulative_squad_cap").toString()
        Requirements.REFERENCE_REQUIREMENT_STRUCTURE_RATIO -> rTable.getStringByName("required_structure_name") + ";" +
                rTable.getIntByName("this_structure_count").toString()
        Requirements.REFERENCE_REQUIREMENT_MOB_BONUS -> rTable.getDoubleByName("mobvalue_required").toString() + ";" +
                rTable.getIntByName("proximity_required").toString()
        Requirements.REFERENCE_REQUIREMENT_STRUCTURE_EXCLUSIVE -> rTable.getStringByName("structure_name_exclusive")
        Requirements.REFERENCE_LOCAL_REQUIRED_ADDON_EXCLUSIVE -> rTable.getStringByName("mutually_exclusive_with")
        Requirements.REFERENCE_REQUIREMENT_SQUAD -> rTable.getStringByName("squad_name") + ";" +
                rTable.getIntByName("min_count")
        else -> {
            log.warn("Unknown requirement reference $ref at $fileName")
            null
        }
    }

    fun getBuildCost(cost: List<RgdData>?): BuildCost {

        val costResources = cost?.getRgdTableByName("cost")

        return BuildCost(
            costResources?.getDoubleByName("requisition"),
            costResources?.getDoubleByName("power"),
            costResources?.getDoubleByName("population"),
            costResources?.getDoubleByName("faith"),
            costResources?.getDoubleByName("souls"),
            cost?.getDoubleByName("time_seconds")
        )
    }

    fun getResourceIncome(resourceExt: List<RgdData>?): ResourceIncome {
        return ResourceIncome(
            resourceExt?.getDoubleByName("faith_per_second")?.let { it * 10 },
            resourceExt?.getDoubleByName("power_per_second")?.let { it * 10 },
            resourceExt?.getDoubleByName("requisition_per_second")?.let { it * 10 },
        )
    }


    fun getUiInfo(uiInfoRgdData: List<RgdData>, modDictionary: Map<Int, String>, modFolderData: String, mod: Mod, log: Logger, isOnOfAbility: Boolean = false): UiInfo {
        val nameRef = uiInfoRgdData?.getStringByName("screen_name_id")?.replace("$", "")
        val name = nameRef?.let { try { modDictionary[it.toInt()] } catch (e: Exception) { it }  }

        val descriptionRefs = uiInfoRgdData.getRgdTableByName("help_text_list")
            ?.mapNotNull{try {
                (it.value as String).replace("$", "")
            } catch (e: Exception) {
                log.error("Error parsing ui help_text weapon $name", e)
                null
            }}
            ?.filter{it != "0" && it != "tables\\text_table.lua" && it != ""}
            ?.sortedBy { try { it.toInt() } catch (e: Exception) { 0 } }

        val description = try {
            descriptionRefs?.map {  try { modDictionary[it.toInt()] } catch (e: Exception) { it } }?.joinToString ( "\n" )
        } catch(e:Exception) {
            log.warn("Error parsing ui description weapon $name", e)
            null
        }

        val icon = try {
            val iconPath = if (isOnOfAbility) {
                uiInfoRgdData.getStringByName("icon_name") + "_off"
            } else uiInfoRgdData.getStringByName("icon_name")

            iconPath?.let { modAttribPathService.getIconPath(modFolderData, iconPath) }
                ?.let { iconsService.convertTgaToJpegImage(iconPath, it, mod.name) }
        } catch (e: Exception) {
            log.error("Error parsing ui icon path for $name", e)
            null
        }

        return UiInfo(name, description, icon)
    }

    fun getArmorType(
        entityData: List<RgdData>,
        armorTypes: Iterable<ArmorType>,
        armorTypeTableName: String
    ): ArmorType? {
        val armorType = entityData.getRgdTableByName("type_ext")
            ?.getRgdTableByName(armorTypeTableName)
            ?.getStringByName("\$REF")

        return armorTypes.find { it.id == armorType
            ?.replace("TYPE_Armour\\tp_", "")
            ?.replace("type_armour\\tp_", "")
            ?.replace(".lua", "") }
    }

    fun getAreaEffectData(areaEffectData: List<RgdData>): AreaEffect {

        val areaEffectInformation = areaEffectData.getRgdTableByName("area_effect_information")

        val areaType = areaEffectInformation?.getRgdTableByName("area_type")
            ?.getStringByName("\$REF")
            ?.replace("type_areaeffect\\tp_","")
            ?.replace(".lua","")

        val filterType = areaEffectInformation?.getRgdTableByName("filter_type")
            ?.getStringByName("\$REF")
            ?.replace("type_areafilter\\tp_","")
            ?.replace(".lua","")

        val cantHaveRadius = areaEffectInformation?.equals("area_effect_point") == true
        val damageRadius = if(cantHaveRadius) 0.0 else areaEffectInformation?.getDoubleByName("radius") ?: 0.0

        val throwData = areaEffectData.getRgdTableByName("throw_data")
        val forceMin = throwData?.getDoubleByName("force_min") ?: 0.0
        val forceMax = throwData?.getDoubleByName("force_max") ?: 0.0

        val armourDamage = areaEffectData
            .getRgdTableByName("weapon_damage")
            ?.getRgdTableByName("armour_damage")
        val minDamage = armourDamage?.getDoubleByName("min_damage") ?: 0.0
        val maxDamage = armourDamage?.getDoubleByName("max_damage") ?: 0.0
        val minDamageValue = armourDamage?.getDoubleByName("min_damage_value") ?: 0.0
        val moraleDamage = armourDamage?.getDoubleByName("morale_damage") ?: 0.0



        val defaultArmourPiercing = armourDamage?.getDoubleByName("armour_piercing") ?: 0.0
        val weaponDmgMap: Map<String, Double> =
            armourDamage?.getRgdTableByName("armour_piercing_types")?.mapNotNull { armour_piercing ->
                if (armour_piercing.name.contains("entry")) {
                    val entry = armour_piercing.value as List<RgdData>
                    val dmgType = entry.getRgdTableByName("armour_type")?.getStringByName("\$REF")?.replace("type_armour\\tp_","")?.replace(".lua","")
                    val dmgValue = entry.getDoubleByName("armour_piercing_value")
                    dmgType!! to dmgValue!!
                } else null
            }?.toMap() ?: emptyMap()



        return AreaEffect(
            minDamage,
            maxDamage,
            damageRadius,
            forceMin,
            forceMax,
            minDamageValue,
            moraleDamage,
            weaponDmgMap,
            defaultArmourPiercing,
            areaType,
            filterType
        )
    }

    fun getAbilities(entityData: List<RgdData>?, abilities: Set<Ability>): List<Ability> = entityData
        ?.getRgdTableByName("ability_ext")
        ?.getRgdTableByName("abilities")
        ?.mapNotNull { ability ->
            if (ability.name.contains("ability_")) {
                val abilityFileName = (ability.value as String?)?.let {
                    it.replace("abilities\\", "").replace(".lua", "").replace(".rgd", "")
                }
                abilities.find { it.filename?.replace(".rgd", "") == abilityFileName }
            } else null
        } ?: emptyList()

    fun String.getPureReference(): String =
        this.split("\\").last()
            .replace(".lua", "")
            .replace(".rgd", "")

}

