define([
        "base/js/dialog",
        "base/js/utils",
        "jquery",
        "./state",
        "./common",
        "./entitySearcher"
    ],
    function (dialog, utils, $, state, common, entitySearcher) {
        var errorElements = { }
        function createErrorElement(name) {
            var element = document.createElement("STRONG")
            element.textContent = ""
            element.style.marginLeft = "8px"
            element.style.color = "red"
            errorElements[name.toLowerCase()] = element
            return element
        }
        function cleanErrors() {
            Object.keys(errorElements).forEach(key => errorElements[key].textContent="")
        }

        var spinner = document.createElement("IMG")
        spinner.className="openbis-feedback"
        spinner.src=""
        function showSpinner(env) {
            var userName = window.location.pathname.split("/")[2];
            spinner.src = '/user/' + userName+ '/nbextensions/jupyter-openbis-extension/spinner.gif'
        }
        function hideSpinner(env) {
            spinner.src=""
        }

        function get_file_list(env, container, path) {
            // get the file list (relative to the notebook) using the existing jupyter api
            var url = env.notebook.base_url + 'api/contents'
            if (path !== "") {
                url = url + '/' + path
            }

            fetch(url)
                .then( function(response) {
                    if (response.ok) {
                        response.json()
                            .then(function(data){
                                createFileTable(env, data, container, state)
                                //state.fileCheckboxes = createFileTable(env, data, container, state.selectedFiles)
                            })
                    }
                    else {
                        console.error(response.status)
                    }
                })
        }

        function get_dataset_list(env, container) {
            var datasets = env.notebook.metadata.datasets
            if (datasets != null) {
                var values = Object.keys(datasets)
                values.sort()
                state.datasetCheckboxes = createSelectTable(values, container, true, state.unselectedDatasets)
            }
        }

        function getOpenBISHistoryId() {
            // Default empty history id
            var resultDatasetHistoryId = "";
            // Search for the history id on the metadata, new format, hidden from the user
            if(Jupyter && 
                Jupyter.notebook && 
                Jupyter.notebook.metadata && 
                Jupyter.notebook.metadata.openbisHistoryId) {
                resultDatasetHistoryId = Jupyter.notebook.metadata.openbisHistoryId;
            }
            // Search for the history id on the cells, older format
            if (!resultDatasetHistoryId) {
                var resultDatasetHistoryIdIdx = 0;
                while(Jupyter.notebook.get_cell(resultDatasetHistoryIdIdx) != null) {
                    var cell = Jupyter.notebook.get_cell(resultDatasetHistoryIdIdx);
                    var cellText = cell.get_text();
                    if(cell.get_text().startsWith("resultDatasetHistoryId='")) {
                        var firstIndexOf = cell.get_text().indexOf("'");
                        var lastIndexOf = cell.get_text().indexOf("'", firstIndexOf + 1);
                        resultDatasetHistoryId = cell.get_text().substring(firstIndexOf + 1, lastIndexOf);
                    }
                    resultDatasetHistoryIdIdx++;
                }
            }
            return resultDatasetHistoryId; // We always return at least the empty one
        }

        function getDatasetTypes(env, connection_name, dataset_types, input_fields) {
            // get all DatasetTypes of a given connection

            var url = env.notebook.base_url + 'openbis/datasetTypes/' + connection_name
            fetch(url)
                .then(function (response) {
                    if (response.ok) {
                        response.json()
                            .then(function (data) {
                                var change_input_fields = function () {
                                    hideSpinner(env)
                                    cleanErrors()

                                    var oldType = state.uploadDataSetType
                                    if (oldType != null && !(oldType in state.uploadDataSetTypes)) {
                                        state.uploadDataSetTypes[oldType] = {}
                                    }

                                    state.uploadDataSetType = dataset_types.options[dataset_types.selectedIndex].value

                                    // remove existing input fields
                                    while (input_fields.firstChild) {
                                        var element = input_fields.firstChild

                                        if (element.nodeName === "INPUT" && state.uploadDataSetType != null) {
                                            state.uploadDataSetTypes[oldType][element.name] = element.value
                                        }

                                        input_fields.removeChild(element)
                                    }

                                    // for every property assignment, create an input field.
                                    for (pa of dts[dataset_types.selectedIndex].propertyAssignments) {
                                        var input_title = document.createElement("STRONG")
                                        input_title.textContent = pa.mandatory ? pa.label + " (mandatory)" : pa.label
                                        var input_error = createErrorElement('prop.'+pa.code)

                                        var input_field = document.createElement("INPUT")
                                        input_field.type = "text"
                                        input_field.name = pa.code
                                        input_field.placeholder = pa.description ? pa.description : pa.label
                                        input_field.size = 90
                                        input_field.style.width="100%"

                                        var mem = state.uploadDataSetTypes[dts[dataset_types.selectedIndex].code]
                                        if (mem == null) {
                                            mem = {
                                                "$HISTORY_ID" : getOpenBISHistoryId() // History Id should get automatically populated if available
                                            }
                                        }
                                        input_field.value = pa.code in mem ? mem[pa.code] : ""

                                        input_fields.appendChild(input_title)
                                        input_fields.appendChild(input_error)
                                        input_fields.appendChild(input_field)
                                        input_fields.appendChild(document.createElement("BR"))
                                    }
                                }
                                dataset_types.onchange = change_input_fields

                                // remove the old and add the new dataset-types
                                dts = data.dataSetTypes
                                while (dataset_types.firstChild) {
                                    dataset_types.removeChild(dataset_types.firstChild);
                                }
                                var index = 0
                                var selectedIndex = -1
                                for (dt of dts) {
                                    var option = document.createElement("OPTION")
                                    option.value = dt.code
                                    option.textContent = dt.description ? dt.code + ": " + dt.description : dt.code
                                    dataset_types.appendChild(option)

                                    if (dt.code === state.uploadDataSetType) {
                                        selectedIndex = index
                                    }
                                    index++
                                }

                                dataset_types.selectedIndex = selectedIndex === -1 ? 0 : selectedIndex
                                // change the input fields, since we just received new datasetTypes
                                change_input_fields()

                            })
                            .catch(function (error) {
                                console.error("Error while parsing dataset types", error)
                            })

                    }
                    else {
                        // ! response.ok
                        errorElements.main.textContent = "Network problem: please check your connection first."
                        while (dataset_types.firstChild) {
                            dataset_types.removeChild(dataset_types.firstChild);
                        }
                    }
                })
                .catch(function (error) {
                    console.error("Error while fetching dataset types:", error)
                })

        }

        function createSelectTable(values, container, checked, overrides) {

            var table = document.createElement("TABLE")
            table.className = 'table-bordered table-striped table-condensed'
            table.style.width = "100%"
            
            var body = table.createTBody()

            var checkboxes = []
            values.forEach( value => {
                var row = body.insertRow()
                var checkbox = document.createElement("INPUT")
                checkbox.type = "checkbox"
                checkbox.value = value
                checkbox.checked = overrides.includes(value) ? !checked :  checked
                checkboxes.push(checkbox)
                row.insertCell().appendChild(checkbox)
                var valueCell = row.insertCell()
                valueCell.textContent = value
                valueCell.style.width = "100%"
            })
            container.appendChild(table)

            return checkboxes
        }

        function createFileTable(env, data, container, state) {
            // list of files that will be uploaded as a dataSet.

            var table = document.createElement("TABLE")
            table.className = 'table-bordered table-striped table-condensed'
            table.style.width = "100%"

            var body = table.createTBody()
            if (data.path !== "") {
                var row = body.insertRow()
                row.insertCell()
                var iconCell = row.insertCell()
                iconCell.className = "item_icon folder_icon icon-fixed-width"
                var filenameCell = row.insertCell()
                filenameCell.textContent = ".."
                filenameCell.style.width = "80%"
                filenameCell.style.cursor = "pointer"
                filenameCell.onclick = function(){
                    var elems = data.path.split('/')
                    elems.pop()
                    get_file_list(env, container, elems.join('/'))
                }

                var sizeCell = row.insertCell()
                sizeCell.style.textAlign = "right"
                sizeCell.style.width = "15%"
            }

            var registerFile = function () {
                if (this.checked) {
                    // add file to state
                    state.selectedFiles.push(this.value)
                }
                else {
                    // remove file from state
                    state.selectedFiles.splice(state.selectedFiles.indexOf(this.value), 1)
                }
            }

            var checkboxes = []
            data.content.sort( (a, b) => a.name.localeCompare(b.name, undefined, {sensitivity: 'base'}) ).forEach( file => {

                var row = body.insertRow()
                var checkboxCell = row.insertCell()
                checkboxCell.style.width="5%"
                var iconCell = row.insertCell()
                var filenameCell = row.insertCell()

                filenameCell.textContent = file.name
                filenameCell.style.width = "100%"
                filenameCell.style.cursor = "pointer"

                if (file.type === "directory") {
                    iconCell.className = "item_icon folder_icon icon-fixed-width"
                    filenameCell.onclick = function () {
                        get_file_list(env, container, file.path)
                    }
                }
                else {
                    var checkbox = document.createElement("INPUT")
                    checkbox.type = "checkbox"
                    checkbox.value = file.path
                    checkbox.checked = state.selectedFiles.includes(file.path) ? true: false
                    checkbox.onclick = registerFile
                    checkboxes.push(checkbox)
                    checkboxCell.appendChild(checkbox)

                    if (file.type === "notebook") {
                        iconCell.className = "item_icon notebook_icon icon-fixed-width"
                    }
                    else {
                        iconCell.className = "item_icon file_icon icon-fixed-width"
                    }
                    filenameCell.onclick = function () {
                        checkbox.checked = !checkbox.checked
                        registerFile.call(checkbox)
                    }
                }

                var sizeCell = row.insertCell()
                sizeCell.textContent = file.size
                sizeCell.style.textAlign = "right"
                sizeCell.style.width = "15%"

            })
            container.innerHTML = ""
            container.appendChild(table)
            return checkboxes
        }

        return {
            help: 'upload Notebook and Data to openBIS',
            icon: 'fa-upload',
            help_index: '',
            handler: function (env) {

                var main_error = createErrorElement('main')

                var dst_title = document.createElement("STRONG")
                dst_title.textContent = "choose a dataSet type"
                var dataset_types = document.createElement("SELECT")
                dataset_types.id = "dataset_type"
                dataset_types.className = "form-control select-xs"
                dataset_types.style.marginLeft = 0
                dataset_types.style.padding = 0

                var input_fields = document.createElement("DIV")
                input_fields.setAttribute("id", "upload-input-fields");

                getDatasetTypes(env, state.connection.name, dataset_types, input_fields)

                var sample_title = document.createElement("STRONG")
                sample_title.textContent = "enter a sample/experiment identifier to attach this dataSet to:"

                var sample_error = createErrorElement('entityIdentifier')

                //var entityIdentifier = entitySearcher.getEntitySearcherForUpload(state)
                var entityIdentifier = document.createElement("INPUT")
                entityIdentifier.type = "text"
                entityIdentifier.name = "entityIdentifier"
                entityIdentifier.size = 40
                entityIdentifier.placeholder = "Sample or Experiment identifier/permId"
                entityIdentifier.value = state.entityIdentifier ? state.entityIdentifier : ""

                var ds_title = document.createElement("STRONG")

                var dataSetListContainer = document.createElement("DIV")
                if (env.notebook.metadata.datasets) {
                    ds_title.textContent = "Parent DataSets"
                    dataSetListContainer.style.maxHeight="150px"
                    dataSetListContainer.style.overflow="auto"
                    get_dataset_list(env, dataSetListContainer)
                }

                var files_title = document.createElement("STRONG")
                files_title.textContent = "Files to upload"
                var fileListContainer = document.createElement("DIV")
                fileListContainer.style.height="200px"
                fileListContainer.style.overflow="auto"
                // get the relative path
                let re = new RegExp(env.notebook.notebook_name+"$")
                rel_path = env.notebook.notebook_path.replace(re, "")
                get_file_list(env, fileListContainer, rel_path)
                
                var inputs = document.createElement("DIV")
                inputs.style.marginTop = '10px'
                inputs.appendChild(main_error)
                inputs.appendChild(spinner)
                inputs.appendChild(document.createElement("BR"))
                inputs.appendChild(dst_title)
                inputs.appendChild(dataset_types)
                inputs.appendChild(input_fields)
                inputs.appendChild(sample_title)
                inputs.appendChild(document.createElement('BR'))
                inputs.appendChild(sample_error)
                inputs.appendChild(entityIdentifier)
                inputs.appendChild(document.createElement('BR'))
                inputs.appendChild(ds_title)
                inputs.appendChild(document.createElement('BR'))
                inputs.appendChild(dataSetListContainer)
                inputs.appendChild(files_title)
                inputs.appendChild(fileListContainer)

                var uploadDialogBox = $('<div/>').append(inputs)

                function saveState() {
                    state.uploadDataSetTypes[state.uploadDataSetType] = {}
                    for (element of input_fields.children) {
                        if (element.nodeName === "INPUT" && state.uploadDataSetType != null) {
                            state.uploadDataSetTypes[state.uploadDataSetType][element.name] = element.value
                        }
                    }
                    state.unselectedDatasets = state.datasetCheckboxes.filter(cb => !cb.checked).map(cb => cb.value)
                    //state.selectedFiles = state.fileCheckboxes.filter(cb => cb.checked).map(cb => cb.value)
                }

                function onOk() {
                    var connection_name = state.connection.name

                    if (!connection_name) {
                        alert("No connection selected")
                        return false
                    }

                    var uploadUrl = env.notebook.base_url + 'openbis/dataset/' + connection_name

                    // add this notebook to the list of files
                    var files = state.selectedFiles
                    if (! files.includes(env.notebook.notebook_path)) {
                        files.push(env.notebook.notebook_path)
                    }
                    console.log(files)

                    var props = {}
                    for (input of $('#upload-input-fields').find('input')) {
                        props[input.name] = input.value
                    }

                    var dataSetInfo = {
                        "type": dataset_types.value,
                        "files": files,
                        "parents": state.datasetCheckboxes.filter(cb => cb.checked).map(cb => cb.value),
                        "entityIdentifier": entityIdentifier.value,
                        "props": props
                    }
                    console.log(dataSetInfo);

                    var settings = {
                        url: uploadUrl,
                        processData: false,
                        type: 'POST',
                        dataType: 'json',
                        data: JSON.stringify(dataSetInfo),
                        contentType: 'application/json',
                        success: function (data) {
                            saveState()
                            $('div.modal').remove()
                            $('div.modal-backdrop').remove()
                            common.createFeedback('success', data.statusText)

                            // write statusText from returned data to notebooks metadata
                            if (typeof env.notebook.metadata.openbis === 'undefined') {
                                env.notebook.metadata.openbis = {}
                            }
                            if (typeof env.notebook.metadata.openbis.permIds === 'undefined') {
                                env.notebook.metadata.openbis.permIds = {}
                            }
                            if (data.permId) {
                                env.notebook.metadata.openbis.permIds[data.permId] = data.statusText
                            }
                        },
                        error: function (data) {
                            hideSpinner()

                            if ("errors" in data.responseJSON) {
                                var errors = data.responseJSON.errors
                                for (error of errors) {
                                    let key, value
                                    Object.keys(error).forEach(k => {
                                        key = k.toLowerCase()
                                        value = error[k]
                                    })
                                    errorElements[key in errorElements ? key : "main"].textContent = value
                                }
                            } else {
                                errorElements["main"].textContent = "Server error"
                            }
                        }
                    }

                    showSpinner(env)
                    cleanErrors()
                    utils.ajax(settings)
                    return false
                }

                function onCancel() {
                    saveState()
                    return true
                }
                dialog_params = {
                    body: uploadDialogBox,
                    title: 'Upload openBIS DataSet',
                    buttons: {
                        'Cancel': {
                            click: onCancel
                        },
                        'Upload': {
                            class: 'btn-primary btn-large',
                            click: onOk
                        }
                    },
                    notebook: env.notebook,
                    keyboard_manager: env.notebook.keyboard_manager
                }

                if (env.notebook.dirty === true) {
                    env.notebook.save_notebook()
                        .then(function () {
                            dialog.modal(dialog_params)
                        })
                        .catch(function (error) {
                            dialog.modal({
                                body: error,
                                title: 'Error saving the notebook',
                                buttons: {
                                    'Back': {}
                                },
                                notebook: env.notebook,
                                keyboard_manager: env.notebook.keyboard_manager
                            })
                        })
                }
                else {
                    dialog.modal(dialog_params)
                }
            }
        }
    }
)