# JSDoc: Source: entities/note_revision.js

# Source: entities/note_revision.js

    "use strict";

    const Entity = require('./entity');
    const protectedSessionService = require('../services/protected_session');
    const utils = require('../services/utils');
    const sql = require('../services/sql');
    const dateUtils = require('../services/date_utils');
    const entityChangesService = require('../services/entity_changes.js');

    /**
     * NoteRevision represents snapshot of note's title and content at some point in the past. It's used for seamless note versioning.
     *
     * @property {string} noteRevisionId
     * @property {string} noteId
     * @property {string} type
     * @property {string} mime
     * @property {string} title
     * @property {boolean} isProtected
     * @property {string} dateLastEdited
     * @property {string} dateCreated
     * @property {string} utcDateLastEdited
     * @property {string} utcDateCreated
     * @property {string} utcDateModified
     *
     * @extends Entity
     */
    class NoteRevision extends Entity {
        static get entityName() { return "note_revisions"; }
        static get primaryKeyName() { return "noteRevisionId"; }
        static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "isProtected", "dateLastEdited", "dateCreated", "utcDateLastEdited", "utcDateCreated", "utcDateModified"]; }

        constructor(row) {
            super(row);

            this.isProtected = !!this.isProtected;

            if (this.isProtected) {
                if (protectedSessionService.isProtectedSessionAvailable()) {
                    this.title = protectedSessionService.decryptString(this.title);
                }
                else {
                    this.title = "[Protected]";
                }
            }
        }

        getNote() {
            return this.repository.getNote(this.noteId);
        }

        /** @returns {boolean} true if the note has string content (not binary) */
        isStringNote() {
            return utils.isStringNote(this.type, this.mime);
        }

        /*
         * Note revision content has quite special handling - it's not a separate entity, but a lazily loaded
         * part of NoteRevision entity with it's own sync. Reason behind this hybrid design is that
         * content can be quite large and it's not necessary to load it / fill memory for any note access even
         * if we don't need a content, especially for bulk operations like search.
         *
         * This is the same approach as is used for Note's content.
         */

        /** @returns {*} */
        getContent(silentNotFoundError = false) {
            if (this.content === undefined) {
                const res = sql.getRow(`SELECT content FROM note_revision_contents WHERE noteRevisionId = ?`, [this.noteRevisionId]);

                if (!res) {
                    if (silentNotFoundError) {
                        return undefined;
                    }
                    else {
                        throw new Error("Cannot find note revision content for noteRevisionId=" + this.noteRevisionId);
                    }
                }

                this.content = res.content;
                if (this.isProtected) {
                    if (protectedSessionService.isProtectedSessionAvailable()) {
                        this.content = protectedSessionService.decrypt(this.content);
                    }
                    else {
                        this.content = "";
                    }
                }
            }

            if (this.isStringNote()) {
                return this.content === null
                    ? ""
                    : this.content.toString("UTF-8");
            }
            else {
                return this.content;
            }
        }

        setContent(content) {
            this.content = content;

            const pojo = {
                noteRevisionId: this.noteRevisionId,
                content: content,
                utcDateModified: dateUtils.utcNowDateTime()
            };

            if (this.isProtected) {
                if (protectedSessionService.isProtectedSessionAvailable()) {
                    pojo.content = protectedSessionService.encrypt(pojo.content);
                }
                else {
                    throw new Error(`Cannot update content of noteRevisionId=${this.noteRevisionId} since we're out of protected session.`);
                }
            }

            sql.upsert("note_revision_contents", "noteRevisionId", pojo);

            const hash = utils.hash(this.noteRevisionId + "|" + content);

            entityChangesService.addEntityChange({
                entityName: 'note_revision_contents',
                entityId: this.noteRevisionId,
                hash: hash,
                isErased: false,
                utcDateChanged: this.getUtcDateChanged()
            }, null);
        }

        beforeSaving() {
            super.beforeSaving();

            this.utcDateModified = dateUtils.utcNowDateTime();
        }

        // cannot be static!
        updatePojo(pojo) {
            if (pojo.isProtected) {
                if (protectedSessionService.isProtectedSessionAvailable()) {
                    pojo.title = protectedSessionService.encrypt(pojo.title);
                }
                else {
                    // updating protected note outside of protected session means we will keep original ciphertexts
                    delete pojo.title;
                }
            }

            delete pojo.content;
        }
    }

    module.exports = NoteRevision;