import Structure from "../Structure";
import SubmitStrategy from "./SubmitStrategies/SubmitStrategy";
import FormBuilder from "../../../FormBuilder";
import EventHandler from "../../../Utility/EventHandler";

export default class Form extends Structure{
    public onSubmittingChange: EventHandler<SubmittingEvent> = new EventHandler();
    public onMessageChange: EventHandler<MessageEvent> = new EventHandler();
    public onFormChange: EventHandler<null> = new EventHandler();

    public forceDivElement = false;
    public name:string = '';
    public displayDefaultNotice: boolean = false;
    public isDefaultText: string = "";
    public isChangedText: string = "";
    public resetButtonText: string = "";
    public writeDefaultData: boolean = false;
    public useHiddenFieldForJson: boolean = false;
    public submitStrategy: SubmitStrategy;

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

    public placeHtml(container: HTMLElement): void {
        let elementToPlace = "form";

        if (this.forceDivElement) {
            elementToPlace = "div";
        } else {
            let parent: HTMLElement|null = container;

            while (parent) {
                if (parent.tagName === "FORM"){
                    elementToPlace = "div";
                    break;
                }

                parent = parent.parentElement;
            }
        }

        const formContainer = document.createElement(elementToPlace);
        formContainer.classList.add(...this.classes, "pad-form-" + this.name, "pad-form", "pad-form-group");
        container.append(formContainer);
        this.placedHtml.push(formContainer);

        if (this.useHiddenFieldForJson){
            const hiddenField = document.createElement("input");
            hiddenField.style.display = "none";
            hiddenField.type = "hidden";
            hiddenField.name = (this.index ?? this.name) + "[dataJson]";
            hiddenField.value = JSON.stringify(this.getDataNode().value);
            this.dataNode.onChange.add(() => {
                hiddenField.value = JSON.stringify(this.getDataNode().value);
                hiddenField.dispatchEvent(new Event("change", {bubbles: true, cancelable: false}));
            });
            formContainer.append(hiddenField);
        }

        this.contents.forEach(c => c.placeHtml(formContainer));

        this.dataNode.onChange.add(() => {
            this.onFormChange.emit(null);
        });
        this.dataNode.onValidChange.add(event => {
            formContainer.classList.toggle("pad-invalid", !event.isValid);
        });

        formContainer.addEventListener("submit", (e: Event) => {
            e.preventDefault();
        });
    }

    public submit(): void{
        this.dataNode.validate(true);
        if (!this.dataNode.isValid) {
            console.warn("Form is not valid so cannot be submitted");
            this.onMessageChange.emit(new MessageEvent("Niet alle velden zijn geldig, check de bovenstaande velden en probeer het opnieuw."));
            return;
        }
        this.submitStrategy.submit(this.writeDefaultData ? this.getDataNode().valueOrDefault : this.getDataNode().value);
    }

    public getFormBuilder(): FormBuilder {
        return this.formBuilder;
    }

    public getPlacedHtml(): (HTMLFormElement|HTMLElement)[] {
        return this.placedHtml as (HTMLFormElement|HTMLElement)[];
    }

    public set submitting(submitting: boolean) {
        this.onSubmittingChange.emit(new SubmittingEvent(submitting));
    }

    public set message(message: string|null) {
        this.onMessageChange.emit(new MessageEvent(message));
    }

    public set serverSideErrors(errors: object) {
        this.dataNode.serverSideErrors = errors;
    }
}

export class MessageEvent{
    constructor(
        public readonly message: string|null = null
    ) {}
}

export class SubmittingEvent{
    constructor(
        public readonly submitting: boolean = false
    ) {}
}