export class ModelMetadataCollection<T extends { target?: Function | string }> {

    protected items: T[] = [];

    /**
     *
     */
    get length() {
        return this.items.length;
    }

    /**
     *
     */
    filter(callbackFunction: (value: T, index: number, array: T[]) => boolean, thisArg?: any): this {
        const collection = new (<any>this.constructor)();
        this.items.filter(callbackFunction).forEach(metadata => collection.add(metadata));
        return collection;
    }

    /**
     *
     */
    filterByTarget(singleClass?: Function | string) {
        if (!singleClass) {
            return new (<any>this.constructor)();
        }

        return this.filterByTargets([singleClass]);
    }

    /**
     *
     */
    filterByTargets(classes: Array<Function|string>): this {
        return this.filter(metadata => { 
            if (!metadata.target) {
                return false;
            }
            
            return classes.indexOf(metadata.target) !== -1;
        });
    }

    /**
     *
     */
    add(metadata: T, checkDuplicateTargets = false): void {
        if (checkDuplicateTargets) {
            if (!metadata.target || !(metadata.target instanceof Function)) {
                throw new Error("No existe éste target");
            }

            if (this.hasWithTarget(metadata.target)) {
                throw new Error("Éste target ya existe");
            }
        }

        this.items.push(metadata);
    }
    /**
     *
     */
    toArray(): Array<T>  {
        return this.items.map(item => item);
    }

    /**
     *
     */
    private hasWithTarget(constructor: Function): boolean {
        return !!this.items.find(metadata => metadata.target === constructor);
    }
}