import Structure from "../Structure";
import StructureFactory, {StructureConfig, StructureImplConfig} from "../StructureFactory";
import {ConditionConfig} from "../../../Conditions/ConditionFactory";
import Condition from "../../../Conditions/Condition";
import {setElementVisibility} from "../../../Utility/ElementVisibility";

export default class Repeating extends Structure{
    public maxCount: number;
    public atLeastOne: boolean;
    public repeatingContents: StructureConfig;
    public repeatingConditions: Condition[] = [];
    public withRemoveButton: boolean = false;
    public removeButtonText: string = "Remove";
    public addButtonText: string = "Add";
    protected _isRepeating: boolean = true;
    protected nextRepeatIndex: number = 0;
    protected createdInstances: Structure[] = [];
    protected addButtons: HTMLButtonElement[] = [];
    protected placedContainers: HTMLElement[] = [];

    constructor(params: Partial<Repeating> = {}) {
        super(params);
        Object.assign(this, params);

        this.formBuilder.onComponentsCreated.add(() => this.checkRepeatingConditions());
    }

    public placeButtons(){
        this.placedHtml.forEach(html => {
            const addButton = document.createElement("button");
            addButton.type = "button";
            addButton.innerText = this.addButtonText;
            addButton.classList.add("pad-add-repeating-button", "button-primary");
            addButton.addEventListener("click", () => {
                this.addRepeatingContent();
            });
            addButton.disabled = this.createdInstances.length + (this.atLeastOne ? 1 : 0) >= this.maxCount;
            html.append(addButton);
            this.addButtons.push(addButton);
        })
    }

    public placeHtml(container: HTMLElement): void {
        if (this.atLeastOne && this.nextRepeatIndex === 0) {
            this.repeatingContents.idPrefix = this.idPrefix + this.id + "-" + this.nextRepeatIndex + "-";
            this.repeatingContents.index = String(this.nextRepeatIndex);
            this.nextRepeatIndex++;
            const nextInstance = this.formBuilder.componentFactory.createComponent(this.repeatingContents, this);
            if (!nextInstance) throw new Error("Failed to create repeating instance");
            this.contents.push(nextInstance as Structure);
            this.dataNode.addChildren((nextInstance as Structure).getDataNode());
            this.formBuilder.manualComponentsCreatedEvent();
        }

        const structureDiv = document.createElement("div");
        structureDiv.classList.add("pad-repeating-group");
        this.addStandardContainerProperties(structureDiv);
        container.append(structureDiv);
        this.placedHtml.push(structureDiv);
        this.contents.forEach(c => c.placeHtml(structureDiv));
        this.createdInstances.forEach(c => this.placeRepeatingContent(c, structureDiv));
        this.setRepeatingVisibility(this._isRepeating);
    }

    protected setRepeatingVisibility(visible: boolean): void {
        if (visible) {
            this.placedContainers.forEach(c => setElementVisibility(c, true));
            this.createdInstances.forEach(c => this.dataNode.addChildren(c.getDataNode()));
            this.addButtons.forEach(b => b.remove());
            this.addButtons = [];
            this.placeButtons();
        } else {
            this.addButtons.forEach(b => b.remove());
            this.addButtons = [];
            this.placedContainers.forEach(c => setElementVisibility(c, false));
            this.createdInstances.forEach(c => this.dataNode.removeChild(c.getDataNode()))
        }
    }

    public addRepeatingCondition(condition: Condition): void {
        condition.onChange.add(value => this.checkRepeatingConditions(value));
        this.repeatingConditions.push(condition);
    }

    protected checkRepeatingConditions(value: boolean|null = null):void {
        if (value === this._isRepeating) return;
        if (value === false) {
            this.isRepeating = false;
            return;
        }
        this.isRepeating = this.repeatingConditions.every(c => c.check());
    }

    set isRepeating(value: boolean) {
        if (value === this._isRepeating) return;
        this._isRepeating = value;
        this.setRepeatingVisibility(value);
    }

    protected placeRepeatingContent(repeatingContent: Structure, container:HTMLElement): void {
        const repeatingDiv = document.createElement("div");
        repeatingDiv.classList.add("pad-repeating-item", "pad-form-group", "pad-hidden");

        const removeButton = document.createElement("button");
        removeButton.type = "button";
        removeButton.innerText = this.removeButtonText;
        removeButton.classList.add("pad-remove-repeating-button", "button-primary");
        removeButton.addEventListener("click", () => {
            this.dataNode.removeChild(repeatingContent.getDataNode());
            this.createdInstances.splice(this.createdInstances.indexOf(repeatingContent), 1);
            setElementVisibility(repeatingDiv, false);
            setTimeout(() => {
                repeatingDiv.remove();
                repeatingContent.cleanHtml();
            }, 1000);
            this.addButtons.forEach(b => b.remove());
            this.addButtons = [];
            this.placeButtons();
        });
        repeatingDiv.append(removeButton);
        repeatingContent.placeHtml(repeatingDiv);
        container.append(repeatingDiv);
        this.placedContainers.push(repeatingDiv);
        setTimeout(() => setElementVisibility(repeatingDiv, true));
    }

    public addRepeatingContent(): void {
        if ((this.createdInstances.length + (this.atLeastOne ? 1 : 0)) >= this.maxCount) return;
        this.addButtons.forEach(b => b.remove());
        this.addButtons = [];

        this.repeatingContents.idPrefix = this.idPrefix + this.id + "-" + this.nextRepeatIndex + "-";
        this.repeatingContents.index = String(this.nextRepeatIndex);
        this.nextRepeatIndex++;

        const createdInstance = this.formBuilder.componentFactory.createComponent({...this.repeatingContents}, this);
        if (!createdInstance) throw new Error("Failed to create repeating instance");
        this.createdInstances.push(createdInstance as Structure);
        this.dataNode.addChildren((createdInstance as Structure).getDataNode());
        this.formBuilder.manualComponentsCreatedEvent();
        this.placedHtml.forEach(html => this.placeRepeatingContent(createdInstance as Structure, html));
        this.placeButtons();
    }
}

export function addRepeatingToFactory(factory: StructureFactory): void {
    factory.registerConstructor("repeating", (config, params) => {
        const impl = config.impl;
        if (!isRepeatingConfig(impl)) return null;

        const groupParam: Partial<Repeating> = params as Partial<Repeating>;

        groupParam.maxCount = impl.maxCount;
        groupParam.atLeastOne = impl.atLeastOne;
        groupParam.repeatingContents = impl.repeatingContents;
        groupParam.addButtonText = impl.addButtonText;
        groupParam.removeButtonText = impl.removeButtonText;
        groupParam.withRemoveButton = impl.withRemoveButton;

        const repeatingObject = new Repeating(groupParam);

        impl.repeatingConditions.forEach(c => {
            const condition = groupParam.formBuilder?.conditionFactory.createCondition(c);
            if (condition) repeatingObject.addRepeatingCondition(condition);
        });

        return repeatingObject;
    });
}

function isRepeatingConfig(config:StructureImplConfig): config is RepeatingConfig{
    return config.type === "repeating";
}

interface RepeatingConfig extends StructureImplConfig{
    type: "repeating";
    maxCount: number;
    atLeastOne: boolean;
    repeatingContents: StructureConfig;
    repeatingConditions: ConditionConfig[];
    withRemoveButton: boolean;
    removeButtonText: string;
    addButtonText: string;
}