/** This file is part of Reactor.
 *  Copyright (c) 2020,2021 Kedron Holdings LLC, All Rights Reserved.
 *  Reactor is not public domain or open source. Distribution or derivative works are expressly prohibited.
 */

export const version = 24355;

import { mixin } from '/client/util.js';

import Observer from '/client/Observer.js';
import Observable from '/client/Observable.js';

import * as Common from './reactor-ui-common.js';

export default class EmbeddableEditor {
    constructor( data, $container, options ) {
        this.configModified = false;
        this.inEdit = false;
        this.options = options || {};
        this.original = data;
        this.data = Common.clone( this.original );
        this.$editor = $('<div class="re-embeddable-editor"></div>' )
            .data( 'embeddable-editor', this )
            .appendTo( $container );
    }

    /* Observer */
    toString() {
        throw new Error("Superclass must implement toString()");
    }

    editor() {
        return this.$editor;
    }

    /** Return the current object as displayed */
    current() {
        return this.data;
    }

    /** Start editing */
    async edit() {
        if ( ! this.inEdit ) {
            this.inEdit = true;
            await this.refresh();
            this.$editor.trigger( 'ready', { editor: this } );
        }
    }

    /** Reset the editor, optionally with new data and options. The current editing session is removed with no
     *  flourish (notice of closure, etc), and no regard for the current modified state of the data. All of that
     *  is left to the container to handle.
     */
    reset( d, options ) {
        this.inEdit = false;
        this.configModified = false;
        if ( d ) {
            this.original = d;
        }
        this.data = Common.clone( this.original );
        if ( options ) {
            this.options = options;
        }
    }

    async refresh() {
        await this.redraw();
        this.updateSaveControls();
    }

    /** Notify the editor that the object has been saved. Typically, the edit
     *  version of the object then becomes the original (so later edit and
     *  revert restores to this point).
     */
    notifySaved() {
        this.original = this.data;
        this.data = Common.clone( this.original );
        this.configModified = false;
        let msg = { modified: false, errors: !this.canSave(), action: 'save', editor: this };
        this.$editor.trigger( 'modified', msg );
        this.publish( msg, 'modified' );
    }

    /** Called by container to revert data to original, unedited version and
     *  restore display to then-current data.
     */
    revert() {
        this.data = Common.clone( this.original );
        this.configModified = false;
        this.refresh();
    }

    /** Return true if the edit data is suitable for saving (no validation errs) */
    canSave() {
        return 0 === this.$editor.find(".tberror").length;
    }

    /** Return true if the edit data has been modified from original */
    isModified() {
        return this.configModified;
    }

    isEmpty() {
        throw new Error( "Subclass does not implement isEmpty()" );
    }

    isEditing() {
        return this.inEdit;
    }

    /**
     * Close the editor and restore the default view
     */
    close() {
        /* Close the editor and restore the view */
        if ( this.$editor ) {
            this.$editor.trigger( 'close' ).remove();
            this.publish( this, 'closed' );
        }
        this.unsubscribe();
        this.disconnect();
        this.inEdit = false;
        this.$editor = false;
        // this.original = false;
        // this.data = false;
        // this.configModified = false;
    }

    /* Observer */
    notify() {
        console.warn("EmbeddableEditor base class received unhandled notification");
    }

    updateSaveControls() {
        let msg = { modified: this.configModified, errors: !this.canSave(), editor: this };
        this.$editor.trigger( 'update-controls', msg );
        this.publish( msg, 'update' );
    }

    signalModified() {
        let msg = { modified: true, errors: !this.canSave(), action: 'edit', editor: this };
        this.configModified = true;
        this.$editor.trigger( 'modified', msg );
        this.publish( msg, 'modified' );
    }
}

mixin( EmbeddableEditor, Observer );
mixin( EmbeddableEditor, Observable );
