var animation; // acts as namespace for everything available externally from this file

if (!animation) {
    animation = {};
}

(function () {
    
    ////////// CLASS AnimationControl
    /* 
        -- after setup, setAnimations can be changed as needed
        -- calling setTimeframe again neccessitates calling setAnimations again;
      
        An animate-able object has these properties:
        
            div                    // the div that contains blankImg
            blankImg               // blankImg will be replaced in the div by the current animation frame, then put back when the animation is swapped out
            timezoneName
            firstFrameDate
            lastFrameDate
            timezoneOffset
                
    */
        var AnimationControl = animation.AnimationControl = function (obcodes, dateBar) {
            this.obcodes = obcodes;
            this.firstObcodeUnixTime = Date.UTC(2007, 9, 1); // Oct 1st, 2007
            
            this.NUM_MILLISECONDS_IN_A_DAY = 1000 * 60 * 60 * 24;
            this.NUM_MILLISECONDS_IN_6_HOURS = 1000 * 60 * 60 * 6;
            
            this.NO_IMAGE_AVAILABLE_URL = '/assets/images/no-map-data.png';
            this.NO_WIND_IMAGE_AVAILABLE_URL = '/assets/images/no-wind-image.png'
            this.NO_IMAGE_AVAILABLE_URL_SET = [];

            this.dateBar = dateBar;

            
            this.playPauseButton = $id('playpause');
            this.timezoneName = $id('timezoneName');
            
            this.MAX_NUM_FRAMES = 28;
            this.numFrames = this.MAX_NUM_FRAMES;
            
            this.NEXT_FRAME_DELAY = 850;  // default time between frames in milliseconds
            
                        
            this.imgs = [];
            this.imgReadyFlags = [];
            for (var i = 0; i < this.MAX_NUM_FRAMES; i++) {
                this.imgs[i] = $createEl('img');
                this.imgs[i].width = 512;
                this.imgs[i].height = 384;
                this.imgReadyFlags[i] = false;
                
                this.NO_IMAGE_AVAILABLE_URL_SET.push(this.NO_WIND_IMAGE_AVAILABLE_URL);
            }
        
            this.lastType = 'height';    
            
            this.loadingFrameIdx = null;
            
            this.paused = true;
            this.startLoadTime = null;
            
            
            var me = this;
            
            this.swapInNewlyLoadedImage = function (evt) {
                if (!me.newFrameCancled) {
                    var loadingFrameIdx = me.loadingFrameIdx;
                    var newImg = me.imgs[loadingFrameIdx];
                    var oldImg = me.currentImg;
                    if (newImg !== oldImg) {
                        me.animatedDiv.replaceChild(newImg, oldImg);
                    }
                    me.currentImg = newImg;
                    me.currentFrameIdx = loadingFrameIdx;
                    me.imgReadyFlags[loadingFrameIdx] = true;                
                    me.dateBar.positionTimeMarker(loadingFrameIdx);
                }
            };
            
            
            this.onImgError = function (error) {
            };
            
            
            this.swapInNewlyLoadedImageAndContinueAnimation = function (evt) {
                if (!me.paused) {
                    me.swapInNewlyLoadedImage();
                    me.paused = true; // so call to play won't be cut short
                    me.play();
                }
            };
            
                
            this.swapInNewlyLoadedImageAndContinueAnimationTimed = function (evt) {
                var imageLoadTime = general.getDate().getTime() - me.startLoadTime;
                if (imageLoadTime < me.NEXT_FRAME_DELAY) {
                    setTimeout(
                        me.swapInNewlyLoadedImageAndContinueAnimation,
                        me.NEXT_FRAME_DELAY - imageLoadTime
                    );
                }
                else {
                    me.swapInNewlyLoadedImageAndContinueAnimation();
                }    
            };
            
        };
       
        AnimationControl.prototype = new Object(); 
        AnimationControl.prototype.constructor = AnimationControl;
                
        
        /**/
        AnimationControl.prototype.initToFirstRegion = function (frame) {
            var region = SWMap.map.getCenter().getTightestContainingRegion();
            
            this.animation = region;
            this.animatedDiv = region.div;
            this.currentType = 'off';
            this.currentImg = this.blankImg = region.blankImg; 
            this.currentFrameIdx = 0;
            this.setTimezoneName(region);
        };
        
        var prevImg;

        /**/
        AnimationControl.prototype.setToFrame = function (frame) {
            this.stop();
            this.newFrameCancled = false;
            
            var me = this;
            
            // make sure last frame is one that's visible for the current location
            if (frame >= this.numFrames) {
                frame = this.numFrames - 1;
                if (frame < 0) {
                    frame = 0;
                    general.reportClientError(
                        'AnimationControl: this.numFrames is less than 0'
                    );
                }
            }
        
            this.loadingFrameIdx = frame;
            
            if (this.imgReadyFlags[frame]) {
                this.swapInNewlyLoadedImage();
            }
            else {
                var newImg = this.imgs[frame];
                var url = this.imgURLs[frame];
                // in Safari, an image onload is only triggered when the new src does not match the current source, so we need to set the URL before setting it for real
                if (url === newImg.src) {
                    newImg.src = '/assets/images/spacer.gif';
                }
                newImg.onload = this.swapInNewlyLoadedImage;
                newImg.src = url;
            }
        };
        
        
        AnimationControl.prototype.rememberCurrentAnimation = function () {
            this.rememberedAnimation = this.animation;
            this.rememberedAnimationType = this.currentType;
        };
        
        
        AnimationControl.prototype.restoreRememberedAnimation = function () {
            this.setAnimation(this.rememberedAnimation, this.rememberedAnimationType);
        };
        
        /**/
        AnimationControl.prototype.play = function (goPastLastIdx) {
            if (this.paused) {
                  
                if ((this.currentFrameIdx === this.numFrames - 1) && !goPastLastIdx) {
                    this.playPauseButton.src = '/assets/images/icn_play_small.gif';
                }
                else {
                    this.paused = false;
                    this.newFrameCancled = false;
                
                    this.playPauseButton.src = '/assets/images/icn_pause_small.gif';
                
                    var loadingFrameIdx = this.currentFrameIdx + 1;
                    if (loadingFrameIdx >= this.numFrames) {
                        loadingFrameIdx = 0;   
                    }
                    
                    this.loadingFrameIdx = loadingFrameIdx;

                    if (this.imgReadyFlags[loadingFrameIdx]) { // if next image already aquired
                        setTimeout(this.swapInNewlyLoadedImageAndContinueAnimation, this.NEXT_FRAME_DELAY);
                    }
                    else { // if next image not yet aquired
                        this.startLoadTime = general.getDate().getTime();
                    
                        var nextImg = this.imgs[loadingFrameIdx];
                        nextImg.onload = this.swapInNewlyLoadedImageAndContinueAnimationTimed;
                        nextImg.src = this.imgURLs[loadingFrameIdx];
                    }
                }
            }
        };
        
        
        /**/
        AnimationControl.prototype.stop = function () {
            this.paused = true;
            this.newFrameCancled = true;
                        
            // toggle the play/pause button
            this.playPauseButton.src = '/assets/images/icn_play_small.gif';
        };
        
        /* 'newDate' is optional boolean specifying whether the date has changed (in which case the image urls need to be redone) */
        AnimationControl.prototype.setAnimation = function (anim, type, frame, newDate, forceUpdate) {
            this.stop();
            this.newFrameCancled = false;
          
            type = type || this.lastType;
                        
            var oldAnim = this.animation;

            if (anim !== oldAnim || forceUpdate) {
                // set old anim back to blank
                if (this.blankImg !== this.currentImg) {
                    this.animatedDiv.replaceChild(this.blankImg, this.currentImg);
                }
                
                this.animation = anim;
                this.animatedDiv = anim.div;
                this.blankImg = anim.blankImg;
                this.currentImg = this.blankImg;
                
                this.setTimezoneName(anim);
            }
            
            if (anim !== oldAnim || type !== this.currentType || newDate || forceUpdate) {
                if (frame && frame !== 0) {
                    this.currentFrameIdx = frame;
                }
                
                // when user picks a new date from the calendar, they want to see the first frame of that day
                if (newDate) {
                    this.currentFrameIdx = 0;
                }
                
                // reset the imgs
                for (var i = 0; i < this.MAX_NUM_FRAMES; i++) {
                    this.imgReadyFlags[i] = false;
                    var img = this.imgs[i];
                    img.onload = null;
                    img.onerror = this.onImgError;
                    // skip for the current idx so spacer.gif is not seen while waiting for the image to load when switching between map types
                    if (i !== this.currentFrameIdx) { 
                        img.src = "/assets/images/spacer.gif";
                    }
                }
                
                this.setType(type, anim);
            }
        };
        
        
        /* */
        AnimationControl.prototype.setTimezoneName = function (anim) {
            this.timezoneName.innerHTML = 
                '<span style="font-size: 11px;">Times are local to</span> <strong>' 
                + anim.timezoneName 
                + '</strong>'
            ;
        };
 
 
        /**/
        AnimationControl.prototype.setMapScaleKey = function (type) {
            var unitOfMeasure;
            
            if (type === 'center_height' || type === 'left_height' || type === 'right_height') {
                type = 'height';
            }
            
            if (SWMap.userPrefs.uom == SW.UserPrefs.UOM.METERS) {
                unitOfMeasure = ({height: 'm', period: '', wind: 'kmh', off: ''})[type];        
            }
            else {
                unitOfMeasure = ({height: 'ft', period: '', wind: 'kt', off: ''})[type];        
            }

            var mapScale = $id("mapScale");
            if (mapScale && mapScale.src) {
                mapScale.src = mapScale.src.substring(0, mapScale.src.lastIndexOf("/")+1) + "scale-" + type + (unitOfMeasure ? "-" + unitOfMeasure : "") + ".gif";
            }
        };
 
 
        /* assumes region has been set in the animation control and that this.currentFrameIdx is correct */
        AnimationControl.prototype.setType = function (type, anim) {
            this.stop();
        
            this.setMapScaleKey(type);
            SWMap.animationSelectionControl.setHighlightedButton(type);
            
            anim = anim || this.animation;
            if (type === 'off') {
                this.animatedDiv.replaceChild(this.blankImg, this.currentImg);
                this.currentImg = this.blankImg;
            }
            else {
                if (type === 'wind' && ((!anim.hasWind) || (anim.regionId === 1560) || (anim.regionId === 1579))) {  // TODO move this logic into deriveFrameURLs
                    this.imgURLs = this.NO_IMAGE_AVAILABLE_URL_SET;
                }
                else {
                    this.imgURLs = this.deriveFrameURLs(
                        anim, 
                        type, 
                        this.getFrameTimes(anim)
                    );
                }
                
                this.setToFrame(this.currentFrameIdx);
            }
            
            if (type === 'height' || type === 'period' || type === 'wind') {
                this.lastType = type;
            }
                
            this.currentType = type;
        };
        
        
        /* update animation when user selects new date */
        AnimationControl.prototype.updateAfterNewDate = function () {
            this.setAnimation(
                this.animation, 
                this.currentType, 
                null,
                true
            );
        };
        
        /* handler for clicking the play/pause button */
        AnimationControl.prototype.playPauseControl = function () {
            if (this.paused) {
                if (this.currentType === 'off') {
                    mapGeneral.centerAndZoomOnRegionAnimation(null, null, null, true); // play after move
                }
                else {
                    this.play(true);
                }
            }
            else {
                this.stop();
            }       
        };
                
        
        /* get the frame url obfuscation code for a particular time (expressed in unix time) */
        AnimationControl.prototype.getObCodeByDate = function (unixTime) {
            
            var obcodeIdx = Math.floor(
                (unixTime - this.firstObcodeUnixTime) / this.NUM_MILLISECONDS_IN_A_DAY
            );
            var obcode = this.obcodes[obcodeIdx];
            
            if (!obcode) {
                throw "No obfuscation code for " + new Date(unixTime);
            }
            
            return obcode;
        };
        
        
        /* get 00:00 of the current calander start date */
        AnimationControl.prototype.getCalendarStartDate = function () {
            var date = this.dateBar.currentStartDate;
            var time = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
            return time;
        };
        
        
        /*  */
        AnimationControl.prototype.getUserDate = function () {
            var date = general.getDate();
            // TODO check that user's system clock is sane (make sure not in DEV mode - see general.getDate())
            var time = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
            return time;
        };
        
        
        /* */
        AnimationControl.prototype.unixTimeToYearMonthDayHours = function (unixTime) {
            var date = new Date(unixTime);
            
            var month = '00' + (date.getUTCMonth() + 1);
            month = month.slice(month.length - 2);
            
            var hour = '00' + date.getUTCHours();
            hour = hour.slice(hour.length - 2);
            
            return {
                year: date.getUTCFullYear(), 
                month: month, 
                day: date.getUTCDate(), 
                hour: hour
            };
        };
        
        
        /**/
        AnimationControl.prototype.roundUnixTimeToNearestQuarterDay = function (unixTime) {
            var date = new Date(unixTime);
            var year = date.getUTCFullYear();
            var month = date.getUTCMonth();
            var day = date.getUTCDate();
            var hours = [0,0,0, 6,6,6, 6,6,6, 12,12,12, 12,12,12, 18,18,18, 18,18,18, 24,24,24][date.getUTCHours()];

            if (hours === 24) {
                var roundedUnixTime = Date.UTC(year, month, day, 0) + this.NUM_MILLISECONDS_IN_A_DAY;
            } else {
                roundedUnixTime = Date.UTC(year, month, day, hours);
            }
            return roundedUnixTime;
        };
        
        
        /* also sets number of frames and tells datebar to obscure frame ticks that are past the last available date for this region */
        AnimationControl.prototype.getFrameTimes = function (anim) {
            var startTime = this.getCalendarStartDate(); 
        
            var timezoneOffsetInMilliseconds = 1000 * 60 * anim.timezoneOffset; // region.timezoneOffset is in minutes
            var firstFrameDate = anim.firstFrameDate || this.firstObcodeUnixTime;
            var lastFrameDate = anim.lastFrameDate;
            
            startTime -= timezoneOffsetInMilliseconds; // e.g. a region 240 minutes ahead needs to see (localtime - 240 min.) to see localtime
            startTime = this.roundUnixTimeToNearestQuarterDay(startTime);
            
            var time = startTime;
            var times = [];
            var numFramesInPast = 0;
            for (var i = 0; i < this.MAX_NUM_FRAMES; i++) {
                if (time > lastFrameDate) {
                    break;
                }
                
                if (time >= firstFrameDate) {
                    times[i] = time;
                } else {
                    times[i] = null;
                }
                time += this.NUM_MILLISECONDS_IN_6_HOURS;
            }
            
            var numTicksToObscure = this.MAX_NUM_FRAMES - i;
            this.dateBar.renderEndTickObscurer(numTicksToObscure);
            this.numFrames = i;
            
            return times; // as unix times
        };
        
        
        /**/
        AnimationControl.prototype.deriveFrameURLs = function (anim, type, times) {
            var animId = (anim.isPerspective) ? anim.perspectiveId + '' : anim.regionId + '';
            var lastTwoDigitsOfAnimId = animId.slice(animId.length - 2);
            var timezoneName = anim.timezoneName;
            
            var userTime = this.getUserDate();
            var userTimeRoundedToDay = Math.floor(userTime / this.NUM_MILLISECONDS_IN_A_DAY);
            
            var userDate = this.unixTimeToYearMonthDayHours(userTime);
            var paramString = '?' + userDate.month + '-' + userDate.day;
            
            var urls = [];
            for (var i in times) {
                var time = times[i];
                
                var url;
                if (time) {
                    var obcode = this.getObCodeByDate(time);
                    var date = this.unixTimeToYearMonthDayHours(time);
                    
                    var timeRoundedToDay = Math.floor(time / this.NUM_MILLISECONDS_IN_A_DAY);
                    var isFutureDate = (timeRoundedToDay >= userTimeRoundedToDay);
                    
                    url = [
                        'http://st', ((date.hour%12)/6) ,'.swellwatch.wetsand.com/', // alternate every other frame onto a different domain st0|st1
                        (anim.isPerspective) ? '3d/' : '2d/', // may as well separate perspectives from regions because they use different sets of ids
                        date.year, '/',
                        date.month, '/',
                        lastTwoDigitsOfAnimId, '/', 
                        animId, '/',
                        type, '/',
                        date.day,
                        date.hour,
                        '-',
                        obcode,
                        '.png',
                        (isFutureDate) ? paramString : ''
                    ].join('');
                    
                } else {
                    url = this.NO_IMAGE_AVAILABLE_URL;
                }
                urls.push(url);
            }
            return urls;            
        };
        
    ////////// END CLASS AnimationControl
       
    
})();
    
