import { BuffObjectTypes } from "../../../../../../generator/BuffObjectTypes.js";
import { UnlockableElementsManager } from "../../../../../../model/unlocks/UnlockableElementsManager.js";
import { MirMarkerTypes } from "../../../../../generator/MirMarkerTypes.js";
import { bitwiseAnd } from "../../../../src/core/binary/operations/bitwiseAnd.js";
import { CellFilterLiteralFloat } from "../../../../src/generation/filtering/numeric/CellFilterLiteralFloat.js";
import { GridTaskActionRuleSet } from "../../../../src/generation/grid/generation/discrete/GridTaskActionRuleSet.js";
import { GridActionRuleSet } from "../../../../src/generation/markers/GridActionRuleSet.js";
import { GridCellActionPlaceMarker } from "../../../../src/generation/markers/GridCellActionPlaceMarker.js";
import { GridCellActionPlaceMarkerGroup } from "../../../../src/generation/markers/GridCellActionPlaceMarkerGroup.js";
import { MarkerNodeMatcherAnd } from "../../../../src/generation/markers/matcher/MarkerNodeMatcherAnd.js";
import { MarkerNodeMatcherByType } from "../../../../src/generation/markers/matcher/MarkerNodeMatcherByType.js";
import {
    MarkerNodeMatcherContainsTag
} from "../../../../src/generation/markers/matcher/MarkerNodeMatcherContainsTag.js";
import { RuleSelectionPolicyType } from "../../../../src/generation/markers/RuleSelectionPolicyType.js";
import { GridCellActionPlaceTags } from "../../../../src/generation/placement/action/GridCellActionPlaceTags.js";
import { GridCellActionSequence } from "../../../../src/generation/placement/action/util/GridCellActionSequence.js";
import { GridCellPlacementRule } from "../../../../src/generation/placement/GridCellPlacementRule.js";
import {
    CellMatcherContainsMarkerWithinRadius
} from "../../../../src/generation/rules/cell/CellMatcherContainsMarkerWithinRadius.js";
import { CellMatcherGridPattern } from "../../../../src/generation/rules/cell/CellMatcherGridPattern.js";
import { GridPatternMatcherCell } from "../../../../src/generation/rules/cell/GridPatternMatcherCell.js";
import { CellMatcherAnd } from "../../../../src/generation/rules/logic/CellMatcherAnd.js";
import { CellMatcherNot } from "../../../../src/generation/rules/logic/CellMatcherNot.js";
import { CellMatcherOr } from "../../../../src/generation/rules/logic/CellMatcherOr.js";
import { GridTags } from "../../grid/GridTags.js";
import { MirGridLayers } from "../../grid/MirGridLayers.js";
import { matcher_tag_not_traversable } from "../../rules/matcher_tag_not_traversable.js";
import { matcher_tag_traversable_unoccupied } from "../../rules/matcher_tag_traversable_unoccupied.js";

const TAG_MAJOR = 'Major Buff';
const TAG_MINOR = 'Minor Buff';
const TAG_UTILITY = 'Utility Buff';


const pMatcherNextToWall = new CellMatcherGridPattern();

pMatcherNextToWall.addRule(0, 1, matcher_tag_not_traversable);
pMatcherNextToWall.addRule(0, 0, matcher_tag_traversable_unoccupied);
pMatcherNextToWall.addRule(0, -1, matcher_tag_traversable_unoccupied);
pMatcherNextToWall.addRule(0, -2, matcher_tag_traversable_unoccupied);


const mBuffObjectNearby = CellMatcherContainsMarkerWithinRadius.from(MarkerNodeMatcherContainsTag.from(MirMarkerTypes.BuffObject), 6);

const mNoBuffObjectsNearby = CellMatcherNot.from(
    mBuffObjectNearby
);

const mNoMajorBuffObjectNearby = CellMatcherNot.from(
    CellMatcherContainsMarkerWithinRadius.from(
        MarkerNodeMatcherAnd.from(
            MarkerNodeMatcherContainsTag.from(MirMarkerTypes.BuffObject),
            MarkerNodeMatcherContainsTag.from(TAG_MAJOR)
        ), 21
    )
);

const mNoMinorBuffObjectNearby = CellMatcherNot.from(
    CellMatcherContainsMarkerWithinRadius.from(
        MarkerNodeMatcherAnd.from(
            MarkerNodeMatcherContainsTag.from(MirMarkerTypes.BuffObject),
            MarkerNodeMatcherContainsTag.from(TAG_MINOR)
        ), 7
    )
);

const mNoUtilityBuffObjectNearby = CellMatcherNot.from(
    CellMatcherContainsMarkerWithinRadius.from(
        MarkerNodeMatcherAnd.from(
            MarkerNodeMatcherContainsTag.from(MirMarkerTypes.BuffObject),
            MarkerNodeMatcherContainsTag.from(TAG_MINOR)
        ), 10
    )
);

pMatcherNextToWall.addRule(0, 0, mNoBuffObjectsNearby);

const placeTags = new GridCellActionPlaceTags();

placeTags.layerId = MirGridLayers.Tags;
placeTags.resize(1, 1);
placeTags.fill(GridTags.Occupied);


const clearTags = new GridCellActionPlaceTags();

clearTags.layerId = MirGridLayers.Tags;
clearTags.resize(1, 1);
clearTags.fill(~GridTags.Traversable);
clearTags.operation = bitwiseAnd;

const placeRoadConnector0 = GridCellActionPlaceMarker.from({ type: 'Road Connector' });
placeRoadConnector0.offset.set(0, -1);
placeRoadConnector0.properties.connectivity = 0.1;

const placeRoadMarkers = GridCellActionPlaceMarkerGroup.from([placeRoadConnector0]);


/**
 *
 * @param {string} tag
 * @return {GridCellPlacementRule}
 */
function makeMajorRule(tag) {
    return GridCellPlacementRule.from({
        matcher: CellMatcherAnd.from(
            CellMatcherAnd.from(
                mNoMajorBuffObjectNearby,
                mNoBuffObjectsNearby
            ),
            CellMatcherAnd.from(
                pMatcherNextToWall,
                CellMatcherNot.from(
                    CellMatcherContainsMarkerWithinRadius.from(MarkerNodeMatcherByType.from(tag), 42)
                )
            )
        ),
        action: GridCellActionSequence.from([
            placeTags,
            clearTags,
            GridCellActionPlaceMarker.from({
                type: tag,
                size: 0.52,
                tags: [MirMarkerTypes.BuffObject, TAG_MAJOR]
            }),
            placeRoadMarkers
        ]),
        probability: CellFilterLiteralFloat.from(0.1)
    });
}

/**
 *
 * @param {string} tag
 * @return {GridCellPlacementRule}
 */
function makeMinorRule(tag) {
    return GridCellPlacementRule.from({
        matcher: CellMatcherAnd.from(
            CellMatcherGridPattern.from([
                GridPatternMatcherCell.from(matcher_tag_not_traversable, 0, -1),
                GridPatternMatcherCell.from(matcher_tag_not_traversable, 1, -1),
                GridPatternMatcherCell.from(matcher_tag_traversable_unoccupied, 0, 0),
                GridPatternMatcherCell.from(matcher_tag_not_traversable, 1, 0),
            ]),

            CellMatcherAnd.from(
                mNoMinorBuffObjectNearby,
                CellMatcherNot.from(
                    CellMatcherOr.from(
                        mBuffObjectNearby,
                        CellMatcherContainsMarkerWithinRadius.from(MarkerNodeMatcherByType.from(tag), 21)
                    )
                )
            )
        ),
        action: GridCellActionSequence.from([
            placeTags,
            clearTags,
            GridCellActionPlaceMarker.from({
                type: tag,
                size: 0.52,
                tags: [MirMarkerTypes.BuffObject, TAG_MINOR]
            })
        ]),
        probability: CellFilterLiteralFloat.from(0.1)
    });
}

/**
 *
 * @param {string} tag
 * @param {number} [frequency]
 * @return {GridCellPlacementRule}
 */
function makeUtilityRule(tag, frequency = 17) {
    return GridCellPlacementRule.from({
        matcher: CellMatcherAnd.from(
            matcher_tag_traversable_unoccupied,

            CellMatcherAnd.from(
                mNoUtilityBuffObjectNearby,
                CellMatcherNot.from(
                    CellMatcherOr.from(
                        mBuffObjectNearby,
                        CellMatcherContainsMarkerWithinRadius.from(MarkerNodeMatcherByType.from(tag), frequency)
                    )
                )
            )
        ),
        action: GridCellActionSequence.from([
            placeTags,
            clearTags,
            GridCellActionPlaceMarker.from({
                type: tag,
                size: 0.52,
                tags: [MirMarkerTypes.BuffObject, TAG_UTILITY]
            })
        ]),
        probability: CellFilterLiteralFloat.from(0.1)
    });
}

/**
 * @param {Engine} engine
 * @returns {GridTaskActionRuleSet}
 */
export const mir_generator_place_buff_objects = (engine) => {

    /**
     *
     * @type {GridCellPlacementRule[]}
     */
    const rules = [];


    /**
     *
     * @type {UnlockableElementsManager|undefined}
     */
    const plugin = engine.plugins.getPlugin(UnlockableElementsManager);


    /**
     *
     * @param {string} id
     * @param {string} type
     */
    function attemptMajorRule(id, type) {

        plugin.runIfElementValue(
            'object',
            `buff/major/${id}`,
            true,
            false,
            () => {
                rules.push(makeMajorRule(type));
            });
    }

    /**
     *
     * @param {string} id
     * @param {string} type
     */
    function attemptMinorRule(id, type) {

        plugin.runIfElementValue(
            'object',
            `buff/minor/${id}`,
            true,
            false,
            () => {
                rules.push(makeMinorRule(type));
            });
    }


    /**
     *
     * @param {string} id
     * @param {string} type
     * @param frequency
     */
    function attemptUtilityRule(id, type, frequency) {

        plugin.runIfElementValue(
            'object',
            `buff/utility/${id}`,
            true,
            false,
            () => {
                rules.push(makeUtilityRule(type, frequency));
            });
    }


    if (plugin !== undefined) {
        attemptMajorRule(
            'increase_ability_power',
            BuffObjectTypes.AttackPowerIncrease
        );
        attemptMajorRule(
            'increase_defense',
            BuffObjectTypes.DefenseIncrease
        );
        attemptMajorRule(
            'increase_health',
            BuffObjectTypes.HealthIncrease
        );
        attemptMajorRule(
            'increase_ability_power_and_armor_piercing',
            BuffObjectTypes.ABILITY_POWER_AND_ARMOR_PIERCING
        );
        attemptMajorRule(
            'increase_experience_modifier',
            BuffObjectTypes.EXPERIENCE_MODIFIER
        );
        attemptMajorRule(
            'increase_movement_distance_and_initiative',
            BuffObjectTypes.MOVEMENT_DISTANCE_AND_INITIATIVE
        );
        attemptMajorRule(
            'increase_life_steal_and_healing_effectiveness',
            BuffObjectTypes.LIFE_STEAL_AND_HEALING_EFFECTIVENESS
        );
        attemptMajorRule(
            'increase_life_steal_and_healing_effectiveness',
            BuffObjectTypes.LIFE_STEAL_AND_HEALING_EFFECTIVENESS
        );

        // Minor buffs

        attemptMinorRule(
            'well',
            BuffObjectTypes.Well
        );
        attemptMinorRule(
            'campfire',
            BuffObjectTypes.Campfire
        );

        // utility
        attemptUtilityRule('resurrect', BuffObjectTypes.RESURRECT_ALL, 30);
        attemptUtilityRule('heal_small', BuffObjectTypes.HEAL_SMALL, 17);

    }


    return GridTaskActionRuleSet.from({
        rules: GridActionRuleSet.from({
            rules: rules,
            policy: RuleSelectionPolicyType.Random
        })
    });
}
