package top.bettercode.summer.tools.recipe.result

import top.bettercode.summer.tools.recipe.criteria.DoubleRange
import top.bettercode.summer.tools.recipe.criteria.RecipeRelation
import top.bettercode.summer.tools.recipe.material.MaterialIDs.Companion.toMaterialIDs
import top.bettercode.summer.tools.recipe.material.RecipeMaterialValue

/**
 *
 * @author <NAME>
 */
class RecipeExt(private val recipe: Recipe) {

    val RecipeMaterialValue.range: DoubleRange?
        get() {
            return recipe.requirement.materialRangeConstraints.filter { it.key.contains(this.id) }.values.firstOrNull()
        }

    val RecipeMaterialValue.double: Boolean
        get() {
            val relationMap = recipe.requirement.materialRelationConstraints.values.find { it.keys.any { m -> m.contains(this.id) } }
                    ?: return false
            val iDs = relationMap.keys.first { it.contains(this.id) }
            val recipeRelation = relationMap[iDs]
            return recipeRelation?.overdose != null || recipeRelation?.overdoseMaterial != null
        }

    val RecipeMaterialValue.relationName: String?
        get() {
            val materialRelationConstraints = recipe.requirement.materialRelationConstraints.filter { it.value.keys.any { m -> m.contains(this.id) } }
            val ids = materialRelationConstraints.keys.firstOrNull() ?: return null

            val materials = recipe.materials
            val usedIds = materials.filter { ids.contains(it.id) }.map { it.id }.toMaterialIDs()
            return usedIds.toString()
        }

    private val RecipeMaterialValue.replaceRate: Double
        get() {
            val materialRelationConstraints = recipe.requirement.materialRelationConstraints.filter { it.value.keys.any { m -> m.contains(this.id) } }
            val ids = materialRelationConstraints.keys.firstOrNull() ?: return 1.0

            val materials = recipe.materials
            val usedIds = materials.filter { ids.contains(it.id) }.map { it.id }.toMaterialIDs()
            return if (ids.replaceIds == usedIds) ids.replaceRate ?: 1.0 else 1.0
        }

    val RecipeMaterialValue.relationRate: RecipeRelation?
        get() {
            val relationMap = recipe.requirement.materialRelationConstraints.values.find { it.keys.any { m -> m.contains(this.id) } }
                    ?: return null
            val iDs = relationMap.keys.first { it.contains(this.id) }
            val recipeRelation = relationMap[iDs]

            return recipeRelation?.replaceRate(replaceRate)
        }


    val RecipeMaterialValue.relationValue: Pair<DoubleRange, DoubleRange>?
        get() {

            val relationRate = relationRate
            if (relationRate == null) {
                val materialRelationConstraints = recipe.requirement.materialRelationConstraints.filter { it.key.contains(this.id) }
                val entry = materialRelationConstraints.entries.firstOrNull() ?: return null
                recipe.apply {
                    return entry.relationValue
                }
            } else {
                var usedMinNormalWeight = 0.0
                var usedMaxNormalWeight = 0.0
                var usedMinOverdoseWeight = 0.0
                var usedMaxOverdoseWeight = 0.0

                val normal = relationRate.normal
                val overdose = relationRate.overdose
                if (normalWeight > 0) {
                    usedMinNormalWeight += normalWeight * normal.min * replaceRate
                    usedMaxNormalWeight += normalWeight * normal.max * replaceRate
                    if (overdose != null) {
                        usedMinOverdoseWeight += normalWeight * overdose.min * replaceRate
                        usedMaxOverdoseWeight += normalWeight * overdose.max * replaceRate
                    }
                } else {
                    usedMinNormalWeight += weight * normal.min * replaceRate
                    usedMaxNormalWeight += weight * normal.max * replaceRate
                    if (overdose != null) {
                        usedMinOverdoseWeight += weight * overdose.min * replaceRate
                        usedMaxOverdoseWeight += weight * overdose.max * replaceRate
                    }
                }
                //过量物料
                var usedMinOverdoseMaterialWeight = 0.0
                var usedMaxOverdoseMaterialWeight = 0.0
                val overdoseMaterial = relationRate.overdoseMaterial
                if (overdoseMaterial != null && overdoseWeight > 0) {
                    val overdoseMaterialNormal = overdoseMaterial.normal
                    val overdoseMaterialOverdose = overdoseMaterial.overdose
                    usedMinOverdoseMaterialWeight += overdoseWeight * overdoseMaterialNormal.min * replaceRate
                    usedMaxOverdoseMaterialWeight += overdoseWeight * overdoseMaterialNormal.max * replaceRate
                    if (overdoseMaterialOverdose != null) {
                        usedMinOverdoseMaterialWeight += overdoseWeight * overdoseMaterialOverdose.min * replaceRate
                        usedMaxOverdoseMaterialWeight += overdoseWeight * overdoseMaterialOverdose.max * replaceRate
                    }
                }
                if (usedMinOverdoseWeight == 0.0) {
                    usedMinOverdoseWeight = usedMinOverdoseMaterialWeight
                }
                if (usedMaxOverdoseWeight == 0.0) {
                    usedMaxOverdoseWeight = usedMaxOverdoseMaterialWeight
                }

                return DoubleRange(usedMinNormalWeight, usedMaxNormalWeight) to DoubleRange(usedMinOverdoseWeight, usedMaxOverdoseWeight)
            }
        }

}
package top.bettercode.summer.tools.recipe.result

import top.bettercode.summer.tools.recipe.criteria.DoubleRange
import top.bettercode.summer.tools.recipe.criteria.RecipeRelation
import top.bettercode.summer.tools.recipe.material.MaterialIDs.Companion.toMaterialIDs
import top.bettercode.summer.tools.recipe.material.RecipeMaterialValue

/**
 *
 * @author Peter Wu
 */
class RecipeExt(private val recipe: Recipe) {

    val RecipeMaterialValue.range: DoubleRange?
        get() {
            return recipe.requirement.materialRangeConstraints.filter { it.key.contains(this.id) }.values.firstOrNull()
        }

    val RecipeMaterialValue.double: Boolean
        get() {
            val relationMap = recipe.requirement.materialRelationConstraints.values.find { it.keys.any { m -> m.contains(this.id) } }
                    ?: return false
            val iDs = relationMap.keys.first { it.contains(this.id) }
            val recipeRelation = relationMap[iDs]
            return recipeRelation?.overdose != null || recipeRelation?.overdoseMaterial != null
        }

    val RecipeMaterialValue.relationName: String?
        get() {
            val materialRelationConstraints = recipe.requirement.materialRelationConstraints.filter { it.value.keys.any { m -> m.contains(this.id) } }
            val ids = materialRelationConstraints.keys.firstOrNull() ?: return null

            val materials = recipe.materials
            val usedIds = materials.filter { ids.contains(it.id) }.map { it.id }.toMaterialIDs()
            return usedIds.toString()
        }

    private val RecipeMaterialValue.replaceRate: Double
        get() {
            val materialRelationConstraints = recipe.requirement.materialRelationConstraints.filter { it.value.keys.any { m -> m.contains(this.id) } }
            val ids = materialRelationConstraints.keys.firstOrNull() ?: return 1.0

            val materials = recipe.materials
            val usedIds = materials.filter { ids.contains(it.id) }.map { it.id }.toMaterialIDs()
            return if (ids.replaceIds == usedIds) ids.replaceRate ?: 1.0 else 1.0
        }

    val RecipeMaterialValue.relationRate: RecipeRelation?
        get() {
            val relationMap = recipe.requirement.materialRelationConstraints.values.find { it.keys.any { m -> m.contains(this.id) } }
                    ?: return null
            val iDs = relationMap.keys.first { it.contains(this.id) }
            val recipeRelation = relationMap[iDs]

            return recipeRelation?.replaceRate(replaceRate)
        }


    val RecipeMaterialValue.relationValue: Pair<DoubleRange, DoubleRange>?
        get() {

            val relationRate = relationRate
            if (relationRate == null) {
                val materialRelationConstraints = recipe.requirement.materialRelationConstraints.filter { it.key.contains(this.id) }
                val entry = materialRelationConstraints.entries.firstOrNull() ?: return null
                recipe.apply {
                    return entry.relationValue
                }
            } else {
                var usedMinNormalWeight = 0.0
                var usedMaxNormalWeight = 0.0
                var usedMinOverdoseWeight = 0.0
                var usedMaxOverdoseWeight = 0.0

                val normal = relationRate.normal
                val overdose = relationRate.overdose
                if (normalWeight > 0) {
                    usedMinNormalWeight += normalWeight * normal.min * replaceRate
                    usedMaxNormalWeight += normalWeight * normal.max * replaceRate
                    if (overdose != null) {
                        usedMinOverdoseWeight += normalWeight * overdose.min * replaceRate
                        usedMaxOverdoseWeight += normalWeight * overdose.max * replaceRate
                    }
                } else {
                    usedMinNormalWeight += weight * normal.min * replaceRate
                    usedMaxNormalWeight += weight * normal.max * replaceRate
                    if (overdose != null) {
                        usedMinOverdoseWeight += weight * overdose.min * replaceRate
                        usedMaxOverdoseWeight += weight * overdose.max * replaceRate
                    }
                }
                //过量物料
                var usedMinOverdoseMaterialWeight = 0.0
                var usedMaxOverdoseMaterialWeight = 0.0
                val overdoseMaterial = relationRate.overdoseMaterial
                if (overdoseMaterial != null && overdoseWeight > 0) {
                    val overdoseMaterialNormal = overdoseMaterial.normal
                    val overdoseMaterialOverdose = overdoseMaterial.overdose
                    usedMinOverdoseMaterialWeight += overdoseWeight * overdoseMaterialNormal.min * replaceRate
                    usedMaxOverdoseMaterialWeight += overdoseWeight * overdoseMaterialNormal.max * replaceRate
                    if (overdoseMaterialOverdose != null) {
                        usedMinOverdoseMaterialWeight += overdoseWeight * overdoseMaterialOverdose.min * replaceRate
                        usedMaxOverdoseMaterialWeight += overdoseWeight * overdoseMaterialOverdose.max * replaceRate
                    }
                }
                if (usedMinOverdoseWeight == 0.0) {
                    usedMinOverdoseWeight = usedMinOverdoseMaterialWeight
                }
                if (usedMaxOverdoseWeight == 0.0) {
                    usedMaxOverdoseWeight = usedMaxOverdoseMaterialWeight
                }

                return DoubleRange(usedMinNormalWeight, usedMaxNormalWeight) to DoubleRange(usedMinOverdoseWeight, usedMaxOverdoseWeight)
            }
        }

}