# core/MinimalStim.js

    /**
     * Base class for all stimuli.
     *
     * @author Alain Pitiot
     * @version 2022.2.0
     * @copyright (c) 2017-2020 Ilixa Ltd. (http://ilixa.com) (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org)
     * @license Distributed under the terms of the MIT License
     */

    import { PsychObject } from "../util/PsychObject.js";
    import * as util from "../util/Util.js";
    import { PsychoJS } from "./PsychoJS.js";

    /**
     * <p>MinimalStim is the base class for all stimuli.</p>
     *
     * @extends PsychObject
     */
    export class MinimalStim extends PsychObject
    {
    	/**
    	 * @memberof module:core
    	 * @param {Object} options
    	 * @param {String} options.name - the name used when logging messages from this stimulus
    	 * @param {module:core.Window} options.win - the associated Window
    	 * @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
    	 * @param {boolean} [options.autoLog= win.autoLog] - whether to log
    	 */
    	constructor({ name, win, autoDraw, autoLog } = {})
    	{
    		super(win._psychoJS, name);

    		// the PIXI representation of the stimulus:
    		this._pixi = undefined;

    		this._addAttribute(
    			"win",
    			win,
    			undefined,
    		);
    		this._addAttribute(
    			"autoDraw",
    			autoDraw,
    			false,
    		);
    		this._addAttribute(
    			"autoLog",
    			autoLog,
    			(typeof win !== "undefined" && win !== null) ? win.autoLog : false,
    		);

    		this._needUpdate = false;
    		this.status = PsychoJS.Status.NOT_STARTED;
    	}

    	/**
    	 * Setter for the autoDraw attribute.
    	 *
    	 * @param {boolean} autoDraw - the new value
    	 * @param {boolean} [log= false] - whether to log
    	 */
    	setAutoDraw(autoDraw, log = false)
    	{
    		this._setAttribute("autoDraw", autoDraw, log);

    		// autoDraw = true: add the stimulus to the draw list if it's not there already
    		if (this._autoDraw)
    		{
    			this.draw();
    		}
    		// autoDraw = false: remove the stimulus from the draw list (and from the root container if it's already there)
    		else
    		{
    			this.hide();
    		}
    	}

    	/**
    	 * Draw this stimulus on the next frame draw.
    	 */
    	draw()
    	{
    		if (this.win)
    		{
    			const index = this._win._drawList.indexOf(this);

    			// if the stimulus is not already in the draw list:
    			if (index < 0)
    			{
    				// update the stimulus if need be before we add its PIXI representation to the window container:
    				this._updateIfNeeded();
    				if (typeof this._pixi === "undefined")
    				{
    					this.psychoJS.logger.warn("the Pixi.js representation of this stimulus is undefined.");
    				}
    				else
    				{
    					this._win.addPixiObject(this._pixi);
    					this.win._drawList.push(this);
    				}
    			}
    			else
    			{
    				// the stimulus is already in the list, if it needs to be updated, we remove it
    				// from the window container, update it, then put it back:
    				if (this._needUpdate && typeof this._pixi !== "undefined")
    				{
    					this._win.removePixiObject(this._pixi);
    					this._updateIfNeeded();
    					this._win.addPixiObject(this._pixi);
    				}
    			}
    		}

    		this.status = PsychoJS.Status.STARTED;
    	}

    	/**
    	 * Hide this stimulus on the next frame draw.
    	 */
    	hide()
    	{
    		if (this._win)
    		{
    			const index = this._win._drawList.indexOf(this);
    			if (index >= 0)
    			{
    				this._win._drawList.splice(index, 1);

    				// if the stimulus has a pixi representation, remove it from the root container:
    				if (typeof this._pixi !== "undefined")
    				{
    					this._win.removePixiObject(this._pixi);
    				}
    			}
    			this.status = PsychoJS.Status.STOPPED;
    		}
    	}

    	/**
    	 * Determine whether an object is inside this stimulus.
    	 *
    	 * @abstract
    	 * @param {Object} object - the object
    	 * @param {String} units - the stimulus units
    	 */
    	contains(object, units)
    	{
    		throw {
    			origin: "MinimalStim.contains",
    			context: `when determining whether stimulus: ${this._name} contains object: ${util.toString(object)}`,
    			error: "this method is abstract and should not be called.",
    		};
    	}

    	/**
    	 * Release the PIXI representation, if there is one.
    	 *
    	 * @param {boolean} [log= false] - whether to log
    	 */
    	release(log = false)
    	{
    		this._setAttribute("autoDraw", false, log);
    		this.status = PsychoJS.Status.STOPPED;

    		if (typeof this._pixi !== "undefined")
    		{
    			this._pixi.destroy(true);
    			this._pixi = undefined;
    		}
    	}

    	/**
    	 * Update the stimulus, if necessary.
    	 *
    	 * Note: this is an abstract function, which should not be called.
    	 *
    	 * @abstract
    	 * @protected
    	 */
    	_updateIfNeeded()
    	{
    		throw {
    			origin: "MinimalStim._updateIfNeeded",
    			context: "when updating stimulus: " + this._name,
    			error: "this method is abstract and should not be called.",
    		};
    	}
    }