"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Thumbnail = void 0;
exports.MediaArrayField = MediaArrayField;
const React = __importStar(require("react"));
const PropTypes = __importStar(require("prop-types"));
const immutability_helper_1 = __importDefault(require("immutability-helper"));
const Context_1 = __importDefault(require("../../Context"));
const react_dropzone_1 = __importDefault(require("react-dropzone"));
const components_1 = require("../components");
const LajiForm_1 = __importDefault(require("../LajiForm"));
const utils_1 = require("../../utils");
const react_spinner_1 = __importDefault(require("react-spinner"));
const exifreader_1 = __importDefault(require("exifreader"));
const utils_2 = require("@luomus/laji-map/lib/utils");
const moment_1 = __importDefault(require("moment"));
const ReactContext_1 = __importDefault(require("../../ReactContext"));
const utils_3 = require("@rjsf/utils");
let mediaUuid = 0;
let ImageArrayField = class ImageArrayField extends React.Component {
    constructor() {
        super(...arguments);
        this.ALLOWED_FILE_TYPES = ["image/jpeg", "image/png", "image/bmp", "image/tiff", "image/gif"];
        this.ACCEPT_FILE_TYPES = ["image/*"];
        this.MAX_FILE_SIZE = 20 * 1024 * 1024;
        this.KEY = "IMAGE";
        this.ENDPOINT = "images";
        this.GLYPH = "camera";
        this.TRANSLATION_TAKE_NEW = "TakeNewPhoto";
        this.TRANSLATION_SELECT_FILE = "SelectPhoto";
        this.TRANSLATION_NO_MEDIA = "NoPhoto";
        this.CONTAINER_CLASS = "images-container";
        this.METADATA_FORM_ID = "JX.111712";
        this.renderMedia = (id) => React.createElement(Thumbnail, { id: id, apiClient: this.props.formContext.apiClient });
        this.renderLoadingMedia = (id) => React.createElement(Thumbnail, { dataURL: id, loading: true, apiClient: this.props.formContext.apiClient });
        this.onMediaClick = (i) => this.openModalFor(i);
        this.renderModalMedia = () => React.createElement("img", { src: this.state.modalMediaSrc });
    }
    formatValue(value, options, props) {
        const { translations } = props.formContext;
        return value && value.length
            ? `${value.length} ${translations.HowManyPhotos}`
            : null;
    }
};
ImageArrayField = __decorate([
    MediaArrayField
], ImageArrayField);
exports.default = ImageArrayField;
function MediaArrayField(ComposedComponent) {
    class MediaArrayField extends ComposedComponent {
        constructor(...args) {
            super(...args);
            this.deprecatedOptions = {
                imageAddModal: "addModal",
                autoOpenImageAddModal: "autoOpenAddModal"
            };
            this.addMediaContainerRef = React.createRef();
            this.getOptions = (uiSchema) => {
                let options = (0, utils_1.getUiOptions)(uiSchema);
                Object.keys(this.deprecatedOptions).forEach(deprecated => {
                    if (options[deprecated] !== undefined) {
                        options = Object.assign(Object.assign({}, options), { [this.deprecatedOptions[deprecated]]: options[deprecated] });
                    }
                });
                return options;
            };
            this.onDragEnter = () => { this.setState({ dragging: true }); };
            this.onDragLeave = () => { this.setState({ dragging: false }); };
            this.onDrop = (files) => {
                this.state.dragging && this.setState({ dragging: false });
                this.onFileFormChange(files);
            };
            this.defaultOnClick = () => {
                const { addModal } = (0, utils_1.getUiOptions)(this.props.uiSchema);
                this.setState({ addModal });
            };
            this.onKeyDown = this.props.formContext.utils.keyboardClick(() => {
                var _a;
                const input = (_a = this.addMediaContainerRef.current) === null || _a === void 0 ? void 0 : _a.querySelector("input");
                const { addModal } = (0, utils_1.getUiOptions)(this.props.uiSchema);
                addModal ? this.defaultOnClick() : input === null || input === void 0 ? void 0 : input.click();
            });
            this.renderMedias = () => {
                const { disabled, readonly } = this.props;
                const { hideDeleteButton = false, deleteConfirmPlacement = "top" } = (0, utils_1.getUiOptions)(this.props.uiSchema);
                return (this.props.formData || []).map((item, i) => (React.createElement("div", { key: i, className: "media-container" },
                    React.createElement("div", { className: "media-container-link", onClick: this.onMediaClick(i) }, this.renderMedia(item, i)),
                    !hideDeleteButton && React.createElement(components_1.DeleteButton, { corner: true, confirm: true, confirmPlacement: deleteConfirmPlacement, translations: this.props.formContext.translations, onClick: this.onMediaRmClick(i), disabled: disabled || readonly, id: `${this.props.idSchema.$id}_${i}` }))));
            };
            this.renderLoadingMedias = () => {
                const containerId = this.getContainerId();
                return (this.state.tmpMedias || []).map((item, i) => {
                    const medias = this._context.tmpMedias[containerId];
                    if (!medias || !medias[item])
                        return null;
                    return (React.createElement("div", { key: i, className: "media-container" },
                        React.createElement("div", { className: "media-container-link" }, this.renderLoadingMedia(medias[item]))));
                });
            };
            this.openModalFor = (i) => () => {
                const item = this.props.formData[i];
                this.setState({ metadataModalOpen: i });
                this.fetching = item;
                this.apiClient.get(`/${this.ENDPOINT}/{id}`, { path: { id: item } }).then((response) => {
                    if (response.id !== this.fetching)
                        return;
                    this._context.metadatas[item] = response;
                    this.setState({ modalIdx: i, modalMediaSrc: response.fullURL, modalMetadata: this._context.metadatas[item] });
                });
            };
            this.onMediaRmClick = (i) => () => {
                const id = this.props.formData[i];
                this.props.onChange((0, immutability_helper_1.default)(this.props.formData, { $splice: [[i, 1]] }));
                try {
                    this.apiClient.delete(`/${this.ENDPOINT}/{id}`, { path: { id } });
                }
                catch (e) {
                }
            };
            this.hideMetadataModal = () => this.setState({ metadataModalOpen: false, metadataSaveSuccess: undefined });
            this.onMetadataFormChange = (formData) => this.setState({ modalMetadata: formData });
            this.renderMetadataModal = () => {
                const { metadataModalOpen, modalIdx, modalMetadata, metadataSaveSuccess } = this.state;
                const { lang, translations } = this.props.registry.formContext;
                const metadataForm = this.state.metadataForm || {};
                if (typeof metadataModalOpen === "number" && !this.state.metadataForm) {
                    const { metadataFormId = this.METADATA_FORM_ID } = (0, utils_1.getUiOptions)(this.props.uiSchema);
                    this.apiClient.get("/forms/{id}", { path: { id: metadataFormId }, query: { format: "schema" } })
                        .then(metadataForm => {
                        if (this.mounted) {
                            this.setState({ metadataForm });
                        }
                    });
                }
                const { Previous, Next } = this.props.formContext.translations;
                const isOpen = modalIdx === metadataModalOpen && modalMetadata && metadataForm.schema;
                const uiSchema = isOpen ? Object.assign(Object.assign({}, metadataForm.uiSchema), { "ui:shortcuts": Object.assign(Object.assign({}, (metadataForm.uiSchema["ui:shorcuts"] || {})), this.props.formContext.services.keyHandler.shortcuts), "ui:disabled": this.props.disabled, "ui:readonly": this.props.readonly }) : undefined;
                const { metadataModal = true } = (0, utils_1.getUiOptions)(this.props.uiSchema);
                const { Modal, Alert, Pager } = this.context.theme;
                return typeof metadataModalOpen === "number" ?
                    React.createElement(Modal, { dialogClassName: "laji-form media-modal", show: true, onHide: this.hideMetadataModal },
                        React.createElement(Modal.Header, { closeButton: true },
                            React.createElement("br", null),
                            React.createElement(Pager, null,
                                React.createElement(Pager.Item, { previous: true, onClick: this.openModalFor(metadataModalOpen - 1), disabled: metadataModalOpen <= 0 },
                                    "\u2190 ",
                                    Previous),
                                React.createElement(Pager.Item, { next: true, onClick: this.openModalFor(metadataModalOpen + 1), disabled: metadataModalOpen >= this.props.formData.length - 1 },
                                    Next,
                                    " \u2192"))),
                        React.createElement(Modal.Body, null,
                            React.createElement("div", { className: `laji-form${metadataModal ? " media-modal-content" : ""}` }, isOpen
                                ? React.createElement(React.Fragment, null,
                                    this.renderModalMedia(modalIdx),
                                    metadataModal && React.createElement(LajiForm_1.default, Object.assign({}, metadataForm, { uiSchema: uiSchema, formData: modalMetadata, onChange: this.onMetadataFormChange, onSubmit: this.onMediaMetadataUpdate, submitText: translations.Save, lang: lang, apiClient: this.props.formContext.apiClient.apiClient, uiSchemaContext: this.props.formContext.uiSchemaContext, showShortcutButton: false }), (metadataSaveSuccess !== undefined) ? (React.createElement(Alert, { variant: metadataSaveSuccess ? "success" : "danger" }, translations[metadataSaveSuccess ? "SaveSuccess" : "SaveFail"])) : null))
                                : React.createElement(react_spinner_1.default, null)))) : null;
            };
            this.onHideMediaAddModal = () => this.setState({ addModal: undefined }, () => {
                this.parseExif([]);
            });
            this.renderMediaAddModal = () => {
                const { disabled, readonly } = this.props;
                const { addModal } = this.state;
                const { labels: { cancel } = { cancel: undefined } } = ((0, utils_1.isObject)(addModal) ? addModal : {});
                const { translations } = this.props.formContext;
                if (!addModal)
                    return null;
                const { Modal } = this.context.theme;
                return (React.createElement(Modal, { dialogClassName: "laji-form media-add-modal", show: true, onHide: this.onHideMediaAddModal },
                    React.createElement(Modal.Header, { closeButton: true }),
                    React.createElement(Modal.Body, null,
                        [
                            ["environment",
                                this.TRANSLATION_TAKE_NEW
                            ],
                            [
                                undefined,
                                this.TRANSLATION_SELECT_FILE
                            ]
                        ].map(([captureMethod, label]) => React.createElement(react_dropzone_1.default, { key: captureMethod || "", accept: this.ACCEPT_FILE_TYPES, onDrop: this.onDrop, disabled: readonly || disabled }, ({ getRootProps, getInputProps }) => {
                            return (React.createElement("div", Object.assign({ className: "btn-block" }, getRootProps()),
                                React.createElement("input", Object.assign({}, getInputProps(), { capture: captureMethod })),
                                React.createElement(components_1.Button, { block: true, disabled: readonly || disabled }, label ? translations[label] : undefined)));
                        })),
                        React.createElement(components_1.Button, { className: "cancel", block: true, onClick: this.onHideMediaAddModal }, cancel || translations[this.TRANSLATION_NO_MEDIA]))));
            };
            this.onAlertOk = () => {
                this.setState({ alert: false, alertMsg: undefined });
            };
            this.parseExif = (files) => {
                const { exifParsers = [] } = (0, utils_1.getUiOptions)(this.props.uiSchema);
                if (!exifParsers)
                    return Promise.resolve({});
                const found = exifParsers.reduce((found, { parse }) => {
                    found[parse] = false;
                    return found;
                }, {});
                return files.reduce((promise, file) => {
                    if (Object.keys(found).every(k => found[k])) {
                        return promise;
                    }
                    return promise.then(found => new Promise(resolve => {
                        exifreader_1.default.load(file).then(tags => {
                            var _a, _b;
                            if ("geometry" in found)
                                try {
                                    const coordinates = ["GPSLongitude", "GPSLatitude"].map(tag => {
                                        var _a;
                                        const coord = (_a = tags[tag]) === null || _a === void 0 ? void 0 : _a.description;
                                        return coord;
                                    });
                                    const rawDatum = (_a = tags["GPSMapDatum"]) === null || _a === void 0 ? void 0 : _a.description;
                                    const datum = typeof rawDatum === "string"
                                        ? rawDatum.trim().toUpperCase()
                                        : undefined;
                                    if ((!datum || datum === "WGS-84" || datum === "WGS84") && (0, utils_2.validateLatLng)(coordinates, utils_2.wgs84Validator)) {
                                        found.geometry = {
                                            type: "Point",
                                            coordinates
                                        };
                                    }
                                }
                                catch (e) {
                                    console.info("Reading GPS from EXIF failed", e);
                                }
                            const readDateFromFile = () => {
                                if (file.lastModified) {
                                    const momentDate = (0, moment_1.default)(file.lastModified);
                                    if (momentDate.isValid()) {
                                        const date = momentDate.format("YYYY-MM-DDTHH:mm");
                                        if (date) {
                                            found.date = date;
                                        }
                                    }
                                }
                            };
                            if ("date" in found) {
                                try {
                                    const rawDate = (_b = tags["DateTimeOriginal"]) === null || _b === void 0 ? void 0 : _b.description;
                                    const momentDate = (0, moment_1.default)(rawDate, "YYYY:MM:DD HH:mm:ss");
                                    if (momentDate.isValid()) {
                                        found.date = momentDate.format("YYYY-MM-DDTHH:mm");
                                    }
                                    else {
                                        readDateFromFile();
                                    }
                                }
                                catch (e) {
                                    console.info("Reading date from EXIF failed, trying to read from file", e);
                                    readDateFromFile();
                                }
                            }
                            else {
                                readDateFromFile();
                            }
                            resolve(found);
                        });
                    }));
                }, Promise.resolve(found)).then((found) => {
                    let { registry } = this.props;
                    const lajiFormInstance = this.props.formContext.services.rootInstance;
                    const schema = lajiFormInstance.getSchema();
                    let formData = lajiFormInstance.getFormData();
                    let defaultMediaMetadata = {};
                    exifParsers.filter((f) => f.type === "event" || found[f.parse]).forEach(({ field, parse, type, eventName }) => {
                        if (type === "mutate") {
                            formData = (0, utils_1.updateFormDataWithJSONPointer)({ formData, schema, registry }, found[parse], field);
                        }
                        if (type === "mutateMetadata") {
                            defaultMediaMetadata = (0, utils_1.updateSafelyWithJSONPointer)(defaultMediaMetadata, found[parse], field);
                        }
                        if (type === "event") {
                            this.props.formContext.services.customEvents.send(`root_${(0, utils_1.JSONPointerToId)(field)}`, eventName, found[parse], undefined, { bubble: false });
                        }
                    });
                    return { formData, defaultMediaMetadata };
                });
            };
            this.sideEffects = (formData) => {
                const lajiFormInstance = this.props.formContext.services.rootInstance;
                const schema = lajiFormInstance.getSchema();
                const lajiFormFormData = lajiFormInstance.getFormData();
                const { sideEffects } = (0, utils_1.getUiOptions)(this.props.uiSchema);
                if (sideEffects) {
                    const thisPath = (0, utils_1.idSchemaIdToJSONPointer)(this.props.idSchema.$id);
                    const containerPath = thisPath.replace(/^(\/.*)\/.*$/, "$1");
                    const parseRelativePaths = (path, containerPath) => {
                        while ((path.match(/\/\.\./g) || []).length > 1) {
                            containerPath = containerPath.replace(/^(\/.*)\/.*$/, "$1");
                            path = path.replace(/^(.*)\/\.\.(.*)/, "$1$2");
                        }
                        return path.replace(/^(.*)\/\.\.(.*)/, `$1${containerPath}$2`);
                    };
                    formData = Object.keys(sideEffects).reduce((formData, field) => {
                        const jsonPointer = parseRelativePaths(field, containerPath);
                        return !(0, utils_1.isDefaultData)((0, utils_1.parseJSONPointer)(formData, jsonPointer), (0, utils_1.parseSchemaFromFormDataPointer)(schema, jsonPointer))
                            ? formData
                            : (0, utils_1.updateFormDataWithJSONPointer)({ schema, registry: this.props.registry, formData }, sideEffects[field], parseRelativePaths(field, containerPath));
                    }, formData);
                }
                if (formData !== lajiFormFormData) {
                    lajiFormInstance.onChange(formData);
                }
            };
            this.onFileFormChange = (files) => {
                if (this.state.addModal) {
                    this.setState({ addModal: undefined });
                }
                const exifDataPromise = this.parseExif(files);
                exifDataPromise.then(data => {
                    if (data.formData) {
                        this.sideEffects(data.formData);
                    }
                });
                const id = this.getContainerId();
                const lajiFormInstance = this.props.formContext.services.rootInstance;
                const saveAndOnChange = () => this.saveMedias(files, exifDataPromise).then(mediaIds => {
                    if (!lajiFormInstance.isMounted() || !mediaIds) {
                        return;
                    }
                    let pointer = this.props.formContext.services.ids.getJSONPointerFromLajiFormIdAndFormDataAndIdSchemaId(this.props.idSchema.$id, id);
                    if (!this.mounted && !pointer) {
                        return;
                    }
                    const newFormData = [
                        ...(this.mounted
                            ? this.props.formData || []
                            : (0, utils_1.parseJSONPointer)(lajiFormInstance.getFormData(), pointer) || []),
                        ...mediaIds
                    ];
                    if (!lajiFormInstance.isMounted())
                        return;
                    if ((this.mounted || id === "root") && id === this.getContainerId()) {
                        this.props.onChange(newFormData);
                        // Settimeout because the resource is undefined 404 if fetched right away.
                        setTimeout(() => {
                            if (!this.mounted)
                                return;
                            const { autoOpenMetadataModal = false } = (0, utils_1.getUiOptions)(this.props.uiSchema);
                            let shouldOpenMetadataModal = autoOpenMetadataModal;
                            if (shouldOpenMetadataModal) {
                                this.openModalFor(newFormData.length - mediaIds.length)();
                            }
                        }, 0);
                        return;
                    }
                    pointer = this.props.formContext.services.ids.getJSONPointerFromLajiFormIdAndFormDataAndIdSchemaId(this.props.idSchema.$id, id);
                    lajiFormInstance.onChange((0, utils_1.updateSafelyWithJSONPointer)(lajiFormInstance.getFormData(), newFormData, pointer));
                });
                this.props.formContext.services.submitHooks.add(this.props, saveAndOnChange);
            };
            this.getContainerId = () => {
                const { _parentLajiFormId = "root" } = this.props.formContext;
                return _parentLajiFormId;
            };
            this.addNameToDataURL = (dataURL, name) => {
                return dataURL.replace(";base64", `;name=${name};base64`);
            };
            this.processFiles = (files) => {
                return Promise.all([].map.call(files, this.processFile));
            };
            this.processFile = (file) => {
                const { name, size, type } = file;
                return new Promise(resolve => {
                    const reader = new window.FileReader();
                    reader.onload = event => {
                        var _a;
                        resolve({
                            dataURL: this.addNameToDataURL((_a = event.target) === null || _a === void 0 ? void 0 : _a.result, name),
                            name,
                            size,
                            type
                        });
                    };
                    reader.readAsDataURL(file);
                });
            };
            this.onMediaMetadataUpdate = (_a) => __awaiter(this, [_a], void 0, function* ({ formData }) {
                this.props.formContext.services.blocker.push();
                try {
                    yield this.apiClient.put(`/${this.ENDPOINT}/{id}`, { path: { id: formData.id } }, formData);
                    this.props.formContext.services.blocker.pop();
                    const notify = () => this.props.formContext.notifier.success(this.props.formContext.translations.SaveSuccess);
                    if (this.mounted) {
                        this.setState({ metadataModalOpen: false }, notify);
                    }
                    else {
                        notify();
                    }
                }
                catch (e) {
                    this.props.formContext.services.blocker.pop();
                    this.mounted && this.setState({ metadataSaveSuccess: false });
                }
            });
            this.getMetadataPromise = (exifDataPromise) => {
                let mediaMetadata = this.props.formContext.mediaMetadata
                    || { intellectualRights: "MZ.intellectualRightsARR" };
                const MACode = this.props.formContext.uiSchemaContext.creator;
                const metadataPromise = ("capturerVerbatim" in mediaMetadata)
                    ? Promise.resolve(Object.assign(Object.assign({}, mediaMetadata), { capturerVerbatim: Array.isArray(mediaMetadata.capturerVerbatim) ? mediaMetadata.capturerVerbatim : [mediaMetadata.capturerVerbatim] }))
                    : MACode
                        ? this.apiClient.get("/person/{id}", { path: { id: MACode } }).then(({ fullName = MACode }) => (Object.assign(Object.assign({ capturerVerbatim: Array.isArray(fullName) ? fullName : [fullName] }, mediaMetadata), { intellectualOwner: fullName }))).catch(() => Promise.resolve(mediaMetadata))
                        : Promise.resolve(mediaMetadata);
                return Promise.all([exifDataPromise, metadataPromise]).then(([exifData, metadata]) => {
                    return Object.assign(Object.assign({}, exifData.defaultMediaMetadata), metadata);
                });
            };
            this.getMaxFileSizeAsString = () => {
                let maxSize = this.MAX_FILE_SIZE.toString().substring(0, this.MAX_FILE_SIZE.toString().length - 6);
                return maxSize + " " + `${this.props.formContext.translations.Mb}`;
            };
            this.getAllowedMediaFormatsTranslationKey = () => {
                return this.ALLOWED_FILE_TYPES.length === 1 ? "AllowedFileFormat" : "AllowedFileFormats";
            };
            this.getAllowedMediaFormatsAsString = () => {
                let formats = "";
                for (let i = 0; i < this.ALLOWED_FILE_TYPES.length; i++) {
                    formats += this.ALLOWED_FILE_TYPES[i].split("/")[1];
                    if (i < this.ALLOWED_FILE_TYPES.length - 2) {
                        formats += ", ";
                    }
                    else if (i === this.ALLOWED_FILE_TYPES.length - 2) {
                        formats += ` ${this.props.formContext.translations.and} `;
                    }
                }
                return formats;
            };
            const [props] = args;
            ["ALLOWED_FILE_TYPES",
                "MAX_FILE_SIZE",
                "KEY",
                "ENDPOINT",
                "GLYPH",
                "renderMedia",
                "renderLoadingMedia",
                "onMediaClick",
                "formatValue",
                "TRANSLATION_TAKE_NEW",
                "TRANSLATION_SELECT_FILE",
                "TRANSLATION_NO_MEDIA",
                "CONTAINER_CLASS"
            ].forEach(prop => {
                if (this[prop] === undefined) {
                    throw new Error(`${(0, utils_1.getReactComponentName)(ComposedComponent)} doesn't implement MediaArrayField ${prop}`);
                }
            });
            const options = this.getOptions(props.uiSchema);
            Object.keys(this.deprecatedOptions).forEach(deprecated => {
                if (options[deprecated] !== undefined) {
                    console.warn(`laji-form warning: ${(0, utils_1.getReactComponentName)(ComposedComponent)} ui:option '${deprecated}' is deprecated. Use '${this.deprecatedOptions[deprecated]}' instead!`);
                }
            });
            this.apiClient = props.formContext.apiClient;
            this._context = (0, Context_1.default)(`${this.KEY}_ARRAY_FIELD`);
            if (!this._context.metadatas)
                this._context.metadatas = {};
            if (!this._context.tmpMedias)
                this._context.tmpMedias = {};
            this.state = { tmpMedias: Object.keys(this._context.tmpMedias[this.getContainerId()] || {}).map(i => +i) };
            const { addModal, autoOpenAddModal } = options;
            if (addModal
                && autoOpenAddModal
                && (props.formData || []).length === 0
                && !props.formContext.uiSchemaContext.isEdit) {
                this.state.addModal = addModal;
            }
        }
        componentDidMount() {
            this.mounted = true;
        }
        componentWillUnmount() {
            this.mounted = false;
        }
        render() {
            const { schema, uiSchema, idSchema, name, formContext, readonly, disabled } = this.props;
            const { translations } = formContext;
            const { description, addModal } = (0, utils_1.getUiOptions)(uiSchema);
            const title = (schema.title === undefined) ? name : schema.title;
            const TitleFieldTemplate = (0, utils_3.getTemplate)("TitleFieldTemplate", this.props.registry, (0, utils_1.getUiOptions)(this.props.uiSchema));
            const { OverlayTrigger, Tooltip, Glyphicon } = this.context.theme;
            const tooltip = (React.createElement(Tooltip, { id: `${this.props.idSchema.$id}-drop-zone-tooltip` },
                React.createElement("span", null,
                    translations.DropOrSelectFiles,
                    ". "),
                React.createElement("span", null,
                    translations[this.getAllowedMediaFormatsTranslationKey()],
                    " ",
                    this.getAllowedMediaFormatsAsString(),
                    " ",
                    translations.and,
                    " ",
                    translations.allowedFileSize,
                    " ",
                    this.getMaxFileSizeAsString(),
                    ".")));
            const { dragging } = this.state;
            const { Row, Col } = this.context.theme;
            const { DescriptionFieldTemplate } = this.props.registry.templates;
            const mediaCount = (this.props.formData || []).length + (this.state.tmpMedias || []).length;
            const showAdd = (0, utils_1.isNullOrUndefined)(this.props.schema.maxItems) || mediaCount < this.props.schema.maxItems;
            return (React.createElement(Row, null,
                React.createElement(Col, { xs: 12 },
                    React.createElement(TitleFieldTemplate, { title: title, schema: schema, uiSchema: uiSchema, id: idSchema.$id, registry: this.props.registry }),
                    description !== undefined ? React.createElement(DescriptionFieldTemplate, { description: description, schema: schema, id: idSchema.$id, registry: this.props.registry }) : null,
                    React.createElement("div", { className: `laji-form-medias ${this.CONTAINER_CLASS}` },
                        this.renderMedias(),
                        this.renderLoadingMedias(),
                        showAdd && React.createElement(OverlayTrigger, { overlay: tooltip },
                            React.createElement(react_dropzone_1.default, { accept: this.ACCEPT_FILE_TYPES, onDragEnter: this.onDragEnter, onDragLeave: this.onDragLeave, onDrop: this.onDrop, disabled: readonly || disabled, noKeyboard: true }, ({ getRootProps, getInputProps }) => {
                                const _a = getRootProps(), { onClick: _onClick } = _a, rootProps = __rest(_a, ["onClick"]);
                                const onClick = addModal ? this.defaultOnClick : _onClick;
                                return (React.createElement("div", Object.assign({ className: (0, utils_1.classNames)("laji-form-drop-zone", dragging && "dragging", (readonly || disabled) && " disabled"), onClick: onClick, role: "button", tabIndex: 0 }, rootProps, { onKeyDown: this.onKeyDown, ref: this.addMediaContainerRef }),
                                    React.createElement("input", Object.assign({}, getInputProps())),
                                    React.createElement(Glyphicon, { glyph: this.GLYPH })));
                            })),
                        this.renderMetadataModal(),
                        this.renderMediaAddModal()))));
        }
        saveMedias(files, exifDataPromise) {
            return __awaiter(this, void 0, void 0, function* () {
                const containerId = this.getContainerId();
                let tmpMedias = [];
                const fail = (translationKey, additionalInfo = "") => {
                    const translation = (Array.isArray(translationKey) ? translationKey : [translationKey])
                        .map((key) => this.props.formContext.translations[key])
                        .join(". ");
                    throw `${translation} ${additionalInfo}`;
                };
                try {
                    const processedFiles = yield this.processFiles(files);
                    let invalidFile = (files.length <= 0);
                    let fileTooLarge = false;
                    let noValidData = true;
                    if (!this._context.tmpMedias[containerId]) {
                        this._context.tmpMedias[containerId] = {};
                    }
                    tmpMedias = processedFiles.map(f => {
                        mediaUuid++;
                        this._context.tmpMedias[containerId][mediaUuid] = f.dataURL;
                        return mediaUuid;
                    });
                    this.mounted && this.setState({ tmpMedias: [...(this.state.tmpMedias || []), ...tmpMedias] });
                    const formData = files.reduce((body, file) => {
                        if (!this.ALLOWED_FILE_TYPES.includes(file.type)) {
                            invalidFile = true;
                        }
                        else if (file.size > this.MAX_FILE_SIZE) {
                            fileTooLarge = true;
                        }
                        else {
                            body.append("data", file);
                            noValidData = false;
                        }
                        return body;
                    }, new FormData());
                    if (noValidData && invalidFile) {
                        fail(this.getAllowedMediaFormatsTranslationKey(), this.getAllowedMediaFormatsAsString() + ".");
                        return;
                    }
                    else if (noValidData && fileTooLarge) {
                        fail("AllowedFileSize", this.getMaxFileSizeAsString() + ".");
                        return;
                    }
                    const tmpMediaEntities = yield this.apiClient.post(`/${this.ENDPOINT}`, undefined, formData);
                    const mediaMetadata = yield this.getMetadataPromise(exifDataPromise);
                    const mediaEntities = yield Promise.all(tmpMediaEntities.map((item) => this.apiClient.post(`/${this.ENDPOINT}/{id}`, { path: { id: item.id } }, mediaMetadata)));
                    const ids = mediaEntities.map((item) => item ? item.id : undefined).filter(item => item !== undefined);
                    tmpMedias.forEach(id => {
                        delete this._context.tmpMedias[containerId][id];
                    });
                    this.mounted && this.setState({ tmpMedias: this.state.tmpMedias.filter(id => !tmpMedias.includes(id)) });
                    return ids;
                }
                catch (e) {
                    tmpMedias.forEach(id => {
                        delete this._context.tmpMedias[containerId][id];
                    });
                    this.mounted && this.setState({ tmpMedias: this.state.tmpMedias.filter(id => !tmpMedias.includes(id)) });
                    throw e;
                }
            });
        }
    }
    MediaArrayField.contextType = ReactContext_1.default;
    MediaArrayField.propTypes = {
        uiSchema: PropTypes.shape({
            "ui:options": PropTypes.shape({
                titleClassName: PropTypes.string,
                addModal: PropTypes.oneOfType([
                    PropTypes.bool,
                    PropTypes.shape({
                        labels: PropTypes.shape({
                            cancel: PropTypes.string
                        })
                    })
                ]),
                autoOpenAddModal: PropTypes.bool,
                autoOpenMetadataModal: PropTypes.bool,
                sideEffects: PropTypes.object,
                exifParsers: PropTypes.arrayOf(PropTypes.object),
                metadataFormId: PropTypes.string,
                hideDeleteButton: PropTypes.bool,
                deleteConfirmPlacement: PropTypes.string
            })
        }),
        schema: PropTypes.shape({
            type: PropTypes.oneOf(["array"]),
            items: PropTypes.shape({
                type: PropTypes.oneOf(["string"]).isRequired
            }).isRequired
        }).isRequired,
        formData: PropTypes.array
    };
    MediaArrayField.displayName = (0, utils_1.getReactComponentName)(ComposedComponent);
    return MediaArrayField;
}
class Thumbnail extends React.PureComponent {
    constructor(props) {
        super(props);
        this.updateURL = (_a) => __awaiter(this, [_a], void 0, function* ({ id, apiClient, apiEndpoint = "images" }) {
            if (!id)
                return;
            try {
                const response = yield apiClient.get(`/${apiEndpoint}/{id}`, { path: { id } });
                if (!this.mounted)
                    return;
                this.setState({ url: response.squareThumbnailURL, linkUrl: response.originalURL });
            }
            catch (e) {
            }
        });
        this.state = {};
        this.updateURL(props);
    }
    componentDidMount() {
        this.mounted = true;
    }
    componentWillUnmount() {
        this.mounted = false;
    }
    UNSAFE_componentWillReceiveProps(props) {
        this.updateURL(props);
    }
    render() {
        const url = this.state.url || this.props.dataURL;
        const linkUrl = this.state.linkUrl;
        const addLink = this.props.downloadLink && linkUrl;
        let img = url ? React.createElement("img", { src: url }) : null;
        img = addLink ? React.createElement("a", { href: linkUrl, target: "_blank", rel: "noopener noreferrer", download: true }, img) : img;
        return (!url || this.props.loading)
            ? React.createElement("div", { className: "media-loading" },
                img,
                React.createElement(react_spinner_1.default, null))
            : img;
    }
}
exports.Thumbnail = Thumbnail;
