# src/IndexedProperty.js

    import { assert } from "akutils";

    import createPropertyClass from "./createPropertyClass";
    import IndexedApi from "./IndexedApi";
    import IndexedShadow from "./IndexedShadow";
    import IndexedShadowImpl from "./IndexedShadowImpl";
    import MapProperty from "./MapProperty";
    import Property from "./Property";
    import PropertyFactoryShader from "./PropertyFactoryShader";
    import StateType from "./StateType";

    /**
    	`IndexedProperty` models an [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)
    	while limiting the shadow API to the non-mutating `Array` methods, such as `filter()`, `find()`, `indexOf()`,
    	and `map()` to name a few. The {@link IndexedShadow} allows for random access and assignment, such as
    	`todos[1].desc`.

    	The default shadow class is {@link IndexedShadow}.

    	Use `IndexedProperty` when you want to model an array state property without exposing mutating functions
    	like `push()`, `remove()`, and `splice()`. You can then specify a custom shadow api to expose mutation
    	methods that involve implementation logic.

    	The `_indexed` instance variable exposes a {@link IndexedApi} instance for working with properties.
    	Use `this._indexed` to perform `array` mutations, such as `pop()`, `push()`, `shift()`, and `splice()`.
    	For example,

    	```js
    	this._indexed.push("new element");
    	```
    	would append "new element" to the array.

    	@see {@link IndexedShadow}
    */
    export default class IndexedProperty extends Property {
    	constructor(stateType) {
    		super(stateType);

    		this._indexed = new IndexedApi(this);

    		this.setImplementationClass(IndexedShadowImpl);
    		this.setShadowClass(IndexedShadow);
    	}

    	/**
    		Factory function for creating an `IndexedProperty` subclass. The generated class will have
    		the `type` {@link StateType} descriptor set upon return.

    		Example usage:
    		```
    		class SomeShadow extends IndexedShadow {
    		    // definition here
    		}

    		export default IndexedProperty.createClass(SomeShadow, type => {
    			// configure type variable
    		});
    		```

    		@param {Object|IndexedShadow} [shadowType={}] - `IndexedShadow` subclass or object literal api definition.
    			If object literal specified, each property and function is mapped onto a Shadow subclass.
    		@param {function(type: StateType)} [typeCallback] - a callback function that will be passed the
    			{@link StateType} spec for additional customization, such as setting autoshadow, initial state,
    			or readonly.
    		@param {Object} [initialState]=[] - the initial state for the new property.

    		@return {IndexedProperty} newly defined `IndexedProperty` subclass.
    	*/
    	static createClass(shadowType={}, typeCallback, initialState=[]) {
    		return createPropertyClass(shadowType, initialState, typeCallback, IndexedProperty, IndexedShadow);
    	}

    	/**
    		Factory function for setting up the {@link StateType} `type` class variable with an appropriately
    		configured intial state.

    		Example usage:
    		```
            export default class SomeProperty extends IndexedProperty {
    			// implement property here
            }

            class SomeShadow extends IndexedShadow {
    			// implement shadow api here
            }

            MapProperty.defineType(SomeProperty, SomeShadow, type => {
    			// configure type variable
            });
    		```

    		@param {IndexedProperty} PropClass - IndexedProperty subclass
    		@param {Object|IndexedShadow} [ShadowType] - `IndexedShadow` subclass or object literal api definition.
    			If object literal specified, each property and function is mapped onto a Shadow subclass.
    		@param {function(type: StateType)} [typeCallback] - a callback function that will be passed the
    			{@link StateType} spec for additional customization, such as setting autoshadow, initial state, or
    			readonly.
    		@param {Object} [initialState=[]] - the initial state for the new property.
    	*/
    	static defineType(PropClass, ShadowType, typeCallback, initialState=[]) {
    		assert( a => a.is(IndexedProperty.isPrototypeOf(PropClass), "PropClass must subclass IndexedProperty") );

    		StateType.defineTypeEx(PropClass, ShadowType, typeCallback, initialState);
    	}
    }

    StateType.defineType(IndexedProperty, spec => {
    	spec.initialState([])
    		.elementType(MapProperty.type)
    		.typeName("IndexedProperty");
    });