import util from '../core/util';
import Task from './Task';

/**
 * @constructor
 * @alias clay.async.TaskGroup
 * @extends clay.async.Task
 */
var TaskGroup = function () {

    Task.apply(this, arguments);

    this._tasks = [];

    this._fulfilledNumber = 0;

    this._rejectedNumber = 0;
};

var Ctor = function (){};
Ctor.prototype = Task.prototype;
TaskGroup.prototype = new Ctor();

TaskGroup.prototype.constructor = TaskGroup;

/**
 * Wait for all given tasks successed, task can also be any notifier object which will trigger success and error events. Like {@link clay.Texture2D}, {@link clay.TextureCube}, {@link clay.loader.GLTF}.
 * @param  {Array.<clay.async.Task>} tasks
 * @chainable
 * @example
 *     // Load texture list
 *     var list = ['a.jpg', 'b.jpg', 'c.jpg']
 *     var textures = list.map(function (src) {
 *         var texture = new clay.Texture2D();
 *         texture.load(src);
 *         return texture;
 *     });
 *     var taskGroup = new clay.async.TaskGroup();
 *     taskGroup.all(textures).success(function () {
 *         // Do some thing after all textures loaded
 *     });
 */
TaskGroup.prototype.all = function (tasks) {
    var count = 0;
    var self = this;
    var data = [];
    this._tasks = tasks;
    this._fulfilledNumber = 0;
    this._rejectedNumber = 0;

    util.each(tasks, function (task, idx) {
        if (!task || !task.once) {
            return;
        }
        count++;
        task.once('success', function (res) {
            count--;

            self._fulfilledNumber++;
            // TODO
            // Some tasks like texture, loader are not inherited from task
            // We need to set the states here
            task._fulfilled = true;
            task._rejected = false;

            data[idx] = res;
            if (count === 0) {
                self.resolve(data);
            }
        });
        task.once('error', function () {

            self._rejectedNumber ++;

            task._fulfilled = false;
            task._rejected = true;

            self.reject(task);
        });
    });
    if (count === 0) {
        setTimeout(function () {
            self.resolve(data);
        });
        return this;
    }
    return this;
};
/**
 * Wait for all given tasks finished, either successed or failed
 * @param  {Array.<clay.async.Task>} tasks
 * @return {clay.async.TaskGroup}
 */
TaskGroup.prototype.allSettled = function (tasks) {
    var count = 0;
    var self = this;
    var data = [];
    if (tasks.length === 0) {
        setTimeout(function () {
            self.trigger('success', data);
        });
        return this;
    }
    this._tasks = tasks;

    util.each(tasks, function (task, idx) {
        if (!task || !task.once) {
            return;
        }
        count++;
        task.once('success', function (res) {
            count--;

            self._fulfilledNumber++;

            task._fulfilled = true;
            task._rejected = false;

            data[idx] = res;
            if (count === 0) {
                self.resolve(data);
            }
        });
        task.once('error', function (err) {
            count--;

            self._rejectedNumber++;

            task._fulfilled = false;
            task._rejected = true;

            // TODO
            data[idx] = null;
            if (count === 0) {
                self.resolve(data);
            }
        });
    });
    return this;
};
/**
 * Get successed sub tasks number, recursive can be true if sub task is also a TaskGroup.
 * @param  {boolean} [recursive]
 * @return {number}
 */
TaskGroup.prototype.getFulfilledNumber = function (recursive) {
    if (recursive) {
        var nFulfilled = 0;
        for (var i = 0; i < this._tasks.length; i++) {
            var task = this._tasks[i];
            if (task instanceof TaskGroup) {
                nFulfilled += task.getFulfilledNumber(recursive);
            } else if(task._fulfilled) {
                nFulfilled += 1;
            }
        }
        return nFulfilled;
    } else {
        return this._fulfilledNumber;
    }
};

/**
 * Get failed sub tasks number, recursive can be true if sub task is also a TaskGroup.
 * @param  {boolean} [recursive]
 * @return {number}
 */
TaskGroup.prototype.getRejectedNumber = function (recursive) {
    if (recursive) {
        var nRejected = 0;
        for (var i = 0; i < this._tasks.length; i++) {
            var task = this._tasks[i];
            if (task instanceof TaskGroup) {
                nRejected += task.getRejectedNumber(recursive);
            } else if(task._rejected) {
                nRejected += 1;
            }
        }
        return nRejected;
    } else {
        return this._rejectedNumber;
    }
};

/**
 * Get finished sub tasks number, recursive can be true if sub task is also a TaskGroup.
 * @param  {boolean} [recursive]
 * @return {number}
 */
TaskGroup.prototype.getSettledNumber = function (recursive) {

    if (recursive) {
        var nSettled = 0;
        for (var i = 0; i < this._tasks.length; i++) {
            var task = this._tasks[i];
            if (task instanceof TaskGroup) {
                nSettled += task.getSettledNumber(recursive);
            } else if(task._rejected || task._fulfilled) {
                nSettled += 1;
            }
        }
        return nSettled;
    } else {
        return this._fulfilledNumber + this._rejectedNumber;
    }
};

/**
 * Get all sub tasks number, recursive can be true if sub task is also a TaskGroup.
 * @param  {boolean} [recursive]
 * @return {number}
 */
TaskGroup.prototype.getTaskNumber = function (recursive) {
    if (recursive) {
        var nTask = 0;
        for (var i = 0; i < this._tasks.length; i++) {
            var task = this._tasks[i];
            if (task instanceof TaskGroup) {
                nTask += task.getTaskNumber(recursive);
            } else {
                nTask += 1;
            }
        }
        return nTask;
    } else {
        return this._tasks.length;
    }
};

export default TaskGroup;
