Source: package/component/loadable_component.js

import { Reporter } from '../../reporter/reporter.js';
import { Component } from '../../component/component.js';
import { checkPropTypes, omit } from '../../lib.js';
import { LoadingBase } from '../loader/loading_base.js';

/**
 * Component without dependencies that needs
 * data be loaded from somewhere
 */


// file has low/med/high quality?
// kan een buildable component altijd de hoogste kwaliteit teruggeven?

class LoadableComponent extends Component {

    /**
     * @param {Reporter} reporter
     * @param {Object} settings
     * @param {UUID} [settings.id]
     * @param {string} [settings.name]
     * @param {QualitySourceMap} settings.source - Either one URL, or three (one for every quality setting)
     * @param {number} [settings.sizeBytes]
     */

    constructor(reporter, settings) {

        super(reporter, settings);

        if (!settings.source) {
            // console.log(settings)
            throw new Error('Missing source property');
        }

        if (!settings.source.medium) {
            throw new Error('Source should always specify a medium quality file');
        }

        const checkSource = src => {
            if ( typeof (src.path) !== 'string' || src.path === '' ) {
                return `Property path = ${src.path}, should be a non-empty string.`;
            }
            if( typeof (src.sizeBytes) !== 'number' ) {
                return `Property sizeBytes = ${src.sizeBytes}, should be a number.`;
            }
            return true;
        }

        // TODO: dynamisch maken adhv Component.qualities

        checkPropTypes(
            settings.source,
            {
                medium: checkSource
            },
            {
                // low: checkSource,
                // high: checkSource
            }
        );

        this.source = {
            'medium': settings.source.medium
        };

        this.source.high = settings.source.high || settings.source.medium;
        this.source.low = settings.source.low || settings.source.medium;

        
        
        // this.on( 'status-change', async ({ quality, part, status, previousStatus }) => {
    
            // if any of the other quality sources are equal to this one
            // copy the result
    
            // for (let setQuality of Component.qualities) {

            //     const currStatus = this._status[ part ][ setQuality ].state;

            //     if ( setQuality !== quality && status !== currStatus && this.source[setQuality].path === this.source[quality].path) {
    
            //         this.report({ msg: `Same source updating ${part} ${quality} (${status}) -> ${setQuality} (${currStatus})` });
    
            //         if (status === 'ready') {
            //             await this._setContent('main', setQuality, this._content[ part ][ quality ] );
            //         }
            //         else {
            //             await this._status.main[setQuality].setState(status);
            //         }
    
            //     }
            // }

        // });
    }


    /**
     * @type {QualitySourceMap}
     */

    source = {};

    /**
     * @protected
     * @interface
     * @method
     * @param {LoadingBase} base
     * @param {LoadingQuality} quality 
     * @returns {Promise<any>}
     */

    async _load(base, quality) {
        this.report({ level: 'notice', msg: 'Override LoadableComponent _load method!' });
        return Promise.reject();
    }


    /**
     * This method is not meant to be called directly.
     * Instead it is meant to be called by the ComponentLoader, which will respond to its 
     * error state changes, in case anything fails.
     * @interface
     * @method
     * @param {LoadingBase} base
     * @param {LoadingQuality} [quality = 'medium']
     * @returns {Promise<LoadableComponent>}
     */

    async load(base, quality = 'medium') {

        await this._status.main[quality].setState('loading');

        let loadErrorMessage = null;

        try {
            const content = await this._load(base, quality);

            await this._setContent('main', quality, content);

            return this;
        }
        catch (err) {

            // await this._status.main[quality].setState('error');

            if (err.message) {
                loadErrorMessage = `Load errored: ${err.message}`;
            }
            else {
                loadErrorMessage = `Unknown loading error.`;
            }

            this.report({ msg: loadErrorMessage, level: 'error' });

            console.error( `Error loading ${quality} quality ${this.label}`, err);

            this._setError('main', quality, new Error( loadErrorMessage ) );
        }

    }
}

export { LoadableComponent };