import { SceneSystem, Transform } from 'dacha';
import { EventType } from '../../../events';
import { ADD_VALUE } from '../../../command-types';
import { ROOT_SCOPE } from '../../../consts/scopes';
import { includesArray } from '../../../utils/includes-array';
import { getTool } from '../../../utils/get-tool';
import { getSavedSelectedSceneId } from '../../../utils/get-saved-selected-scene-id';
import { getSavedInspectedEntity } from '../../../utils/get-saved-inspected-entity';
import { getActorIdByPath } from '../../../utils/get-actor-id-by-path';
import { TOOL_NAME, PREVIEW_FEATURE_NAME, VIEW_COMPONENTS } from './consts';
import { createFromTemplate, updatePreviewPosition, isActorPath, } from './utils';
export class TemplateToolSystem extends SceneSystem {
    constructor(options) {
        super();
        this.handleSelectScene = (event) => {
            const { sceneId } = event;
            this.selectedSceneId = sceneId;
        };
        this.handleInspectEntity = (event) => {
            this.selectedActorPath = isActorPath(event.path) ? event.path : undefined;
        };
        this.handleToolCursorMove = (event) => {
            const { x, y } = event;
            this.cursor.x = x;
            this.cursor.y = y;
        };
        this.handleToolCursorLeave = () => {
            this.cursor.x = null;
            this.cursor.y = null;
        };
        this.handleAddFromTemplate = (event) => {
            var _a, _b, _c, _d, _e;
            if (!this.selectedSceneId) {
                return;
            }
            const { x, y } = event;
            this.cursor.x = x;
            this.cursor.y = y;
            updatePreviewPosition(this.cursor, this.world, this.preview);
            const tool = getTool(this.world);
            const templateId = tool.features.templateId.value;
            const nestToSelected = tool.features.nestToSelected.value;
            if (!templateId) {
                return;
            }
            const template = this.configStore.get([
                'templates',
                `id:${templateId}`,
            ]);
            const transform = (_a = this.preview) === null || _a === void 0 ? void 0 : _a.getComponent(Transform);
            let actorPositionX = (_b = transform === null || transform === void 0 ? void 0 : transform.world.position.x) !== null && _b !== void 0 ? _b : 0;
            let actorPositionY = (_c = transform === null || transform === void 0 ? void 0 : transform.world.position.y) !== null && _c !== void 0 ? _c : 0;
            if (nestToSelected && this.selectedActorPath) {
                const selectedActorId = getActorIdByPath(this.selectedActorPath);
                const parentActor = this.scene.findChildById(selectedActorId);
                const parentTransform = parentActor === null || parentActor === void 0 ? void 0 : parentActor.getComponent(Transform);
                const nestedPosition = parentTransform === null || parentTransform === void 0 ? void 0 : parentTransform.invertedWorldMatrix.apply({
                    x: actorPositionX,
                    y: actorPositionY,
                });
                actorPositionX = (_d = nestedPosition === null || nestedPosition === void 0 ? void 0 : nestedPosition.x) !== null && _d !== void 0 ? _d : 0;
                actorPositionY = (_e = nestedPosition === null || nestedPosition === void 0 ? void 0 : nestedPosition.y) !== null && _e !== void 0 ? _e : 0;
            }
            const path = nestToSelected && this.selectedActorPath
                ? this.selectedActorPath.concat('children')
                : ['scenes', `id:${this.selectedSceneId}`, 'actors'];
            const siblings = this.configStore.get(path);
            const actor = createFromTemplate(template, siblings, actorPositionX, actorPositionY);
            this.configStore.dispatch({
                command: ADD_VALUE,
                scope: ROOT_SCOPE,
                options: { path, value: actor },
            });
        };
        /**
         * Tries to pick an initial value for template feature once template tool is selected
         */
        this.handleSelectTool = (event) => {
            const { name } = event;
            if (name !== TOOL_NAME) {
                return;
            }
            const templates = this.configStore.get(['templates']);
            const tool = getTool(this.world);
            if (tool.features.templateId.value === undefined && templates.length > 0) {
                this.world.dispatchEvent(EventType.SetToolFeatureValue, {
                    name: 'templateId',
                    value: templates[0].id,
                });
            }
        };
        /**
         * Listens preview visiblity update and reset preview in visible state
         */
        this.handleSetToolFeatureValue = (event) => {
            const tool = getTool(this.world);
            if (tool.name !== TOOL_NAME) {
                return;
            }
            const { name, value } = event;
            if (name === PREVIEW_FEATURE_NAME && value === true) {
                this.deletePreview();
            }
        };
        /**
         * Listens template update to sync template feature value and reset preview
         * if selected template was changed
         */
        this.handleTemplatesUpdate = (path) => {
            var _a;
            const tool = getTool(this.world);
            if (tool.name !== TOOL_NAME || !includesArray(path, ['templates'])) {
                return;
            }
            const templates = this.configStore.get(['templates']);
            const templateId = tool.features.templateId.value;
            if (templateId &&
                templates.every((template) => template.id !== templateId)) {
                tool.features.templateId.value = (_a = templates[0]) === null || _a === void 0 ? void 0 : _a.id;
            }
            if (!templateId && templates.length > 0) {
                tool.features.templateId.value = templates[0].id;
            }
            if (!this.preview) {
                return;
            }
            if (!includesArray(path, [
                'templates',
                `id:${this.preview.templateId}`,
            ])) {
                return;
            }
            this.deletePreview();
        };
        const { world, scene } = options;
        this.world = world;
        this.scene = scene;
        this.mainActor = this.world.data.mainActor;
        this.configStore = this.world.data.configStore;
        this.actorCreator = this.world.data.actorCreator;
        this.selectedSceneId = getSavedSelectedSceneId(this.configStore);
        const entityPath = getSavedInspectedEntity(this.configStore);
        this.selectedActorPath = isActorPath(entityPath) ? entityPath : undefined;
        this.cursor = { x: null, y: null };
        this.world.addEventListener(EventType.SelectScene, this.handleSelectScene);
        this.world.addEventListener(EventType.InspectedEntityChange, this.handleInspectEntity);
        this.world.addEventListener(EventType.ToolCursorMove, this.handleToolCursorMove);
        this.world.addEventListener(EventType.ToolCursorLeave, this.handleToolCursorLeave);
        this.world.addEventListener(EventType.AddFromTemplate, this.handleAddFromTemplate);
        this.world.addEventListener(EventType.SelectTool, this.handleSelectTool);
        this.world.addEventListener(EventType.SetToolFeatureValue, this.handleSetToolFeatureValue);
        this.unsubscribeStore = this.configStore.subscribe(this.handleTemplatesUpdate);
    }
    onSceneDestroy() {
        var _a;
        this.world.removeEventListener(EventType.SelectScene, this.handleSelectScene);
        this.world.removeEventListener(EventType.InspectedEntityChange, this.handleInspectEntity);
        this.world.removeEventListener(EventType.ToolCursorMove, this.handleToolCursorMove);
        this.world.removeEventListener(EventType.ToolCursorLeave, this.handleToolCursorLeave);
        this.world.removeEventListener(EventType.AddFromTemplate, this.handleAddFromTemplate);
        this.world.removeEventListener(EventType.SetToolFeatureValue, this.handleSetToolFeatureValue);
        this.world.removeEventListener(EventType.SelectTool, this.handleSelectTool);
        (_a = this.unsubscribeStore) === null || _a === void 0 ? void 0 : _a.call(this);
    }
    deletePreview() {
        if (!this.preview) {
            return;
        }
        this.preview.remove();
        this.preview = undefined;
    }
    hidePreview() {
        if (!this.preview) {
            return;
        }
        VIEW_COMPONENTS.forEach((componentClass) => {
            var _a;
            const viewComponent = (_a = this.preview) === null || _a === void 0 ? void 0 : _a.getComponent(componentClass);
            if (viewComponent) {
                viewComponent.opacity = 0;
            }
        });
    }
    // TODO: Simplify after actor/template creation refactoring
    spawnPreview(templateId) {
        const preview = this.actorCreator.create({
            templateId,
            isNew: true,
        });
        this.mainActor.appendChild(preview);
        return preview;
    }
    updatePreview() {
        const tool = getTool(this.world);
        const { x, y } = this.cursor;
        if (tool.name !== TOOL_NAME || x === null || y === null) {
            this.deletePreview();
            return;
        }
        const templateId = tool.features.templateId.value;
        const preview = tool.features.preview.value;
        if (!templateId) {
            return;
        }
        if (this.preview && this.preview.templateId !== templateId) {
            this.deletePreview();
        }
        if (!this.preview) {
            this.preview = this.spawnPreview(templateId);
        }
        if (!preview) {
            this.hidePreview();
        }
    }
    update() {
        if (!this.selectedSceneId) {
            return;
        }
        this.updatePreview();
        updatePreviewPosition(this.cursor, this.world, this.preview);
    }
}
TemplateToolSystem.systemName = 'TemplateToolSystem';
