package com.dowstats.service.w40k

import com.dowstats.Metadata
import com.dowstats.data.entities.ArmorType
import com.dowstats.data.entities.Mod
import com.dowstats.data.entities.ModifiersBase.ModifiersBaseCompanion.fillDataFromRgdTable
import com.dowstats.data.entities.ModifiersBase.ModifiersBaseCompanion.filterUseless
import com.dowstats.data.entities.weapon.Weapon
import com.dowstats.data.entities.weapon.WeaponArmorPiercing
import com.dowstats.data.entities.weapon.WeaponModifiers
import com.dowstats.data.entities.weapon.WeaponRequirements
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.getPureIntByName
import com.dowstats.data.rgd.RgdDataUtil.getRgdTableByName
import com.dowstats.data.rgd.RgdDataUtil.getStringByName
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import java.math.BigDecimal

@Service
class WeaponRgdExtractService @Autowired constructor(
    private val commonParseRgdService: CommonParseRgdService,
) {

    val log = LoggerFactory.getLogger(WeaponRgdExtractService::class.java)


    fun extractToWeaponEntity(weaponFileName: String, weaponData: List<RgdData>, mod: Mod, armorTypes: Set<ArmorType>, modFolderData: String, modDictionary: Map<Int, String>): Weapon? {

        val weapon = Weapon()

        weapon.filename = weaponFileName

        val weaponUiInfo = weaponData.getRgdTableByName("ui_info")
        val weaponUiData = weaponUiInfo?.let{ commonParseRgdService.getUiInfo(weaponUiInfo, modDictionary, modFolderData, mod, log)}
        weapon.name = weaponUiData?.name
        weapon.icon = weaponUiData?.iconPath
        weapon.description = weaponUiData?.description
        weapon.showInReinforce = weaponUiInfo?.getBooleanByName("show_in_reinforce") ?: false
        weapon.uiHotkeyName = weaponData.getStringByName("ui_hotkey_name")

        val cost = commonParseRgdService.getBuildCost(weaponData.getRgdTableByName("cost"))
        weapon.costRequisition = cost.requisition ?: 0.0
        weapon.costPower = cost.power ?: 0.0
        weapon.costTimeSeconds = cost.time
        weapon.accuracy = weaponData.getDoubleByName("accuracy") ?: 1.0
        weapon.accuracyReductionMoving = weaponData.getDoubleByName("accuracy_reduction_when_moving")
        weapon.minRange = weaponData.getDoubleByName("min_range")
        weapon.maxRange = weaponData.getDoubleByName("max_range")

        weapon.reloadTime = weaponData.getDoubleByName("reload_time")
        weapon.setupTime =  weaponData.getDoubleByName("setup_time")
        weapon.isMeleeWeapon = weaponData.getBooleanByName("melee_weapon") ?: false
        weapon.canAttackAir = weaponData.getBooleanByName("can_attack_air_units") ?: true
        weapon.canAttackGround = weaponData.getBooleanByName("can_attack_ground_units") ?: true


        val areaEffectTable = weaponData.getRgdTableByName("area_effect")

        if (areaEffectTable != null) {
            val areaEffectData = commonParseRgdService.getAreaEffectData(areaEffectTable)

            val armoursPiercing = armorTypes.map {
                val weaponArmourPiercing = WeaponArmorPiercing()
                weaponArmourPiercing.weapon = weapon
                weaponArmourPiercing.armorType = it
                val piercingValue =
                    (areaEffectData.weaponDmgMap[it.id] ?: areaEffectData.defaultArmorPiercing).toBigDecimal()
                weaponArmourPiercing.piercingValue =
                    piercingValue.let { pv -> if (pv <= BigDecimal(100)) pv else BigDecimal(100) }
                weaponArmourPiercing
            }

            weapon.minDamageValue = areaEffectData.minDamageValue
            weapon.minDamage = areaEffectData.minDamage
            weapon.maxDamage = areaEffectData.maxDamage
            weapon.throwForceMin = areaEffectData.throwForceMin
            weapon.throwForceMax = areaEffectData.throwForceMax
            weapon.moraleDamage = areaEffectData.moraleDamage
            weapon.weaponPiercings = armoursPiercing
            weapon.damageRadius = areaEffectData.damageRadius
        }

        weapon.modId = mod.id
        weapon.weaponModifiers = getWeaponModifiers(weapon, weaponData).toMutableSet()
        weapon.weaponRequirements = getWeaponRequirements(weapon, weaponData).toMutableSet()

        return if(weapon.minDamage == 0.0 && weapon.maxDamage == 0.0 && weapon.moraleDamage == 0.0){
            null
        } else weapon
    }

    private fun getWeaponModifiers(weapon: Weapon, weaponData: List<RgdData>?): List<WeaponModifiers> =
        weaponData?.getRgdTableByName("area_effect")
            ?.getRgdTableByName("weapon_damage")
            ?.getRgdTableByName("modifiers")
            ?.mapNotNull { m ->
            if (m.name.contains("modifier_")) {
                val mTable = m.value as List<RgdData>
                val modifier = mTable.getRgdTableByName("modifier")
                val maxLifetime = try {
                    mTable.getDoubleByName("max_lifetime")
                } catch (e: Exception) {
                    mTable.getPureIntByName("max_lifetime")?.toDouble()
                }
                if (modifier?.getStringByName("\$REF") == "modifiers\\no_modifier.lua" || modifier == null) null else {
                    WeaponModifiers().also {
                        it.weapon = weapon
                        it.maxLifeTime = maxLifetime
                    }.fillDataFromRgdTable(modifier)
                }
            } else null
        }?.filterUseless() ?: emptyList()

    private fun getWeaponRequirements(weapon: Weapon, weaponData: List<RgdData>): List<WeaponRequirements> {
        return try{
            val requirements = weaponData.getRgdTableByName("requirements")
            requirements?.mapNotNull { r ->
                if (r.name.contains("required_")) {
                    try {
                        val rTable = r.value as List<RgdData>
                        if (rTable.getStringByName("\$REF")?.contains(Metadata.Requirements.REFERENCE_REQUIREMENT_NONE) == true) null else {
                            WeaponRequirements().also {
                                it.weapon = weapon
                                it.reference = rTable.getStringByName("\$REF")
                                it.value = commonParseRgdService.getRequirementReference(it.reference, rTable, weapon.filename!!, log)
                            }
                        }
                    } catch (e: Exception){
                        log.error("Error parsing requirement ${r.name} weapon ${weapon.name}", e)
                        null
                    }

                } else null
            } ?: emptyList()
        }  catch (e: Exception){
            log.error("Error parsing requirement weapon ${weapon.name}", e)
            emptyList()
        }

    }


}
