    var active_file_inputs = new Set();

    function add_input_form() {
        var form_idx = $('#id_images-TOTAL_FORMS').val();
        $('#image_form_set').append($($('#empty_form').html().replace(/__prefix__/g, form_idx)).on("change",
            input_change));
        $('#id_images-TOTAL_FORMS').val(parseInt(form_idx) + 1);
    }

    async function blobToBase64(blob) {
        return new Promise((resolve, _) => {
            const reader = new FileReader();
            reader.onloadend = () => resolve(reader.result);
            reader.readAsDataURL(blob);
        });
    }

    function extendInputs(form_id, n) {
        // Makes sure we have n inputs available
        // returns available inputs
        inputs = getUnusedInputs($(`#${form_id} input[type=file][id^=id_images-]`));
        //fileInput = document.getElementById("id_images-0-image");


        // Make sure we have enough input targets
        input_diff = (n - inputs.length)

        if (input_diff > 0) {
            for (let j = 0; j < input_diff; j++) {
                add_input_form()
            }

            // Need to make sure we include the new ones...
            inputs = getUnusedInputs($(`#${form_id} input[type=file][id^=id_images-]`));
        }
        return inputs;
    }

    function getCurrentFormFiles(form_id) {
        inputs = $(`#${form_id} input[type=file]`);

        let files = new Set();
        for (let j = 0; j < inputs.length; j++) {
            i = inputs.get(j);
            if (i.files.length > 0) {
                files.add(i.files[0]);
            }
        }
        return files;
    }

    function getUnusedInputs(inputs) {
        new_inputs = [];
        for (let i = 0; i < inputs.length; i++) {
            input = inputs.get(i);

            if (!input.value) {
                new_inputs.push(input);
            }

        }

        return $(new_inputs);

    }


    function addFile(f, feedback) {
        console.log("add file", f)
        let dT = new DataTransfer();
        dT.clearData();
        dT.items.add(f);
        for (let i = 0; i < inputs.length; i++) {
            el = inputs.get(i);

            if (el.files.length == 0) {
                el.files = dT.files;
                //ocr(el)

                if (feedback) {
                    arr = el.name.split("-");
                    arr.splice(2, 1, "feedback_image");
                    feedback_el_name = arr.join("-");

                    $(`[name=${feedback_el_name}]`).prop("checked", true);
                }

                $(el).change();
                return false;
            }
        }

    }


    function ocr2(url, input) {
        console.log("OCR", input)
        Tesseract.recognize(
            url,
            'eng',
            //{ logger: m => console.log(m) }
        ).then(({
            data: {
                text
            }
        }) => {
            l = text.toLowerCase();
            uploading_el = $(`img[data-input-id='${input.id}'], div[data-input-id='${input.id}']`)
            uploading_el.removeClass("image-ident-loading");
            console.log("found text", l);
            if (l.includes("accession") || l.includes("number") || l.search(
                    /(ref|rk9|rh8|ra9|rbz)\d+/g) > -1) {
                console.log("Match found ", input);
                $(input).addClass("image-ident-warning");
                uploading_el.addClass("image-ident-warning");
            } else {
                $(input).addClass("image-ident-ok");

            }
        })

    }

    function input_change(evt) {
        file = evt.target.files[0];
        $(evt.target).removeClass("image-ident-warning");
        $(evt.target).removeClass("image-ident-ok");

        el = evt.target;

        readFileAndProcess2(el);

    }

    function showEditPopup(url) {
        var win = window.open(url, "Edit",
            'height=500,width=800,resizable=yes,scrollbars=yes');
        return false;
    }

    function showAddPopup(triggeringLink) {
        var name = triggeringLink.id.replace(/^add_/, '');
        href = triggeringLink.href;
        var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
        win.focus();
        return false;
    }

    function closePopup(win, newID, newRepr, id) {
        $(id + "_to").append('<option value=' + newID + ' title=' + newRepr + ' >' + newRepr + '</option>')
        win.close();
    }


    async function readFileAndProcess2(el) {

        //file_set = $("#image_form_set input[type=file]");

        //file_set.each(async (n, el) => {

        if (el.files.length > 0) {
            filename = el.files[0].name;
            // Probably no need to await here anymore
            //await readFile(el.files[0], el);

            $(el).parent().parent().find(".temp-thumb").remove();

            let url = URL.createObjectURL(el.files[0])

            n = el.id.split("-")[1];
            $(`#drop-filenames span[data-input-id='${el.id}']`).remove();

            let extra_class = " image-ident-loading";
            if ($(el).hasClass("image-ident-warning")) {
                extra_class = " image-ident-warning";
            } else if ($(el).hasClass("image-ident-ok")) {
                extra_class = " image-ident-ok";
            }

            //let extra_class = "";
            let imageId;
            if (el.files[0].type.startsWith("image")) {

                let hash = CryptoJS.MD5(el.files[0]).toString();

                base = await blobToBase64(el.files[0]);
                imageId = "base64://" + base.split(",")[1];
                imageId2 = base;
                const element = $(`<img class='temp-thumb' src=${imageId}></div>`).get(0)
                $(el).parent().parent().prepend(element);


                ocr2(url, el);
                checkHash(hash, el);

                if ($("#drop-filenames").length > 0) {
                    $("#drop-filenames").append(
                        `<span data-input-id='${el.id}'><span>${n}: ${el.files[0].name}</span><img class='uploading${extra_class}' data-input-id='${el.id}' src=${url}></span>`
                    )
                }
            } else {
                imageId = `wadouri:${url}`;
                imageId2 = imageId

                const element = $(`<div class='temp-thumb' src=${imageId2}></div>`).get(0)
                $(el).parent().parent().prepend(element);
                cornerstone.enable(element);
                cornerstone.loadAndCacheImage(imageId).then(function (image) {
                    cornerstone.displayImage(element, image);
                    cornerstone.resize(element)


                    if (hash_url) {
                        let pixel_data = image.getPixelData().toString()

                        let hash = CryptoJS.MD5(pixel_data).toString();
                        checkHash(hash, el);
                    }


                    console.log("image", image)

                    let bytesView = image.getPixelData()
                    let str = new TextDecoder("utf-8").decode(bytesView, );

                    console.log("bytes", bytesView)
                    console.log("str", str)

                    extractDicomDetails(image.data.byteArray)

                });

                if ($("#drop-filenames").length > 0) {
                    $("#drop-filenames").append(
                        `<span data-input-id='${el.id}'><span>${n}: ${el.files[0].name}</span><div class='uploading${extra_class}' data-input-id='${el.id}' src=${url}></div></span>`
                    )

                    const element2 = $(`#drop-filenames div[data-input-id='${el.id}']`).get(0);
                    cornerstone.enable(element2);
                    cornerstone.loadAndCacheImage(imageId).then(function (image) {
                        cornerstone.displayImage(element2, image);

                        $(element2).addClass("temp-thumb-large");
                        cornerstone.resize(element2)
                        setTimeout(function () {
                            ocr2($(element2).find("canvas").get(0).toDataURL(), el);
                            $(element2).removeClass("temp-thumb-large");
                            cornerstone.resize(element2)
                        }, 500);
                    });
                }

            }





            active_file_inputs.delete(el)
            console.log("active", active_file_inputs)
            // Only load once all queued files have been processed
            if (active_file_inputs.size < 1) {
                //load_viewer = true;
                loadViewer();
            }
        }
        //})
    }

    function checkHash(hash, input, url) {
        $.ajax({
                url: hash_url,
                data: {
                    csrfmiddlewaretoken: csrf_token,
                    hash: hash
                },
                type: "POST",
                dataType: "json",
            })
            // $.ajax().done(), $.ajax().fail(), $ajax().always() are upto you. Add/change accordingly
            .done(function (data) {
                console.log(data);

                if (data.status == "success") {
                    console.log("success");
                    console.log(data.id)
                    if (data.id) {
                        //toastr.info(`id ${data.id}`)
                        uploading_el = $(`img[data-input-id='${input.id}'], div[data-input-id='${input.id}']`)
                        $(input).addClass("image-duplicate");
                        uploading_el.addClass("image-duplicate");

                        toastr.warning(`Image (${input.files[0].name}) already exists.<br /><a href='${data.url}'>Click to view</a><br /><button type="button" class="btn clear">Dismiss</button>`, "Duplicate image", {
                            "closeButton": false,
                            "debug": false,
                            "newestOnTop": false,
                            "progressBar": false,
                            "positionClass": "toast-top-full-width",
                            "preventDuplicates": false,
                            "onclick": null,
                            "showDuration": "0",
                            "hideDuration": "0",
                            "timeOut": 0,
                            "extendedTimeOut": 0,
                            "showEasing": "swing",
                            "hideEasing": "linear",
                            "showMethod": "fadeIn",
                            "hideMethod": "fadeOut",
                            "tapToDismiss": false
                        })

                    }
                }
            })
            .always(function () {
                console.log('[Done]');
            })
    }


    function updateImagePositions() {
        console.log("UP pos");

        file_set = $("#image_form_set ul");

        n = 0;
        for (let i = 0; i < file_set.length; i++) {
            ul = file_set.get(i);
            console.log(ul)

            file_element = $(ul).find("input[type=file]").get(0);

            // If a (new) file is being uploaded we save it's name prior to it being hashed
            if (file_element.files.length > 0) {
                file_name = file_element.files[0].name
                //$(ul).find("input[type=text]").val(file_name);
                $(ul).find("input[name*='filename']").val(file_name);
            }

            if (file_element.files.length > 0 || $(ul).find(":contains('Currently:')")) {
                n++;
                $(ul).find("input[type=number]").val(n);
            }
        }

    }


    function loadViewer() {
        console.log("LOAD VIEWER")
        // temp fix before starting viewer collapsed
        $("#single-dicom-viewer").show()
        //file_set = $("#image_form_set input[type=file]");

        //n = 0;
        //for (let i = 0; i < file_set.length; i++) {
        //    el = file_set.get(i)
        //    if (el.files.length > 0) {
        //        n++;
        //    }
        //}

        image_set = $("#image_form_set .temp-thumb").get() //.map(function() { return $(this).attr("src")}).get();
        console.log("image set", image_set);
        //console.log()
        //if (n == image_set.length) {
        const event = new CustomEvent('loadDicomViewer', {
            "detail": image_set
        });

        window.dispatchEvent(event);
        sortable('.sortable');
        //sortable('.sortable')[0].addEventListener('sortstop', function(e) {
        updateImagePositions();
        //}

    }

    function extractDicomDetails(byteArray) {
        // We need to setup a try/catch block because parseDicom will throw an exception
        // if you attempt to parse a non dicom part 10 file (or one that is corrupted)
        try {
            console.log("bytearray", byteArray)
            // parse byteArray into a DataSet object using the parseDicom library
            var dataSet = dicomParser.parseDicom(byteArray);
            console.log("ds", dataSet)

            // dataSet contains the parsed elements.  Each element is available via a property
            // in the dataSet.elements object.  The property name is based on the elements group
            // and element in the following format: xggggeeee where gggg is the group number
            // and eeee is the element number with lowercase hex characters.

            // To access the data for an element, we need to know its type and its tag.
            // We will get the sopInstanceUid from the file which is a string and
            // has the tag (0020,000D)
            var study_description = dataSet.string('x00081030');
            var accession_number = dataSet.string('x00080050').toLowerCase();
            var patient_id = dataSet.string('x00100020').toLowerCase();
            var modality = dataSet.string('x00080060');
            var projection = dataSet.string('x00185101');

            try {
                var plane = dataSet.string('x00180022').toLowerCase();
            } catch (err) {
                var plane = "";
            }
            console.log("STUDY", study_description);
            console.log("acc", accession_number);
            console.log("pid", patient_id);
            console.log("projection", projection);
            console.log("plane", plane);

            function select_plane(option) {
                let plane_select_object = document.getElementsByName("plane")[0]
                const $options = Array.from(plane_select_object.options);
                const optionToSelect = $options.find(item => item.text === option);
                optionToSelect.selected = true;

            }

            if (plane.includes("axial")) {
                select_plane("axial");
            } else if (plane.includes("sagittal")) {
                select_plane("sagittal");
            } else if (plane.includes("coronal")) {
                select_plane("coronal");
            } else if (plane.includes("ap")) {
                select_plane("AP");
            } else if (plane.includes("lat") | plane.includes("lateral")) {
                select_plane("lateral");
            } else if (plane.includes("pa")) {
                select_plane("PA");

            }


            modality_map = {
                "DX": "Radiograph",
                "CR": "Radiograph",
                "CT": "CT",
                "MR": "MR",
                "RF": "Fluroscopy",
                "US": "Ultrasound",
            }

            if (modality in modality_map) {
                let modality_select_object = document.getElementsByName("modality")[0]
                const $options = Array.from(modality_select_object.options);
                const optionToSelect = $options.find(item => item.text === modality_map[modality]);
                optionToSelect.selected = true;



            } else {
                toastr.info(`Unable to automatically set modality: ${modality}`);
            }

            // If the dicom study_description contains multiple values select the first
            study_description = study_description.split(",")[0].trim();

            to_replace = ["lt", "rt", "contrast", "LT", "RT", "Contrast"];

            for (let i = 0; i < to_replace.length; i++) {

                study_description = study_description.replace(to_replace[i], "");
            }

            let select_object = document.getElementsByName("examination")[0]

            if (select_object.value == "") {
                $.get(`${select_object.dataset.autocompleteLightUrl}?q=${study_description}`, (data) => {
                    console.log(data)
                    if (data.results.length == 1) {

                        $(select_object).select2("trigger", "select", {
                            data: data.results[0]
                        })

                    }
                })

            }

            not_anon = false;

            site_codes = ["ref", "rk9", "ra9", "rh8", "rbz", "rba"]

            for (let i = 0; i < site_codes.length; i++) {
                if (
                    accession_number.startsWith(site_codes[i]) ||
                    patient_id.startsWith(site_codes[i])) {
                    not_anon = true;
                }
            }

            if (not_anon) {
                toastr.warning(`File does not appear to be annonymised<br/>Accession: ${accession_number} <br/>Patient ID: ${patient_id}<br/>`, 'Anonymisation warning', {
                    "closeButton": false,
                    "debug": false,
                    "newestOnTop": false,
                    "progressBar": false,
                    "positionClass": "toast-top-full-width",
                    "preventDuplicates": false,
                    "onclick": null,
                    "showDuration": "0",
                    "hideDuration": "0",
                    "timeOut": 0,
                    "extendedTimeOut": 0,
                    "showEasing": "swing",
                    "hideEasing": "linear",
                    "showMethod": "fadeIn",
                    "hideMethod": "fadeOut",
                });
            }

            // TODO: fix
            //$.ajax({
            //    url: `{% url 'generic:examination-autocomplete' %}/${encodeURI(study_description)}`,
            //    //context: document.body,
            //    success: function(data){
            //        console.log(data)
            //    }
            //});

            //// try to match with a study description (if not already selected)
            //if ($(`#id_examination_to option`).length < 1) {
            //    option = $(`#id_examination_from option[title*='${study_description}' i]`);

            //    if (option.length) {
            //        option.appendTo($("#id_examination_to"));
            //        toastr.success(`Examination set to ${option.attr('title')} from dicom data`);
            //    } else {
            //        toastr.warning(
            //            `Unable to set examination ${study_description} from dicom data (it needs creating)`);

            //    }
            //}

        } catch (err) {
            console.log(err);
        }
    }