import Input from "../Input";
import InputFactory, {InputImplConfig} from "../InputFactory";

export default class NumberInput extends Input<number>{
    public placeholder: string|null = null;
    public min: number|null = null;
    public max: number|null = null;
    public step: number|null = null;

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

    public placeHtml(container: HTMLElement): void {
        const questionContainer = this.getQuestionContainer();
        questionContainer.classList.add("pad-number-input");
        container.append(questionContainer);
        this.placedHtml.push(questionContainer);
    }

    protected getInputElement(): HTMLElement {
        const inputElement = document.createElement("input");
        inputElement.name = this.getFullPathName();
        inputElement.setAttribute("autocomplete", this.autocomplete);
        inputElement.type = "number";
        inputElement.step = String(this.step ?? 1);
        if(this.min !== null) inputElement.min = String(this.min);
        if(this.max !== null) inputElement.max = String(this.max);
        if(this.placeholder) inputElement.placeholder = this.placeholder;

        inputElement.addEventListener("input", () => {
            let value: number|null = inputElement.valueAsNumber;
            if (isNaN(value)) value = null;
            this.value = value;
        });

        inputElement.addEventListener("change", () => {
            this.saveValue();
        });

        this.onChange.add(event => {
            inputElement.value = "" + (event.value ?? '');
        });

        this.getDataNode().onChange.add(event => {
            inputElement.value = "" + (event.value ?? '');
        });

        this.getDataNode().onReset.add( () => {
            this.value = undefined;
            inputElement.value = String(this.getDataNode().valueOrDefault ?? 0);
        })

        inputElement.value = String(this._value ?? this.getDataNode().valueOrDefault ?? 0);
        return inputElement;
    }

    public saveValue(): void {
        if (this._value == null) {
            this.getDataNode().value = this._value;
            return;
        }

        if (isNaN(this._value)) {
            this.getDataNode().value = null;
            return;
        }

        if(this.min !== null && this._value < this.min) {
            this.getDataNode().value = this.min;
            return;
        }

        if(this.max !== null && this._value > this.max) {
            this.getDataNode().value = this.max;
            return;
        }

        this.getDataNode().value = this._value;
    }
}

export function addNumberInputToFactory(factory: InputFactory): void {
    factory.registerConstructor("number", (config, params) => {
        const impl = config.impl;
        if (!isNumberInputConfig(impl)) return null;

        const numberInputParam: Partial<NumberInput> = params as Partial<NumberInput>;

        numberInputParam.placeholder = impl.placeholder;
        numberInputParam.min = impl.min;
        numberInputParam.max = impl.max;
        numberInputParam.step = impl.step;

        return new NumberInput(numberInputParam);
    });
}

function isNumberInputConfig(config: InputImplConfig): config is NumberInputConfig{
    return config.type === "number";
}

interface NumberInputConfig extends InputImplConfig{
    type: "number";
    placeholder: string|null;
    min: number|null;
    max: number|null;
    step: number|null;
}