// Shows n loading dots while images are preloaded.
var loadSplash = (function()
{
    // Constants

    var DOT_COUNT = 3;
    var FADE_STEPS = 10;
    var TIMER_INTERVAL = 50;
    var MIN_OPACITY = 0.2;


    // Private Attributes

    var that = {};
    var divRef;
    var parentRef;
    var dotList = [];
    var timerTick = 0;
    var timerId;
    var isShown = false;

    var imageCache = [];
    var imageCount = 0;
    var loadedCallback;
    var loadStartTime;
    var loadTimeMin = 0;


    // Private Methods

    function startTimer()
    {
        timerId = setInterval(onTimer, TIMER_INTERVAL);
    }


    function stopTimer()
    {
        clearInterval(timerId);
        timerId = undefined;
    }


    function onTimer()
    {
        // Set opacity
        var dot1 = Math.floor(timerTick / FADE_STEPS);
        var dot2 = (dot1 + 1 === DOT_COUNT) ? 0 : dot1 + 1;
        var part = (FADE_STEPS - (timerTick % FADE_STEPS)) / FADE_STEPS;

        setOpacity(dotList[dot1], part * (1 - MIN_OPACITY) + MIN_OPACITY);
        setOpacity(dotList[dot2], (1 - part) * (1 - MIN_OPACITY) + MIN_OPACITY);

        // Make sure the old dot's opacity is set to MIN_OPACITY.
        if (part === 1)
        {
            var dot0 = (dot1 - 1 < 0) ? DOT_COUNT - 1 : dot1 - 1;
            setOpacity(dotList[dot0], MIN_OPACITY);
        }

        // Update timer tick
        timerTick++;
        if (timerTick >= FADE_STEPS * DOT_COUNT)
        {
            timerTick = 0;
        }
    }


    function onAllImagesLoaded()
    {
        that.hide();

        if (loadedCallback)
        {
            loadedCallback();
        }
    }


    function onImageLoaded()
    {
        imageCount--;

        if (imageCount === 0)
        {
            var now = new Date();
            var loadTime = now.getTime() - loadStartTime.getTime();

            if (loadTime >= loadTimeMin)
            {
                onAllImagesLoaded();
            }
            else
            {
                setTimeout(onAllImagesLoaded, loadTimeMin - loadTime);
            }
        }
    }


    // Public Methods

    /**
     * Shows the loading splash.
     */
    that.show = function()
    {
        if (isShown === false)
        {
            parentRef.appendChild(divRef);
            isShown = true;

            startTimer();
        }
    };


    /**
     * Hides the loading splash.
     */
    that.hide = function()
    {
        if (isShown === true)
        {
            stopTimer();
            isShown = false;

            parentRef.removeChild(divRef);
        }
    };


    /** 
     * Loads all images in the given array while showing loading splash. When all images are loaded,
     * the given callback is called.
     * srcArray: an array of strings that is a path to an image to preload.
     * callback: optional javascript function that is call when all images are loaded.
     * parentElement: the DOM object reference to insert the loading splash into.
     * minLoadTime: optional number that displayes the load splash at least this number in ms.
     */
    that.load = function(srcArray, callback, parentElement, minLoadTime)
    {
        if (!srcArray || !(srcArray instanceof Array))
        {
            throw "loadSplash.load - srcArray cannot be null or undefined.";
        }

        if (!parentElement || typeof(parentElement) !== "object")
        {
            throw "loadSplash.load - parentElement must be an html object reference.";
        }

        loadedCallback = callback;
        parentRef = parentElement;

        if (minLoadTime !== undefined)
        {
            loadTimeMin = minLoadTime;
        }
        else
        {
            loadTimeMin = 0;
        }

        loadStartTime = new Date();

        for (var i = 0; i < srcArray.length; i++)
        {
            var img = new Image();
            img.onload = onImageLoaded;

            // Makes sure the page still loads even if an image is not loaded.
            img.onerror = onImageLoaded;
            img.onabort = onImageLoaded;

            imageCache.push(img);
            imageCount++;
            img.src = srcArray[i];
        }

        that.show();
    };


    // Initialize component

    (function()
    {
        // Create div for storing images
        divRef = document.createElement("div");
        divRef.className = "load_splash";

        for (var i = 0; i < DOT_COUNT; i++)
        {
            var dot = document.createElement("div");
            dot.className = (i === 0) ? "load_splash_dot" : "load_splash_dot margin";
            setOpacity(dot, MIN_OPACITY);

            dotList.push(dot);
            divRef.appendChild(dot);
        }

    }());

    return that;
}());

