import 'babel-polyfill';

const SSD_MOBILENETV1 = 'ssd_mobilenetv1';
const TINY_FACE_DETECTOR = 'tiny_face_detector';
const MTCNN = 'mtcnn';
const fr_elements = {
    'login_submit': '#submit',
    'fr_remove_items': '#loginform #user_login, #loginform #user_pass',
    'fr_results': '#fr_results',
    'fr_wrapper': '#wrapper_input_recognition',
    'fr_input': '#inputRecognition',
    'fr_state': '#stateRecognition',
    'fr_overlay': '#overlayRecognition',
    'fr_login': '#fr_facelogin',
    'fr_table': '#facerec_user_table',
    'fr_table_pict_status': '#fr_picture_status',
    'fr_table_pin_status': '#fr_pin_status',
    'fr_table_preview_ok': '#fr_picture_message_ok',
    'fr_table_preview_error': '#fr_picture_message_error',
    'fr_table_video': '#facerec_video',
    'fr_table_canvas': '#facerec_canvas',
    'fr_table_picture': '#facerec_picture',
    'fr_table_take_image': '#facerec_take_image',
    'fr_table_start_camera': '#facerec_start_camera',
    'fr_table_admin_camera_div': '#facerec_admin_camera_div',
    'fr_table_preview_img': '#fr_picture_preview',
    'fr_table_user_nonce': '#fr_ajax_nonce',
    'fr_table_light_switch': '#facerec_light_switch',
}

let selectedFaceDetector = SSD_MOBILENETV1;
let minConfidence = 0.5;
let inputSize = 512;
let scoreThreshold = 0.5;
let minFaceSize = 20;
let facerecStreaming = false;

jQuery(document).ready(function ($) {
    $ = jQuery;
    // Login JS
    if ($(fr_elements.fr_input).length) {
        facerecStartScript();

        if ('replace_wp' === face_recognition_data.module_how) {
            $(fr_elements.fr_remove_items).closest('p').remove();
        }
        if (tmp_descriptors.length > 0) {
            tmp_descriptors.forEach(function (element) {
                let temp = jQuery.parseJSON(element[1]);
                let i = 0;
                for (var descr in temp.descriptor) {
                    i++;
                }
                const descriptors = new Float32Array(i);
                let values = Object.values(temp.descriptor);
                for (let j = 0; j < values.length; j++) {
                    descriptors[j] = values[j];
                }
                face_descriptors.push(new faceapi.LabeledFaceDescriptors('face-' + ((element[0] + 12) * 35) + '-data', [descriptors]));
            });

            $(fr_elements.fr_input).get(0).addEventListener("loadedmetadata", async (e) => {
                frontendFacerecDoRecognition();
            });

            $(fr_elements.fr_state).on('click', function (e) {
                e.preventDefault();
                const videoEl = $(fr_elements.fr_input).get(0);
                if ('start' === $(this).attr('attr-state')) {
                    videoEl.play();
                    $(this).attr('attr-state', 'stop');
                    $(this).html(face_recognition_data.translate.stop_recognition);
                    $(fr_elements.fr_results).html(face_recognition_data.translate.recognizing);
                } else if ('stop' === $(this).attr('attr-state')) {
                    videoEl.pause();
                    $(this).attr('attr-state', 'start');
                    $(this).html(face_recognition_data.translate.start_recognition);
                    $(fr_elements.fr_results).html('<br />');
                }
            });
        } else {
            $(fr_elements.fr_wrapper).before('<span id="fr_no_image_model">' + face_recognition_data.translate.no_data_user + '</span>').remove();
        }

        $(fr_elements.fr_table_light_switch).on('click', function (e) {
            e.preventDefault();
            $(this).toggleClass('turn_on').toggleClass('turn_off');
            $('body').toggleClass('facerec_switch_on');
        });
    }

    // Admin JS
    if ($(fr_elements.fr_table).length) {
        if ('1' === face_recognition_data.pin) {
            $(fr_elements.login_submit).on('click', function () {
                let return_val = true;
                if ($(fr_elements.fr_table_pict_status).val() === 'picture_empty') {
                    if (!confirm(face_recognition_data.translate.picture_empty_confirm)) {
                        return_val = false;
                    }
                } else {
                    return_val = true;
                }

                if ($(fr_elements.fr_table_pin_status).val() === 'pin_empty' && $(fr_elements.fr_table_pin_status).val() === '') {
                    if (confirm(face_recognition_data.translate.pin_empty_confirm)) {

                    } else {
                        return_val = false;
                    }
                } else {
                    return_val = true;
                }

                return return_val;
            });
        }

        facerecStartScript();

        $(fr_elements.fr_table_light_switch).on('click', function (e) {
            e.preventDefault();
            $(this).toggleClass('turn_on').toggleClass('turn_off');
            $('body').toggleClass('facerec_switch_on');
            if ($('#wpadminbar').length) {
                $('#wpadminbar').toggleClass('facerec_switch_childs_on');
            }
            if ($('#adminmenuback').length) {
                $('#adminmenu .wp-has-current-submenu .wp-submenu').toggleClass('facerec_switch_childs_on');
                $('#adminmenu').toggleClass('facerec_switch_childs_on');
                $('#adminmenuback').toggleClass('facerec_switch_childs_on');
                $('#adminmenuwrap').toggleClass('facerec_switch_childs_on');
            }
        });

        $(fr_elements.fr_table_start_camera).on('click', function (e) {
            e.preventDefault();
            $(this).html(face_recognition_data.translate.loading);
            $(fr_elements.fr_table_admin_camera_div).show();
            let facerecCameraWidth = 320;
            let facerecCameraHeight = 0;
            let facerecVideo = $(fr_elements.fr_table_video).get(0);
            let facerecCanvas = $(fr_elements.fr_table_canvas).get(0);

            navigator.mediaDevices.getUserMedia({
                    video: true,
                    audio: false
                })
                .then(function (stream) {
                    facerecVideo.srcObject = stream;
                    facerecVideo.play();
                    $(fr_elements.fr_table_start_camera).hide();
                })
                .catch(function (err) {
                    $(fr_elements.fr_table_start_camera).html(face_recognition_data.translate.refresh_page_allow_camera);
                    console.log(face_recognition_data.translate.camera_cannot_start + ': ' + err);
                });

            facerecVideo.addEventListener('canplay', function (ev) {
                if (!facerecStreaming) {
                    facerecCameraHeight = facerecVideo.videoHeight / (facerecVideo.videoWidth / facerecCameraWidth);

                    if (isNaN(facerecCameraHeight)) {
                        facerecCameraHeight = facerecCameraWidth / (4 / 3);
                    }

                    facerecVideo.setAttribute('width', facerecCameraWidth);
                    facerecVideo.setAttribute('height', facerecCameraHeight);
                    facerecCanvas.setAttribute('width', facerecCameraWidth);
                    facerecCanvas.setAttribute('height', facerecCameraHeight);
                    facerecStreaming = true;
                    $(fr_elements.fr_table_take_image).attr('disabled', false);
                }
            }, false);
        });

        $(fr_elements.fr_table_take_image).on('click', function (e) {
            e.preventDefault();
            let facerecCanvas = $(fr_elements.fr_table_canvas).get(0);
            let facerecVideo = $(fr_elements.fr_table_video).get(0);
            let facerecCameraWidth = 320;
            let facerecCameraHeight = facerecVideo.videoHeight / (facerecVideo.videoWidth / facerecCameraWidth);

            var context = facerecCanvas.getContext('2d');
            facerecCanvas.width = facerecCameraWidth;
            facerecCanvas.height = facerecCameraHeight;
            context.drawImage(facerecVideo, 0, 0, facerecCameraWidth, facerecCameraHeight);

            var data = facerecCanvas.toDataURL('image/png');
            facerecDoImageUpload(data, 'webcam');
        });

        $(fr_elements.fr_table_picture).on('change', function (e) {
            e.preventDefault();
            let $previewImageInput = $(fr_elements.fr_table_picture);
            if ('' !== $previewImageInput.val()) {
                facerecDoImageUpload($previewImageInput[0].files[0], 'upload');
            }
        });
    }
});

async function facerecDoImageUpload(data_image, source = 'upload') {
    $ = jQuery;
    let $previewOk = $(fr_elements.fr_table_preview_ok);
    let $previewError = $(fr_elements.fr_table_preview_error);
    let $previewImageInput = $(fr_elements.fr_table_picture);
    let $previewImage = $(fr_elements.fr_table_preview_img);
    let $previewStatus = $(fr_elements.fr_table_pict_status);
    let $userNonce = $(fr_elements.fr_table_user_nonce);
    const user_id = $('#user_id').val();

    $('html, body').animate({
        scrollTop: $previewError.offset().top
    }, 1000);

    let formData = new FormData();
    formData.append("user_id", user_id);
    formData.append("source", source);
    formData.append("nonce", $userNonce.val());
    if ('webcam' == source) {

    }
    formData.append("facerec_picture", data_image);

    $.ajax({
        url: face_recognition_data.ajax_url + '?action=fr_upload_image',
        type: "POST",
        data: formData,
        contentType: false,
        cache: false,
        processData: false,
        beforeSend: function () {
            $previewError.html('');
            $(fr_elements.login_submit).attr('disabled', 'disabled');
        },
        success: function (data) {
            data = JSON.parse(data);
            if ("undefined" !== data.status) {
                if (1 === data.status) {
                    $previewOk.html(face_recognition_data.translate.face_image_saved);
                    $previewError.html('');

                    $previewImage.attr('src', data.src + '?version=' + facerecRandomNumber(100));
                    facerecSave(data.src, user_id);
                    $previewStatus.val('picture_ok');
                } else if (-1 === data.status) {
                    $previewError.html(data.message);
                    $previewOk.html('');
                    $previewStatus.val('picture_empty');
                }
            } else {
                $previewError.html(data.message);
                $previewOk.html('');
                $previewStatus.val('picture_empty');
            }
        },
        error: function (data) {
            data = JSON.parse(data);
            $previewError.html(data.message);
            $previewOk.html('');
            $previewStatus.val('picture_empty');
            $(fr_elements.login_submit).removeAttr('disabled');
        },
        complete: function () {
            $previewImageInput.val('');
        }
    });
}

// Face Recognition Functions
async function facerecSave(imgSrc, user_id = '') {
    $ = jQuery;
    let $previewError = $(fr_elements.fr_table_preview_error);
    let $previewOk = $(fr_elements.fr_table_preview_ok);
    let $previewImage = $(fr_elements.fr_table_preview_img);
    let $userNonce = $(fr_elements.fr_table_user_nonce);

    $('html, body').animate({
        scrollTop: $previewError.offset().top
    }, 1000);

    if ('' === user_id) {
        user_id = $('#user_id').val();
    }

    let myImage = new Image();
    myImage.onload = async function () {
        const face = await faceapi.detectSingleFace(myImage, getFaceDetectorOptions()).withFaceLandmarks().withFaceDescriptor();
        if (undefined !== face) {
            $.ajax({
                url: face_recognition_data.ajax_url + '?action=fr_upload_image_map',
                type: "POST",
                data: {
                    'user_id': user_id,
                    'face': JSON.stringify(face),
                    'nonce': $userNonce.val(),
                },
                cache: false,
                beforeSend: function () {},
                success: function (data) {
                    data = JSON.parse(data);
                    if (1 === data.status) {
                        $previewOk.html(face_recognition_data.translate.face_map_upload);
                        $previewError.html('');
                    } else {
                        $previewError.html(face_recognition_data.translate.face_map_upload_error + ' (' + data.message + ')');
                        $previewOk.html('');
                    }
                },
                error: function (data) {
                    data = JSON.parse(data);
                    $previewError.html(data.message);
                    $previewOk.html('');
                    $(fr_elements.login_submit).removeAttr('disabled');
                },
                complete: function () {
                    $(fr_elements.login_submit).removeAttr('disabled');
                }
            });
        } else {
            $previewError.html(face_recognition_data.translate.face_not_found);
            $previewOk.html('');
            $.ajax({
                url: face_recognition_data.ajax_url + '?action=fr_image_none',
                type: "POST",
                data: {
                    'user_id': user_id,
                    'nonce': $userNonce.val(),
                },
                cache: false,
                success: function (data) {
                    data = JSON.parse(data);
                    if (1 === data.status) {
                        $previewImage.attr('src', face_recognition_data.image_none + '?version=' + facerecRandomNumber(100));
                        $previewError.html($previewError.html() + '<br />' + data.message);
                        $previewOk.html('');
                    } else {
                        $previewError.html(data.message);
                        $previewOk.html('');
                    }
                    $(fr_elements.login_submit).removeAttr('disabled');
                }
            });
        }
    };
    myImage.src = imgSrc + '?version=' + facerecRandomNumber(100);
}

function facerecErrorCallback(error) {
    console.error(JSON.stringify(error));
}

async function facerecStartScript() {
    $ = jQuery;
    selectedFaceDetector = TINY_FACE_DETECTOR;
    getCurrentFaceDetectionNet().load(face_recognition_data.plugin_url + '/assets/facerec_weights/tiny_face_detector_model-weights_manifest.json');

    await faceapi.loadFaceLandmarkModel(face_recognition_data.plugin_url + '/assets/facerec_weights/face_landmark_68_model-weights_manifest.json');
    await faceapi.loadFaceRecognitionModel(face_recognition_data.plugin_url + '/assets/facerec_weights/face_recognition_model-weights_manifest.json');

    if ($(fr_elements.fr_input).length) {
        try {
            const stream = await navigator.mediaDevices.getUserMedia({
                video: {}
            }, null, facerecErrorCallback);
            const videoEl = $(fr_elements.fr_input).get(0);
            videoEl.srcObject = stream;

            if (1 == face_recognition_data.autostart) {
                videoEl.play();
            }
        } catch (error) {
            $(fr_elements.fr_wrapper).html(face_recognition_data.translate.camera_blocked);
        }
    }
}

async function frontendFacerecDoRecognition() {
    $ = jQuery;
    const videoEl = $(fr_elements.fr_input).get(0);
    if (videoEl.paused || videoEl.ended || !isFaceDetectionModelLoaded()) return setTimeout(() => frontendFacerecDoRecognition(), 200);

    const result = await faceapi.detectSingleFace(videoEl, getFaceDetectorOptions()).withFaceLandmarks().withFaceDescriptor();

    if (result) {
        let $facelogin = $(fr_elements.fr_login);
        const canvas = $(fr_elements.fr_overlay).get(0);

        const resizedResults = resizeCanvasAndResults(videoEl, canvas, result);
        if (face_descriptors.length > 0) {
            const faceMatcher = new faceapi.FaceMatcher(face_descriptors);

            let best_match = faceMatcher.findBestMatch(resizedResults.descriptor);
            if ('unknown' !== best_match.label) {
                $(fr_elements.fr_login).val(best_match.label);

                $(fr_elements.fr_results).html('<span class="green">' + face_recognition_data.translate.face_recognized + '</span>');

                videoEl.srcObject = null;
                $('#stateRecognition').attr('attr-state', 'start');
                $('#stateRecognition').html(face_recognition_data.translate.start_recognition);
            } else {
                $(fr_elements.fr_results).html('<span class="green">' + face_recognition_data.translate.face_not_recognized + '</span>');
                $(fr_elements.fr_login).val('face_none');
            }
        } else {
            $(fr_elements.fr_results).html(face_recognition_data.translate.no_models_found);
            $(fr_elements.fr_login).val('model_none');
        }
    }
    setTimeout(() => frontendFacerecDoRecognition(), 2000);
}

// Utils Face Recognition Functions
function getFaceDetectorOptions() {
    return selectedFaceDetector === SSD_MOBILENETV1 ?
        new faceapi.SsdMobilenetv1Options({
            minConfidence
        }) :
        (
            selectedFaceDetector === TINY_FACE_DETECTOR ?
            new faceapi.TinyFaceDetectorOptions({
                inputSize,
                scoreThreshold
            }) :
            new faceapi.MtcnnOptions({
                minFaceSize
            })
        )
}

function getCurrentFaceDetectionNet() {
    if (selectedFaceDetector === SSD_MOBILENETV1) {
        return faceapi.nets.ssdMobilenetv1;
    }
    if (selectedFaceDetector === TINY_FACE_DETECTOR) {
        return faceapi.nets.tinyFaceDetector;
    }
    if (selectedFaceDetector === MTCNN) {
        return faceapi.nets.mtcnn;
    }
}

function isFaceDetectionModelLoaded() {
    return !!getCurrentFaceDetectionNet().params;
}

function resizeCanvasAndResults(dimensions, canvas, results) {
    const {
        width,
        height
    } = dimensions instanceof HTMLVideoElement ? faceapi.getMediaDimensions(dimensions) : dimensions;
    canvas.width = width;
    canvas.height = height;
    return faceapi.resizeResults(results, {
        width,
        height
    });
}

function facerecRandomNumber(unit = 10) {
    return Math.ceil(Math.random() * unit);
}