import { NotificationModule } from "../notification";
import InputStatusModule from "../inputStatus";

export type AsyncValueUpdateData = {
    id?: number,
    value: any
};

export interface AsyncValueUpdateOptions {
    getIdCallback?: ($element: JQuery) => number;
    performValidation?: boolean;
    updateFailedCallback?: ($element: JQuery, jqXHR: JQuery.jqXHR<any>, errorTextStatus: JQuery.Ajax.ErrorTextStatus, errorThrown: string) => void;
    updateCompletedCallback?: ($element: JQuery, successData: any, successTextStatus: JQuery.Ajax.SuccessTextStatus, jqXHR: JQuery.jqXHR<any>) => void;
    getAdditionalPostParametersCallback?: ($element: JQuery) => object;
};


export default class AsyncValueUpdateModule {


    private initAsyncValueUpdateModule(selector: string, options?: AsyncValueUpdateOptions) {

        let self = this;

        let animationEndEvent = "webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend";


        $(selector).each(function (index, element) {

            let $element = $(element);

            if ($element.data("async-value-update-module-initialized") === true)
                return;

            let updateUrl = $element.data("async-value-update-url");
            if (updateUrl === undefined || updateUrl.length <= 0) {
                console.log("Invalid updateUrl for element", $element, updateUrl);
                return;
            }

            let elementName = $element.data("async-value-update-name");
            if (elementName === undefined || elementName.length <= 0) {
                var $previousElement = $element.prev();

                if ($previousElement !== undefined && $previousElement.prop("nodeName") === "LABEL") {
                    elementName = $previousElement.html();
                }
            }

            let revertChangeOnError = $element.data("async-value-update-revert-on-error") != false;

            let changeEventName = $element.data("async-value-update-event");
            if(changeEventName === undefined || changeEventName.length === 0)
                changeEventName = "change";


            let showSuccessNotification = $element.data("async-value-update-show-success-notification");
            if(showSuccessNotification === undefined)
                showSuccessNotification = true;

            let inputStatusModule: InputStatusModule;
            let timeoutHandle: number;

            let oldValue = self.getValueFromElement($element);

            $element.on(changeEventName, function () {

                if (timeoutHandle !== undefined)
                    window.clearTimeout(timeoutHandle);

                if (inputStatusModule === undefined || inputStatusModule.isInitialised() === false)
                    inputStatusModule = new InputStatusModule($element);

                let asyncValidationForm = $element.closest("form.fake-async-validation-form");
                var elementId = $element.prop("id");

                if (options.performValidation === true) {
                    let validateElementResult = asyncValidationForm.validate().element("#" + elementId);
                    if (validateElementResult === false) {
                        inputStatusModule.showError();
                        return;
                    }
                }
                

                let value = self.getValueFromElement($element);

                var asyncValueUpdateData: AsyncValueUpdateData = {
                    value: value
                };

                if (options !== undefined && options.getIdCallback !== undefined) {
                    asyncValueUpdateData.id = options.getIdCallback($element);
                };

                let postData: any;
                if(options !== undefined && options.getAdditionalPostParametersCallback !== undefined) {
                    let extraParameters = options.getAdditionalPostParametersCallback($element);
                    postData = {
                        ...asyncValueUpdateData,
                        ...extraParameters
                    };
                }
                else {
                    postData = asyncValueUpdateData;
                }

                $element.prop("disabled", "");
                inputStatusModule.showLoading();

                $.ajax({
                    method: "POST",
                    url: updateUrl,
                    data: postData
                })
                    .fail(function (jqXHR, textStatus, errorThrown) {
                        inputStatusModule.showError();

                        if(options.updateFailedCallback !== undefined) {
                            options.updateFailedCallback($element, jqXHR, textStatus, errorThrown);
                        }

                        if(revertChangeOnError === true)
                            self.setValueForElement($element, oldValue);
                    })
                    .done(function (successData, textStatus, jqXHR) {
                        if (showSuccessNotification === true && elementName !== undefined)
                            NotificationModule.showSuccessSmall(elementName + " blev succesfuldt opdateret")
                        
                        if ($element.prop("type") !== "checkbox")
                            inputStatusModule.showSuccess();

                        if(options.updateCompletedCallback !== undefined) {
                            options.updateCompletedCallback($element, successData, textStatus, jqXHR);
                        }

                        oldValue = value;
                    })
                    .always(function () {
                        $element.prop("disabled", "");

                        if ($element.prop("type") === "checkbox")
                            inputStatusModule.remove();
                        else {
                            timeoutHandle = window.setTimeout(function () {
                                inputStatusModule.removeWithFadeOut();
                            }, 4000);
                        }
                    });
            });

            $element.data("async-value-update-module-initialized", true);
        });
    }

    private getValueFromElement($element: JQuery) {
        let elementType = $element.prop("nodeName");

        switch (elementType) {

            case "INPUT":
                return this.getValueFromInputElement($element);

            case "SELECT":
                return $element.val();

            case "TEXTAREA":
                return $element.val();

            default:
                console.log("Error getting value from element", elementType, $element);
                throw Error("Error getting value from element type " + elementType);
        }
    }

    private setValueForElement($element: JQuery, value: any): void {
        let elementType = $element.prop("nodeName");

        switch (elementType) {

            case "INPUT":
                this.setValueForInputElement($element, value);
                break;

            case "SELECT":
            case "TEXTAREA":
                $element.val(value);
                break;

            default:
                console.log("Error getting value from element", elementType, $element);
                throw Error("Error getting value from element type " + elementType);
        }
    }


    private setValueForInputElement($element: JQuery, value: any): void {

        let inputType = $element.prop("type");

        switch (inputType) {

            case "text":
                $element.val(value);
                break;

            case "checkbox":
                if(value === true)
                    $element.prop("checked", true);
                else
                    $element.prop("checked", false);
                break;

            default:
                console.log("Error getting value from input element", inputType, $element);
                throw Error("Error getting value from element type " + inputType);
        }
    }

    private getValueFromInputElement($element: JQuery) {

        let inputType = $element.prop("type");

        switch (inputType) {

            case "text":
                return $element.val();

            case "checkbox":
                return $element.is(":checked");

            default:
                console.log("Error getting value from input element", inputType, $element);
                throw Error("Error getting value from element type " + inputType);
        }
    }


    init(selector: string, options?: AsyncValueUpdateOptions) {

        //Set performValidation default to true
        if (options.performValidation !== false)
            options.performValidation = true;

        this.initAsyncValueUpdateModule(selector, options);
    }
}