define(['jquery', 'underscore', 'jquery.ajaxQueue'], function($) {
    'use strict';
    return (function() {
        var Storage = {};

    /**
     * Adds some data to the Storage object. If data with existent `data_id`
     * is added, nothing happens.
     * @function
     * @param {String} data_id Unique identifier for the data.
     * @param {Any} data Data that should be stored.
     * @return {Object} Object itself for chaining.
     */
        Storage.set = function(data_id, data) {
            Storage[data_id] = data;
            return this;
        };

    /**
     * Return data from the Storage object by identifier.
     * @function
     * @param {String} data_id Unique identifier of the data.
     * @return {Any} Stored data.
     */
        Storage.get = function(data_id) {
            return Storage[data_id];
        };

    /**
     * Deletes data from the Storage object by identifier.
     * @function
     * @param {String} data_id Unique identifier of the data.
     * @return {Boolean} Boolean value that indicate if data is removed.
     */
        Storage.remove = function(data_id) {
            return (delete Storage[data_id]);
        };

    /**
     * Returns model from collection by 'field_name' property.
     * @function
     * @param {Object} collection The model (CMS.Models.Metadata) information
     * about metadata setting editors.
     * @param {String} field_name Name of field that should be found.
     * @return {
     *    Object:        When model exist.
     *    Undefined:     When model doesn't exist.
     * }
     */
        var _getField = function(collection, field_name) {
            var model;

            if (collection && field_name) {
                model = collection.findWhere({
                    field_name: field_name
                });
            }

            return model;
        };

    /**
     * Parses Youtube link and return video id.
     * @function
     * These are the types of URLs supported:
     * http://www.youtube.com/watch?v=OEoXaMPEzfM&feature=feedrec_grec_index
     * http://www.youtube.com/user/IngridMichaelsonVEVO#p/a/u/1/OEoXaMPEzfM
     * http://www.youtube.com/v/OEoXaMPEzfM?fs=1&amp;hl=en_US&amp;rel=0
     * http://www.youtube.com/watch?v=OEoXaMPEzfM#t=0m10s
     * http://www.youtube.com/embed/OEoXaMPEzfM?rel=0
     * http://www.youtube.com/watch?v=OEoXaMPEzfM
     * http://youtu.be/OEoXaMPEzfM
     * @param {String} url Url that should be parsed.
     * @return {
     *    String:        Video Id.
     *    Undefined:     When url has incorrect format or argument is
     *                   non-string, video id's length is not equal 11.
     * }
     */
        var _youtubeParser = (function() {
            var cache = {},
                regExp = /(?:http|https|)(?:\:\/\/|)(?:www.|)(?:youtu\.be\/|youtube\.com(?:\/embed\/|\/v\/|\/watch\?v=|\/ytscreeningroom\?v=|\/feeds\/api\/videos\/|\/user\S*[^\w\-\s]|\S*[^\w\-\s]))([\w\-]+)/i;

            return function(url) {
                if (typeof url !== 'string') {
                    return void(0);
                }

                if (cache[url]) {
                    return cache[url];
                }

                var match = url.match(regExp);
                cache[url] = (match) ? match[1] : void(0);

                return cache[url];
            };
        }());

    /**
     * Parses links with html5 video sources in mp4 or webm formats.
     * @function
     * @param {String} url Url that should be parsed.
     * @return {
     *    Object:        Object with information about the video
     *                   (file name, video type),
     *    Undefined:     when url has incorrect format or argument is
     *                   non-string.
     * }
     */
        var _videoLinkParser = (function() {
            var cache = {};

            return function(url) {
                if (typeof url !== 'string') {
                    return void(0);
                }

                if (cache[url]) {
                    return cache[url];
                }

                var link = document.createElement('a'),
                    match;

                link.href = url;
            // The regular expression try catches file name and file extension.
            // '[scheme://hostname/pathname/]filename.extension[?query#hash]'
                match = link.pathname.match(/\/{1}([^\/]+)\.([^\/]+)$/);
                if (match) {
                    cache[url] = {
                        video: match[1],
                        type: match[2]
                    };
                } else {
                // Links like http://goo.gl/pxxZrg
                // The regular expression try catches file name.
                // '[scheme://hostname/pathname/]filename[?query#hash]'
                    match = link.pathname.match(/\/{1}([^\/\.]+)$/);
                    if (match) {
                        cache[url] = {
                            video: match[1],
                            type: 'other'
                        };
                    }
                }

                return cache[url];
            };
        }());

    /**
     * Facade function that parses html5 and youtube links.
     * @function
     * @param {String} url Url that should be parsed.
     * @return {
     *    object:        Object with information about the video:
     *                   {
     *                       mode: "youtube|html5|incorrect",
     *                       video: "file_name|youtube_id",
     *                       type: "youtube|mp4|webm|other"
     *                   },
     *    undefined:     when argument is non-string.
     * }
     */
        var _linkParser = function(url) {
            var youtubeIdLength = 11,
                result;

            if (typeof url !== 'string') {
                return void(0);
            }

            if (_youtubeParser(url)) {
                if (_youtubeParser(url).length === youtubeIdLength) {
                    result = {
                        mode: 'youtube',
                        video: _youtubeParser(url),
                        type: 'youtube'
                    };
                } else {
                    result = {
                        mode: 'incorrect'
                    };
                }
            } else if (_videoLinkParser(url)) {
                result = $.extend({mode: 'html5'}, _videoLinkParser(url));
            } else {
                result = {
                    mode: 'incorrect'
                };
            }

            return result;
        };

    /**
     * Returns short-hand youtube url.
     * @function
     * @param {String} video_id Youtube Video Id that will be added to the link.
     * @return {String} Short-hand Youtube url.
     * @examples
     * _getYoutubeLink('OEoXaMPEzfM'); => 'http://youtu.be/OEoXaMPEzfM'
     */
        var _getYoutubeLink = function(video_id) {
            return 'http://youtu.be/' + video_id;
        };

    /**
     * Returns list of objects with information about the passed links.
     * @function
     * @param {Array} links List of links that will be processed.
     * @returns {Array} List of objects.
     * @examples
     * var links = [
     *      'http://youtu.be/OEoXaMPEzfM',
     *      'video_name.mp4',
     *      'video_name.webm'
     * ]
     *
     * _getVideoList(links); // =>
     *     [
     *       {mode: `youtube`, type: `youtube`, ...},
     *       {mode: `html5`, type: `mp4`, ...},
     *       {mode: `html5`, type: `webm`, ...}
     *     ]
     */
        var _getVideoList = function(links) {
            if ($.isArray(links)) {
                var arr = [],
                    data;

                for (var i = 0, len = links.length; i < len; i += 1) {
                    data = _linkParser(links[i]);

                    if (data.mode !== 'incorrect') {
                        arr.push(data);
                    }
                }

                return arr;
            }
        };

    /**
     * Synchronizes 2 Backbone collections by 'field_name' property.
     * @function
     * @param {Object} fromCollection Collection with which synchronization will
     * happens.
     * @param {Object} toCollection Collection which will synchronized.
     */
        var _syncCollections = function(fromCollection, toCollection) {
            fromCollection.each(function(m) {
                var model = toCollection.findWhere({
                    field_name: m.getFieldName()
                });

                if (model) {
                    model.setValue(m.getDisplayValue());
                }
            });
        };

    /**
     * Sends Ajax requests in appropriate format.
     * @function
     * @param {String} action Action that will be invoked on server.
     * @param {String} component_locator the locator of component.
     * @param {Array} videoList List of object with information about inserted
     * urls.
     * @param {Object} extraParams Extra parameters that can be send to the
     * server.
     * @return {Object} XMLHttpRequest object. Using this object, we can
     * attach callbacks to AJAX request events (for example on 'done',
     * 'fail', etc.).
     */
        var _command = (function() {
        // We will store the XMLHttpRequest object that $.ajax() function
        // returns, to abort an ongoing AJAX request (if necessary) upon
        // subsequent invocations of _command() function.
        //
        // A new AJAX request will be made on each invocation of the
        // _command() function.
            var xhr = null;

            return function(action, locator, videoList, extraParams) {
                var params, data;

                if (extraParams) {
                    if ($.isPlainObject(extraParams)) {
                        params = extraParams;
                    } else {
                        params = {params: extraParams};
                    }
                }

                data = $.extend(
                {locator: locator},
                {videos: videoList},
                params
            );

                xhr = $.ajaxQueue({
                    url: '/transcripts/' + action,
                    data: {data: JSON.stringify(data)},
                    notifyOnError: false,
                    type: 'get'
                });

                return xhr;
            };
        }());

        return {
            getField: _getField,
            parseYoutubeLink: _youtubeParser,
            parseHTML5Link: _videoLinkParser,
            parseLink: _linkParser,
            getYoutubeLink: _getYoutubeLink,
            syncCollections: _syncCollections,
            command: _command,
            getVideoList: _getVideoList,
            Storage: {
                set: Storage.set,
                get: Storage.get,
                remove: Storage.remove
            }
        };
    }());
});
