(function () {
    window.BlazorInputFile = {
        init: function init(elem, componentInstance) {
            elem._blazorInputFileNextFileId = 0;

            elem.addEventListener('change', function handleInputFileChange(event) {
                elem._blazorFilesById = {};
                var fileList = Array.prototype.map.call(elem.files, function (file) {
                    var result = {
                        id: ++elem._blazorInputFileNextFileId,
                        lastModified: new Date(file.lastModified).toISOString(),
                        name: file.name,
                        size: file.size,
                        type: file.type,
                        relativePath: file.webkitRelativePath
                    };
                    elem._blazorFilesById[result.id] = result;
                    Object.defineProperty(result, 'blob', { value: file });

                    return result;
                });

                componentInstance.invokeMethodAsync('NotifyChange', fileList).then(function () {
                    elem.value = '';
                }, function (err) {
                    elem.value = '';
                    throw new Error(err);
                });
            });
        },

        toImageFile(elem, fileId, format, maxWidth, maxHeight) {
            var originalFile = getFileById(elem, fileId);

            return new Promise(function (resolve) {
                var originalFileImage = new Image();
                originalFileImage.onload = function () { resolve(originalFileImage); };
                originalFileImage.src = URL.createObjectURL(originalFile.blob);
            }).then(function (loadedImage) {
                return new Promise(function (resolve) {
                    var desiredWidthRatio = Math.min(1, maxWidth / loadedImage.width);
                    var desiredHeightRatio = Math.min(1, maxHeight / loadedImage.height);
                    var chosenSizeRatio = Math.min(desiredWidthRatio, desiredHeightRatio);

                    var canvas = document.createElement('canvas');
                    canvas.width = Math.round(loadedImage.width * chosenSizeRatio);
                    canvas.height = Math.round(loadedImage.height * chosenSizeRatio);
                    canvas.getContext('2d').drawImage(loadedImage, 0, 0, canvas.width, canvas.height);
                    canvas.toBlob(resolve, format);
                });
            }).then(function (resizedImageBlob) {
                var result = {
                    id: ++elem._blazorInputFileNextFileId,
                    lastModified: originalFile.lastModified,
                    name: originalFile.name,
                    size: resizedImageBlob.size,
                    type: format,
                    relativePath: originalFile.relativePath
                };

                elem._blazorFilesById[result.id] = result;

                Object.defineProperty(result, 'blob', { value: resizedImageBlob });

                return result;
            });
        },

        readFileData: function readFileData(elem, fileId, startOffset, count) {
            var readPromise = getArrayBufferFromFileAsync(elem, fileId);

            return readPromise.then(function (arrayBuffer) {
                var uint8Array = new Uint8Array(arrayBuffer, startOffset, count);
                var base64 = uint8ToBase64(uint8Array);
                return base64;
            });
        },

        ensureArrayBufferReadyForSharedMemoryInterop: function ensureArrayBufferReadyForSharedMemoryInterop(elem, fileId) {
            return getArrayBufferFromFileAsync(elem, fileId).then(function (arrayBuffer) {
                getFileById(elem, fileId).arrayBuffer = arrayBuffer;
            });
        },

        readFileDataSharedMemory: function readFileDataSharedMemory(readRequest) {
            var inputFileElementReferenceId = Blazor.platform.readStringField(readRequest, 0);
            var inputFileElement = document.querySelector('[_bl_' + inputFileElementReferenceId + ']');
            var fileId = Blazor.platform.readInt32Field(readRequest, 4);
            var sourceOffset = Blazor.platform.readUint64Field(readRequest, 8);
            var destination = Blazor.platform.readInt32Field(readRequest, 16);
            var destinationOffset = Blazor.platform.readInt32Field(readRequest, 20);
            var maxBytes = Blazor.platform.readInt32Field(readRequest, 24);

            var sourceArrayBuffer = getFileById(inputFileElement, fileId).arrayBuffer;
            var bytesToRead = Math.min(maxBytes, sourceArrayBuffer.byteLength - sourceOffset);
            var sourceUint8Array = new Uint8Array(sourceArrayBuffer, sourceOffset, bytesToRead);

            var destinationUint8Array = Blazor.platform.toUint8Array(destination);
            destinationUint8Array.set(sourceUint8Array, destinationOffset);

            return bytesToRead;
        }
    };

    function getFileById(elem, fileId) {
        var file = elem._blazorFilesById[fileId];
        if (!file) {
            throw new Error('There is no file with ID ' + fileId + '. The file list may have changed');
        }

        return file;
    }

    function getArrayBufferFromFileAsync(elem, fileId) {
        var file = getFileById(elem, fileId);

        if (!file.readPromise) {
            file.readPromise = new Promise(function (resolve, reject) {
                var reader = new FileReader();
                reader.onload = function () { resolve(reader.result); };
                reader.onerror = function (err) { reject(err); };
                reader.readAsArrayBuffer(file.blob);
            });
        }

        return file.readPromise;
    }

    var uint8ToBase64 = (function () {
        var lookup = [];

        var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
        for (var i = 0, len = code.length; i < len; ++i) {
            lookup[i] = code[i];
        }

        function tripletToBase64(num) {
            return lookup[num >> 18 & 0x3F] +
                lookup[num >> 12 & 0x3F] +
                lookup[num >> 6 & 0x3F] +
                lookup[num & 0x3F];
        }

        function encodeChunk(uint8, start, end) {
            var tmp;
            var output = [];
            for (var i = start; i < end; i += 3) {
                tmp =
                    ((uint8[i] << 16) & 0xFF0000) +
                    ((uint8[i + 1] << 8) & 0xFF00) +
                    (uint8[i + 2] & 0xFF);
                output.push(tripletToBase64(tmp));
            }
            return output.join('');
        }

        return function fromByteArray(uint8) {
            var tmp;
            var len = uint8.length;
            var extraBytes = len % 3;
            var parts = [];
            var maxChunkLength = 16383;
            for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
                parts.push(encodeChunk(
                    uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)
                ));
            }
            if (extraBytes === 1) {
                tmp = uint8[len - 1];
                parts.push(
                    lookup[tmp >> 2] +
                    lookup[(tmp << 4) & 0x3F] +
                    '=='
                );
            } else if (extraBytes === 2) {
                tmp = (uint8[len - 2] << 8) + uint8[len - 1];
                parts.push(
                    lookup[tmp >> 10] +
                    lookup[(tmp >> 4) & 0x3F] +
                    lookup[(tmp << 2) & 0x3F] +
                    '='
                );
            }

            return parts.join('');
        };
    })();
})();