"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TransientImageService = void 0;
const tslib_1 = require("tslib");
/* eslint-disable no-empty */
const core_1 = require("@loopback/core");
const exifr_1 = tslib_1.__importDefault(require("exifr"));
const lodash_1 = require("lodash");
const sharp_1 = tslib_1.__importDefault(require("sharp"));
const helper_1 = require("../../helper");
const services_1 = require("../../services");
const entity_name_1 = require("./../../models/entity-name");
const application_service_1 = require("./../../services/application.service");
const node_intercept_service_1 = require("./../../services/entity-intercept/node-intercept.service");
const inside_rest_service_1 = require("./../../services/inside-rest/inside-rest.service");
const object_node_service_1 = require("./../../services/object-node/object-node.service");
const object_type_service_1 = require("./../../services/object-type.service");
const transient_entity_service_1 = require("./../../services/transient-entity/transient-entity.service");
const uri_complete_service_1 = require("./../../services/uri-complete/uri-complete.service");
const content_image_const_1 = require("./content-image.const");
const content_image_definition_1 = require("./content-image.definition");
const exifrOptions = {
    translateKeys: true,
    translateValues: true,
    xmp: true,
    icc: true,
    iptc: true,
    jfif: true,
    ifd1: true,
    // Other TIFF tags
    makerNote: true,
    userComment: true,
};
let TransientImageService = class TransientImageService {
    constructor(transientEntityService, nodeInterceptService, insideRestService, uriCompleteService, contentImageService, objectTreeService, objectNodeService, objectTypeService) {
        this.transientEntityService = transientEntityService;
        this.nodeInterceptService = nodeInterceptService;
        this.insideRestService = insideRestService;
        this.uriCompleteService = uriCompleteService;
        this.contentImageService = contentImageService;
        this.objectTreeService = objectTreeService;
        this.objectNodeService = objectNodeService;
        this.objectTypeService = objectTypeService;
        this.nodeInterceptService.registerEntityInterceptorService(content_image_const_1.CONTENT_IMAGE_PROVIDER, TransientImageService.name, 'Set position and date from Exif image data', content_image_const_1.IMAGE_TYPE.name, application_service_1.EntityActionType.create, this.interceptImageNodeCreate.bind(this));
        this.transientEntityService.registerTransientEntityTypeFunction(content_image_const_1.CONTENT_IMAGE_PROVIDER, TransientImageService.name, 'Add referenced images field and its json schema definition. Add load Images method. Add create Gallery method.', entity_name_1.EntityName.objectNode, content_image_const_1.IMAGE_GALLERY_REFERRER_TYPE.name, this.completeImageGalleryReferrerNode.bind(this));
        this.transientEntityService.registerTransientEntityTypeFunction(content_image_const_1.CONTENT_IMAGE_PROVIDER, TransientImageService.name, 'Add load Images method definition to load several images in one operation and reset images position and date info method', entity_name_1.EntityName.objectNode, content_image_const_1.IMAGE_GALLERY_TYPE.name, this.completeImageGalleryTypeNode.bind(this));
        this.transientEntityService.registerTransientEntityTypeFunction(content_image_const_1.CONTENT_IMAGE_PROVIDER, TransientImageService.name, 'Add all images of the gallery as referenced images', entity_name_1.EntityName.objectTree, content_image_const_1.DISPLAYED_IMAGE_GALLERY_TYPE.name, this.completeDisplayedImageGalleryTree.bind(this));
        this.transientEntityService.registerTransientEntityTypeFunction(content_image_const_1.CONTENT_IMAGE_PROVIDER, transient_entity_service_1.TransientEntityService.name, 'Add image EXIF info and reset info method', entity_name_1.EntityName.objectNode, content_image_const_1.IMAGE_TYPE.name, this.completeImageTypeNode.bind(this));
    }
    async interceptImageNodeCreate(entityId, image, requestEntity, ctx) {
        try {
            if (!requestEntity.imagePosition || !requestEntity.imageDate) {
                const exif = (await this.getImageExif(requestEntity));
                if (!requestEntity.imagePosition) {
                    const position = this.getPositionFromExif(exif);
                    if (position) {
                        requestEntity.imagePosition = position;
                    }
                }
                if (!requestEntity.imageDate) {
                    const date = this.getImageDateFromExif(exif);
                    if (date) {
                        requestEntity.imageDate = date;
                    }
                }
            }
        }
        catch (error) { }
        return true;
    }
    async getImageExif(image) {
        var _a;
        let result = {};
        if ((_a = image === null || image === void 0 ? void 0 : image.contentImage) === null || _a === void 0 ? void 0 : _a.base64) {
            try {
                result = await exifr_1.default.parse(Buffer.from(image.contentImage.base64, 'base64'), exifrOptions);
            }
            finally {
                if (!result) {
                    result = {};
                }
            }
        }
        return result;
    }
    async getImageMetadata(image) {
        var _a;
        let result = {};
        if ((_a = image.contentImage) === null || _a === void 0 ? void 0 : _a.base64) {
            try {
                const img = sharp_1.default(Buffer.from(image.contentImage.base64, 'base64'));
                result = await img.metadata();
            }
            finally {
                if (!result) {
                    result = {};
                }
            }
        }
        return result;
    }
    getImageDateFromExif(exifData) {
        const imageDate = exifData.CreateDate
            ? exifData.CreateDate
            : exifData.DateTimeOriginal
                ? exifData.DateTimeOriginal
                : exifData.ModifyDate
                    ? exifData.ModifyDate
                    : undefined;
        return imageDate ? imageDate.toISOString() : undefined;
    }
    getPositionFromExif(exifData) {
        return exifData.latitude && exifData.longitude
            ? `${exifData.latitude},${exifData.longitude}`
            : undefined;
    }
    async completeImageTypeNode(imageNode, ctx) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
        if (((_a = imageNode.contentImage) === null || _a === void 0 ? void 0 : _a.base64) && ((_c = (_b = imageNode.entityCtx) === null || _b === void 0 ? void 0 : _b.jsonSchema) === null || _c === void 0 ? void 0 : _c.properties)) {
            imageNode.metadata = await this.getImageMetadata(imageNode);
            for (const key of Object.keys(imageNode.metadata)) {
                if (lodash_1.isObject(imageNode.metadata[key]) &&
                    !(imageNode.metadata[key] instanceof Date)) {
                    delete imageNode.metadata[key];
                }
            }
            imageNode.entityCtx.jsonSchema.properties.metadata = {
                title: 'Metadata image info',
                type: 'object',
                'x-schema-form': {
                    type: 'json',
                    readonly: true,
                    disabled: true,
                },
            };
            imageNode.exif = await this.getImageExif(imageNode);
            for (const key of Object.keys(imageNode.exif)) {
                if (lodash_1.isObject(imageNode.exif[key]) &&
                    !(imageNode.exif[key] instanceof Date)) {
                    delete imageNode.exif[key];
                }
            }
            const imageDate = this.getImageDateFromExif(imageNode.exif);
            if (imageDate && ((_h = (_g = (_f = (_e = (_d = imageNode.entityCtx) === null || _d === void 0 ? void 0 : _d.jsonSchema) === null || _e === void 0 ? void 0 : _e.properties) === null || _f === void 0 ? void 0 : _f.imageDate) === null || _g === void 0 ? void 0 : _g['x-schema-form']) === null || _h === void 0 ? void 0 : _h.conditionalValue)) {
                imageNode.entityCtx.jsonSchema.properties.imageDate['x-schema-form'].conditionalValue.defaultValue = `return ${JSON.stringify(imageDate)}`;
            }
            const imagePosition = this.getPositionFromExif(imageNode.exif);
            if (imagePosition && ((_o = (_m = (_l = (_k = (_j = imageNode.entityCtx) === null || _j === void 0 ? void 0 : _j.jsonSchema) === null || _k === void 0 ? void 0 : _k.properties) === null || _l === void 0 ? void 0 : _l.imagePosition) === null || _m === void 0 ? void 0 : _m['x-schema-form']) === null || _o === void 0 ? void 0 : _o.conditionalValue)) {
                imageNode.entityCtx.jsonSchema.properties.imagePosition['x-schema-form'].conditionalValue.defaultValue = `return ${JSON.stringify(imagePosition)}`;
            }
            imageNode.entityCtx.jsonSchema.properties.exif = {
                title: 'EXIF image info',
                type: 'object',
                'x-schema-form': {
                    type: 'json',
                    readonly: true,
                    disabled: true,
                },
            };
            if (imageDate || imagePosition) {
                if (!((_p = imageNode.entityCtx) === null || _p === void 0 ? void 0 : _p.actions)) {
                    imageNode.entityCtx.actions = {};
                }
                if (!imageNode.entityCtx.actions.methods) {
                    imageNode.entityCtx.actions.methods = [];
                }
                imageNode.entityCtx.actions.methods.push({
                    methodId: 'resetInfo',
                    methodName: 'Reset information',
                    actionName: 'Reset information from image',
                    parameters: {
                        type: 'object',
                        properties: {},
                    },
                    icon: 'fas fa-undo',
                });
                if (imagePosition) {
                    imageNode.entityCtx.actions.methods[imageNode.entityCtx.actions.methods.length - 1].parameters.properties.position = {
                        type: 'boolean',
                        title: `Reset position (to <a href="https://maps.google.com/maps?q=${imagePosition}&t=&z=15&ie=UTF8" target="_new">${imagePosition}</a>)`,
                        default: true,
                    };
                }
                if (imageDate) {
                    imageNode.entityCtx.actions.methods[imageNode.entityCtx.actions.methods.length - 1].parameters.properties.date = {
                        type: 'boolean',
                        title: `Reset date (to ${imageDate})`,
                        default: true,
                    };
                }
            }
        }
    }
    async completeDisplayedImageGalleryTree(entity, ctx) {
        const images = (await this.objectTreeService.getChildrenByImplementedTypeId(entity))[content_image_const_1.IMAGE_TYPE.name];
        entity.images = images ? images : [];
    }
    async completeImageGalleryTypeNode(objectNode, ctx) {
        var _a;
        if (!objectNode.entityCtx) {
            objectNode.entityCtx = { entityType: entity_name_1.EntityName.objectNode };
        }
        if (!((_a = objectNode.entityCtx) === null || _a === void 0 ? void 0 : _a.actions)) {
            objectNode.entityCtx.actions = {};
        }
        if (!objectNode.entityCtx.actions.methods) {
            objectNode.entityCtx.actions.methods = [];
        }
        await this.addLoadImagesJsonSchemaMethod(objectNode, ctx);
        const galleryTree = await this.insideRestService.read(this.uriCompleteService.getUri(entity_name_1.EntityName.objectTree, objectNode.id, ctx), ctx);
        if (0 < galleryTree.children.length) {
            let handlebarsMethodSampling = '[';
            for (const imageTree of galleryTree.children) {
                handlebarsMethodSampling +=
                    ('[' === handlebarsMethodSampling ? '' : ',') +
                        `
        {
          "uri":${JSON.stringify(imageTree.treeNode.uri)},
          "methodId": "resetInfo",
          "parameters":{
            "position": {{&json position}},
            "date": {{&json date}}
          }
        }`;
            }
            handlebarsMethodSampling += `
         {{#order}},
            {
              "methodId":"resetOrder",
              "parameters":{ }
            }
         {{/order}}
      `;
            handlebarsMethodSampling += ']';
            objectNode.entityCtx.actions.methods.push({
                methodId: 'resetInfo',
                methodName: 'Reset images information and order',
                actionName: 'Reset information from images and order them',
                parameters: {
                    type: 'object',
                    properties: {
                        position: {
                            type: 'boolean',
                            title: `Reset images position`,
                            default: true,
                        },
                        date: {
                            type: 'boolean',
                            title: `Reset images date`,
                            default: true,
                        },
                        order: {
                            type: 'boolean',
                            title: `Reset images order`,
                            default: true,
                        },
                    },
                },
                icon: 'fas fa-undo',
                handlebarsMethodSampling: handlebarsMethodSampling,
            });
        }
    }
    async getImageGalleriesParents(objectNode) {
        const galleriesType = await this.objectTypeService.getParentTypesOfImplementingType(content_image_const_1.IMAGE_GALLERY_TYPE.name);
        return this.objectNodeService.searchByParentNamespaceId(objectNode.parentNamespaceId, {
            objectTypeIds: galleriesType,
        });
    }
    async addCreateImagesGalleryJsonSchemaMethod(objectNode, ctx) {
        var _a, _b, _c, _d, _e, _f, _g;
        if (!objectNode.entityCtx) {
            objectNode.entityCtx = { entityType: entity_name_1.EntityName.objectNode };
        }
        if (!((_a = objectNode.entityCtx) === null || _a === void 0 ? void 0 : _a.actions)) {
            objectNode.entityCtx.actions = {};
        }
        if (!objectNode.entityCtx.actions.methods) {
            objectNode.entityCtx.actions.methods = [];
        }
        const imageGalleries = await this.getImageGalleriesParents(objectNode);
        if (imageGalleries && 0 < imageGalleries.length) {
            objectNode.entityCtx.actions.methods.push({
                methodId: 'createGallery',
                methodName: 'Create images gallery',
                actionName: 'Create gallery',
                parameters: {
                    type: 'object',
                    properties: {
                        useExistingGallery: {
                            type: 'boolean',
                            title: 'Use existing gallery',
                        },
                        name: {
                            type: 'string',
                            title: 'Gallery name',
                            'x-schema-form': {
                                type: 'conditional-text',
                                conditionalValue: {
                                    title: 'Specify a gallery name',
                                    defaultValue: `return ${JSON.stringify(objectNode.title)}`,
                                },
                            },
                        },
                    },
                },
                handlebarsMethodSampling: `[
          {{^useExistingGallery}}
            {
              "methodId": "createGallery",
              "parameters":{
                "name": {{&json name}},
                "parentId": {{&json parentId}}
              }
            }
          {{/useExistingGallery}}
          {{#useExistingGallery}}
            {
              "methodId": "configureGallery",
              "parameters":{
                "imageGalleryObjectTreeId": {{&json imageGalleryObjectTreeId}}
              }
            }
          {{/useExistingGallery}}
          {{#images}},
          {
            "methodId":"load",
            "parameters": {
              "title": {{&json ../title}},
              "images": [
                {{&json this}}
              ]
            }
          }{{/images}}
          ]`,
                icon: 'far fa-images',
            });
            const properties = objectNode.entityCtx.actions.methods[objectNode.entityCtx.actions.methods.length - 1].parameters.properties;
            if (1 < imageGalleries.length) {
                properties.parentId = {
                    type: 'string',
                    title: 'Galleries parent',
                    required: true,
                    oneOf: imageGalleries.map((gallery) => ({
                        enum: [gallery.id],
                        title: gallery.name,
                    })),
                };
            }
            if (((_e = (_d = (_c = (_b = objectNode.entityCtx) === null || _b === void 0 ? void 0 : _b.jsonSchema) === null || _c === void 0 ? void 0 : _c.properties.imageGalleryObjectTreeId) === null || _d === void 0 ? void 0 : _d.oneOf) === null || _e === void 0 ? void 0 : _e.length) > 0) {
                helper_1.addCondition('true !== model.useExistingGallery', properties.name);
                if (properties.parentId) {
                    helper_1.addCondition('true !== model.useExistingGallery', properties.parentId);
                }
                properties.imageGalleryObjectTreeId = lodash_1.cloneDeep((_g = (_f = objectNode.entityCtx) === null || _f === void 0 ? void 0 : _f.jsonSchema) === null || _g === void 0 ? void 0 : _g.properties.imageGalleryObjectTreeId);
                properties.imageGalleryObjectTreeId.required = true;
                helper_1.addCondition('true === model.useExistingGallery', properties.imageGalleryObjectTreeId);
            }
            else {
                delete properties.useExistingGallery;
            }
            properties.title = {
                type: 'string',
                title: 'Displayed name',
                'x-schema-form': {
                    type: 'conditional-text',
                    conditionalValue: {
                        title: 'Specify a label for images',
                        defaultValue: `return ${JSON.stringify(objectNode.title)}`,
                    },
                },
                default: objectNode.title,
            };
            helper_1.addCondition('(true === model.useExistingGallery && model.imageGalleryObjectTreeId) || !model.useExistingGallery', properties.title);
            properties.images = {
                type: 'array',
                'x-schema-form': {
                    type: 'images',
                },
                items: (await this.contentImageService.getContentDefinition())
                    .properties.contentImage,
            };
            helper_1.addCondition('(true === model.useExistingGallery && model.imageGalleryObjectTreeId) || !model.useExistingGallery', properties.images);
        }
    }
    async addLoadImagesJsonSchemaMethod(objectNode, ctx) {
        var _a;
        if (!objectNode.entityCtx) {
            objectNode.entityCtx = { entityType: entity_name_1.EntityName.objectNode };
        }
        if (!((_a = objectNode.entityCtx) === null || _a === void 0 ? void 0 : _a.actions)) {
            objectNode.entityCtx.actions = {};
        }
        if (!objectNode.entityCtx.actions.methods) {
            objectNode.entityCtx.actions.methods = [];
        }
        objectNode.entityCtx.actions.methods.push({
            methodId: 'load',
            methodName: 'Add images',
            actionName: 'Load images',
            parameters: {
                type: 'object',
                properties: {
                    title: {
                        type: 'string',
                        title: 'Displayed name',
                        'x-schema-form': {
                            type: 'conditional-text',
                            conditionalValue: {
                                title: 'Specify a label for images',
                                defaultValue: `return ${JSON.stringify(objectNode.title)}`,
                            },
                        },
                        default: objectNode.title,
                    },
                    images: {
                        type: 'array',
                        'x-schema-form': {
                            type: 'images',
                        },
                        items: (await this.contentImageService.getContentDefinition())
                            .properties.contentImage,
                    },
                },
            },
            handlebarsMethodSampling: `[{{#images}}
          {
            "methodId":"load",
            "parameters": {
              "title": {{&json ../title}},
              "images": [
                {{&json this}}
              ]
            }
          }{{#unless @last}},{{/unless}}{{/images}}
        ]`,
            icon: 'far fa-images',
        });
    }
    async completeImageGalleryReferrerNode(objectNode, ctx) {
        var _a, _b, _c, _d, _e;
        const images = [];
        if (!objectNode.imageGalleryTree && objectNode.imageGalleryObjectTreeUri) {
            try {
                objectNode.imageGalleryTree = (await this.insideRestService.read(objectNode.imageGalleryObjectTreeUri, ctx));
            }
            catch (error) { }
        }
        if ((_a = objectNode.imageGalleryTree) === null || _a === void 0 ? void 0 : _a.children) {
            for (const image of objectNode.imageGalleryTree.children) {
                images.push(image);
            }
        }
        objectNode.images = images;
        if (-1 <
            lodash_1.indexOf((_b = objectNode.entityCtx) === null || _b === void 0 ? void 0 : _b.implementedTypes, content_image_const_1.IMAGE_GALLERY_SELECTOR_TYPE.name) && ((_e = (_d = (_c = objectNode.entityCtx) === null || _c === void 0 ? void 0 : _c.jsonSchema) === null || _d === void 0 ? void 0 : _d.properties) === null || _e === void 0 ? void 0 : _e.selectedImages)) {
            if (0 < images.length) {
                objectNode.entityCtx.jsonSchema.properties.selectedImages.items.enum = [];
                objectNode.entityCtx.jsonSchema.properties.selectedImages.items.enumNames = [];
                for (const image of images) {
                    if (-1 ===
                        lodash_1.indexOf(objectNode.entityCtx.jsonSchema.properties.selectedImages.items
                            .enum, image.treeNode.name)) {
                        objectNode.entityCtx.jsonSchema.properties.selectedImages.items.enum.push(image.treeNode.name);
                        objectNode.entityCtx.jsonSchema.properties.selectedImages.items.enumNames.push(image.treeNode.name +
                            '<span class="imageSelect"><img onclick="showImg(\'' +
                            image.treeNode.contentImageUri +
                            '\')" src="' +
                            image.treeNode.contentImageUri +
                            '" ></span>');
                    }
                }
                if (objectNode.selectedImages) {
                    objectNode.images = lodash_1.filter(images, (image) => -1 < lodash_1.indexOf(objectNode.selectedImages, image.treeNode.name));
                }
                else {
                    objectNode.selectedImages =
                        objectNode.entityCtx.jsonSchema.properties.selectedImages.items.enum;
                }
                helper_1.addCondition("model.imageGalleryObjectTreeId=='" +
                    objectNode.imageGalleryObjectTreeId +
                    "'", objectNode.entityCtx.jsonSchema.properties.selectedImages);
            }
            else {
                delete objectNode.entityCtx.jsonSchema.properties.selectedImages;
            }
        }
        if (objectNode.imageGalleryTree) {
            await this.addLoadImagesJsonSchemaMethod(objectNode, ctx);
        }
        else {
            await this.addCreateImagesGalleryJsonSchemaMethod(objectNode, ctx);
        }
    }
};
TransientImageService = tslib_1.__decorate([
    tslib_1.__param(0, core_1.service(transient_entity_service_1.TransientEntityService)),
    tslib_1.__param(1, core_1.service(node_intercept_service_1.NodeInterceptService)),
    tslib_1.__param(2, core_1.service(inside_rest_service_1.InsideRestService)),
    tslib_1.__param(3, core_1.service(uri_complete_service_1.UriCompleteService)),
    tslib_1.__param(4, core_1.service(content_image_definition_1.ContentImageService)),
    tslib_1.__param(5, core_1.service(services_1.ObjectTreeService)),
    tslib_1.__param(6, core_1.service(object_node_service_1.ObjectNodeService)),
    tslib_1.__param(7, core_1.service(object_type_service_1.ObjectTypeService)),
    tslib_1.__metadata("design:paramtypes", [transient_entity_service_1.TransientEntityService,
        node_intercept_service_1.NodeInterceptService,
        inside_rest_service_1.InsideRestService,
        uri_complete_service_1.UriCompleteService,
        content_image_definition_1.ContentImageService,
        services_1.ObjectTreeService,
        object_node_service_1.ObjectNodeService,
        object_type_service_1.ObjectTypeService])
], TransientImageService);
exports.TransientImageService = TransientImageService;
//# sourceMappingURL=transient-image.service.js.map