lang/View.js

const { _ } = require('@kitmi/utils');
const { generateDisplayName, deepCloneField, Clonable } = require('./XemlUtils');

const Dataset = require('./Dataset');

/**
 * Geml view class.
 * @class View
 */
class View extends Clonable {

    isList = false;

    /**          
     * @param {Linker} linker
     * @param {string} name - View name
     * @param {object} xemlModule - Source ool module
     * @param {object} info - View info
     */
    constructor(linker, name, xemlModule, info) {
        super();

        /**
         * Linker to process this view
         * @member {Linker}
         */
        this.linker = linker;

        /**
         * Name of this view
         * @member {string}
         */
        this.name = name;

        /**
         * Owner geml module
         * @member {object}
         */
        this.xemlModule = xemlModule;

        /**
         * Raw metadata
         * @member {Object}
         */
        this.info = info;        
    }

    /**
     * Start linking this view
     * @returns {View}
     */
    link() {
        pre: !this.linked;

        if (this.info.dataset) {
            this.dataset = this.linker.loadDoc(this.xemlModule, this.info.dataset);
        } else {
            assert: this.info.entity, 'Invalid view syntax!';
            
            let mainEntity = this.linker.getReferencedEntity(this.xemlModule, this.info.entity);
            
            this.dataset = new Dataset(this.linker, mainEntity.name, this.xemlModule, { mainEntity: mainEntity.name });
            this.dataset.link();
        }

        if (this.info.isList) {
            this.isList = true;
        }

        if (!_.isEmpty(this.info.accept)) {
            this.params = this.info.accept.concat();
        }

        if (!_.isEmpty(this.info.selectBy)) {
            this.selectBy = this.info.selectBy.concat();
        }

        if (!_.isEmpty(this.info.groupBy)) {
            this.groupBy = this.info.groupBy.concat();
        }

        if (!_.isEmpty(this.info.orderBy)) {
            this.orderBy = this.info.orderBy.concat();
        }

        if (this.info.skip) {
            this.skip = _.isPlainObject(this.info.skip) ? Object.assign({}, this.info.skip) : this.info.skip;
        }

        if (this.info.limit) {
            this.limit = _.isPlainObject(this.info.limit) ? Object.assign({}, this.info.limit) : this.info.limit;
        }

        this.linked = true;

        return this;
    }

    inferTypeInfo(inSchema) {
        if (!_.isEmpty(this.params)) {
            let inferredParams = [];

            this.params.forEach(param => {
                if (GemlUtils.isMemberAccess(param.type)) {
                    let [ entityName, fieldName ] = param.type.split('.');

                    if (!inSchema.hasEntity(entityName)) {
                        throw new Error(`Parameter "${param.name}" references to an entity "${entityName}" which is not belong to the schema.`);
                    }

                    let entity = inSchema.entities[entityName];
                    //console.dir(entity.toJSON(), {depth: 8, colors: true});

                    let field = entity.getEntityAttribute(fieldName);
                    inferredParams.push(Object.assign(_.omit(_.toPlainObject(field), ['isReference', 'optional', 'displayName']), {name: param.name}));
                } else {
                    const [ typeInfo, baseInfo ] = this.linker.trackBackType(this.xemlModule, param);
                    inferredParams.push(typeInfo);
                }
            });

            this.params = inferredParams;
        }
    }

    getDocumentHierarchy(inSchema) {
        return inSchema.getDocumentHierachy(this.xemlModule, this.dataset.name);
    }

    /**
     * Clone the view     
     * @returns {View}
     */
    clone() {
        super.clone();
        
        let view = new View(this.linker, this.name, this.xemlModule, this.info);

        deepCloneField(this, view, 'dataset');
        deepCloneField(this, view, 'params');
        deepCloneField(this, view, 'selectBy');
        deepCloneField(this, view, 'groupBy');
        deepCloneField(this, view, 'orderBy');
        deepCloneField(this, view, 'skip');
        deepCloneField(this, view, 'limit');

        view.isList = this.isList;
        view.linked = true;

        return view;
    }
    
    /**
     * Translate the view into a plain JSON object
     * @returns {object}
     */
    toJSON() {
        return {            
            name: this.name,
            dataset: this.dataset.toJSON(),
            isList: this.isList,
            params: this.params,
            selectBy: this.selectBy,
            groupBy: this.groupBy,
            orderBy: this.orderBy,
            skip: this.skip,
            limit: this.limit
        };
    }
}

module.exports = View;