/*
 * This depends on...
 *   video_picker.css
 *   video.js
 */
MOBX.VideoPicker = (function () {
    var publicObj = {};
    var pickerElement = null;
    var limit = 9;
    var currentOffset = 0;
    var activatedClassName = "video_picker_activated";
    var nextActivated = true;
    var previousActivated = false;
    var nextElement = null;
    var previousElement = null;
    
    var onClickFunction,
        onUnclickFunction,
        onUpdateFunction,
        onPreselectFunction;

    var selectedVideoUids = [];

    var multiSelectEnabled;

    var markSelected = false;
    var selectedVideoClass = 'selected';

    var externalValidationFunction = function (vid) {
        return vid.status === 'deployed';
    };
    var formElementsContainer = null;
    
    // go off to the site and get 3 times the limit of videos.  If there are no videos returned, deactivate
    // the next link.  Otherwise, activate the next link.
    function fetchNextPage() {
        function handleVideos(vids) {
            debug('receiving videos');
            if (vids && vids.length > 0) {
                debug('got some videos');
                debug('vids length: ' + vids.length);
                debug('all loaded videos length: ' + MOBX.Video.count());
                if (!nextActivated) {
                    debug('activating next');
                    activateNext();
                }
            }
        }
        debug('fetching next page');
        MOBX.Video.fetchVideos((limit * 3), handleVideos, {'offset': (MOBX.Video.count())});
    }
    
    // deactivate the "see more videos" link
    function deactivateNext() {
        nextActivated = false;
        nextElement.removeClassName("active");
    }
    
    // deactivate the previous videos link
    function deactivatePrevious() {
        previousActivated = false;
        //temporary code
        previousElement.removeClassName("active");
    }
    
    // activate the "see more videos" link
    function activateNext() {
        nextActivated = true;
        nextElement.addClassName("active");
    }
    
    // activate the "previous videos" link
    function activatePrevious() {
        previousActivated = true;
        previousElement.addClassName("active");
    }
    
    // when a user clicks "see more videos" then handle the click here
    // limit is the number of videos per page, so add that to currentOffset (so we know where we are)
    // fire off the json updater with the correct page of videos (replacing what's already in there)
    // fetch the next page if it's not already available in the MOBX.Video object
    function handleNextClick(evt) {
        Event.stop(evt);
        
        if (nextActivated) {
            currentOffset += limit;
            activatePrevious();
            debug('updating the picker element');
            debug("currentOffset:" + currentOffset);
            debug("videos length:" + MOBX.Video.count());
            updatePicker(MOBX.Video.loadedVideos().slice(currentOffset - limit, currentOffset));
            if (currentOffset < MOBX.Video.count()) {
                fetchNextPage();
            } else {
                deactivateNext();
            }
            
        }
    }
    
    // subtract limit from currentOffset so we know where we are, update the video picker element with the current page
    // deactivate previous if there are no 
    function handlePreviousClick(evt) {
        Event.stop(evt);
        if (previousActivated) {
            currentOffset -= limit;
            activateNext();
            debug("currentOffset:" + currentOffset);
            updatePicker(MOBX.Video.loadedVideos().slice(currentOffset - limit, currentOffset));
            if (currentOffset <= limit) {
                currentOffset = limit;
                deactivatePrevious();
            }
        }
    }
    
    publicObj.goToFirstPage = function () {
        if (MOBX.Video.count() > 0) {
             currentOffset = limit;
             if (MOBX.Video.count() > limit) {
                 activateNext();
             }
             deactivatePrevious();
             updatePicker(MOBX.Video.loadedVideos().slice(currentOffset - limit, currentOffset));   
        }
    };
    
    function showUploadPrompt() {
        pickerElement.addClassName("no_videos");
        $$(".video_picker_no_videos").invoke("show");
        $$(".video_picker_has_videos").invoke("hide");
    }
    
    function hideUploadPrompt() {
        pickerElement.removeClassName("no_videos");
        $$(".video_picker_no_videos").invoke("hide");
        $$(".video_picker_has_videos").invoke("show");
    }
    
    function handleUploadCanceled() {
        if (MOBX.Video.count() === 0) {
            showUploadPrompt();
        }
    }
    
    function updatePicker(vids) {
        MOBX.JSONUpdater.update(pickerElement, vids, 200, {'location': 'top'});
        validateVideos();
        var videoElems = pickerElement.select('.status_success');
        MOBX.EventHandler.fireCustom(pickerElement, 'updatedVideos', {videos: videoElems});
        markSelectedVideos();
        if (onUpdateFunction) {
            onUpdateFunction(vids);
        }
    }

    function markSelectedVideos() {
        if (markSelected) {
            publicObj.visibleSelectedVideos().each(function(video) {
                var elem = video.getDomElement();
                if (elem) {
                    elem.addClassName(selectedVideoClass);
                }
            });
        }
    }

    function clickSelectedVideos() {
        var selectedVideos = publicObj.selectedVideos();

        /* At this point, we have access to video objects for all selected videos.
         * This is also then only time when it's possible that an invalid video is selected (via the preselect option)
         * => prune invalid videos out here */
        var invalidAndSelected = selectedVideos.reject(externalValidationFunction);
        publicObj.unselectVideos(invalidAndSelected);

        if (selectedVideos.length > 0) {
            if (onPreselectFunction) {
                onPreselectFunction(selectedVideos);
            }
            else {
                selectedVideos.each(function (video) {
                    onClickFunction(video);
                });
            }
        }
    }

    /* adds the video to the list of selected videos and, if markSelected is
       enabled, adds the selectedVideoClass to the video's element.
       THIS DOES NOT CALL THE onClickFunction */
    publicObj.selectVideo = function (video) {
        publicObj.selectVideos([video]);
    };

    publicObj.selectVideos = function (videos) {
        var uids = videos.pluck('uid');
        selectedVideoUids = selectedVideoUids.concat(uids).uniq();

        if (markSelected) {
            videos.each(function(video) {
                var el = video.getDomElement();
                if(el) {
                    el.addClassName(selectedVideoClass);
                }
            });
        }
    };

    /* removes the video from the list of selected videos and, if markSelected
       is enabled, removes the selectedVideoClass from the video's element.
       THIS DOES NOT CALL THE onUnclickFunction */
    publicObj.unselectVideo = function (video) {
        publicObj.unselectVideos([video]);
    };

    publicObj.unselectVideos = function (videos) {
        var uids = videos.pluck('uid'),
            without = selectedVideoUids.without;
        selectedVideoUids = without.apply(selectedVideoUids, uids);

        if (markSelected) {
            videos.each(function (video) {
                var el = video.getDomElement();
                if(el) {
                    el.removeClassName(selectedVideoClass);
                }
            });
        }
    };

    publicObj.visibleVideos = function () {
        return MOBX.Video.loadedVideos().select(function (video) {
            return video && video.getDomElement();
        });
    };

    publicObj.selectedVideos = function () {
        return selectedVideoUids.map(function (uid) {
            return MOBX.Video.findByUid(uid);
        }).compact();
    };

    publicObj.visibleSelectedVideos = function () {
        return publicObj.selectedVideos().select(function (video) {
            return video && video.getDomElement();
        });
    };

    publicObj.unselectedVideos = function () {
        return MOBX.Video.loadedVideos().reject(function (video) {
            return selectedVideoUids.include(video.uid);
        });
    };

    publicObj.visibleUnselectedVideos = function () {
        return publicObj.unselectedVideos().select(function (video) {
            return video && video.getDomElement();
        });
    };

   function isVideoSelected(video) {
       return selectedVideoUids.include(video.uid);
   }

   function whenSelectedLoaded(action) {
       var uidsNotLoaded = selectedVideoUids.reject(function (uid) {
           return MOBX.Video.findByUid(uid);
       });
       if (uidsNotLoaded.length === 0) {
           action();
       } else {
           MOBX.Video.fetchVideosByUid(uidsNotLoaded, function (videos) {
               // this caches the video records, so they're accessible with findByUid
               action();
           });
       }
   }

    function validateVideos() {
        pickerElement.select('.video').each(function (videoDomElement) {
            if (!videoDomElement.hasClassName("place_holder")) {
                var video = MOBX.Video.fromDomElement(videoDomElement);
                if (video && !externalValidationFunction(video)) {
                    videoDomElement.addClassName("unavailable");
                    videoDomElement.removeClassName("responds_to_mouseover");
                }
            } else {
                videoDomElement.removeClassName("responds_to_mouseover");
            }
        });
    }
    
    // if there are no videos in the video picker then this function gets called to go fetch some of the videos.
    // fires off a request for videos and then populates the div... then goes and gets the next page of video
    function populateInitial() {
        debug("populate called");        
        function handleVideos(vids) {
            currentOffset = MOBX.Video.count();
            debug('handling videos');
            if (vids && vids.length > 0) {
                hideUploadPrompt();
                debug("populating the div");
                updatePicker(vids);
                fetchNextPage();
                markSelectedVideos();
                whenSelectedLoaded(clickSelectedVideos);
            } else {
                debug("showing the upload prompt");
                showUploadPrompt();
            }
        }
        
        hideUploadPrompt();
        
        debug("length of videos loaded: " + MOBX.Video.count());
        MOBX.Video.fetchVideos(limit, handleVideos, {'offset': currentOffset});
    }
    
    // if a video gets added by some other thing - then hide the uploader
    function handleExternalVideoAddition(evt) {
        debug('hiding the upload prompt');
        hideUploadPrompt();
    }
      
    // subscribe to the next/previous links and listen for 
    function subscribeToEvents() {
        MOBX.EventHandler.subscribe(".video", "click", videoClickDelegater);
        MOBX.EventHandler.subscribe(".video_picker_next", "click", handleNextClick);
        MOBX.EventHandler.subscribe(".video_picker_previous", "click", handlePreviousClick);
        MOBX.EventHandler.subscribe(["#", pickerElement.id].join(""), "video_added", handleExternalVideoAddition);
        MOBX.EventHandler.subscribe(["#", pickerElement.id].join(""), "upload_canceled", handleUploadCanceled);

        function replaceEncodingVideo(video) {
            $("video_" + video.uid).up(".mobx_inserted_content").remove();
            MOBX.JSONUpdater.Insert.top($("video_picker"), video, 200);
        }
        
        MOBX.EventHandler.subscribe(MBX.cssNamespace, MBX.CustomEvent.Encoder.FINISHED, function (event) {
            MOBX.Video.fetchVideo(event.uid, replaceEncodingVideo);
        });
    }
    
    
    // we handle checking videos and finding videos here so the apps built on top of this can just get
    // a valid video back (non-encoding, non-empty)
    function videoClickDelegater(evt) {
        Event.stop(evt);
        var el = Event.element(evt);
        if (!el.hasClassName("video")) {
            el = el.up(".video");
        }
        if (!el.hasClassName("encoding") && !el.hasClassName("place_holder") && !el.hasClassName("unavailable")) {
            var vid = MOBX.Video.fromDomElement(el);
            if (externalValidationFunction(vid)) {
                if (!isVideoSelected(vid)) {
                    if (!multiSelectEnabled && selectedVideoUids.length > 0) {
                        var prevVideo = publicObj.selectedVideos()[0];
                        publicObj.unselectVideo(prevVideo);
                        onUnclickFunction(prevVideo);
                    }
                    publicObj.selectVideo(vid);
                    onClickFunction(vid);
                    // in case the button was used to select it, unfocus it
                    el.down('.button').blur();
                } else {
                    publicObj.unselectVideo(vid);
                    onUnclickFunction(vid);
                }
            }
        }
    }
    

    function processOpts(opts) {
        opts = opts || {};
        if (!opts.onVideoClick) {
            throw new Error("Oops... no click handler specified for the video picker");
            return false;
        }
        limit = opts.limit || limit;
        multiSelectEnabled = !!opts.multiSelect;

        onClickFunction     = opts.onVideoClick;
        onUnclickFunction   = opts.onVideoUnclick || function () {};
        onUpdateFunction    = opts.onPageChange || null;
        onPreselectFunction = opts.onPreselect  || null;

        if (opts.validationFunction) {
            externalValidationFunction = opts.validationFunction;
        }
        if (opts.markSelected) {
            markSelected = true;
            if (opts.markSelected !== true) {
                selectedVideoClass = opts.markSelected.toString();
            }
        }
        if (opts.preselected) {
            if( !MOBX.isArray(opts.preselected)) {
                opts.preselected = [opts.preselected];
            }
            if (!multiSelectEnabled && opts.preselected.length > 1) {
                throw new Error("Can't preselect more than one video when multiSelect is not enabled");
            }
            selectedVideoUids = opts.preselected;
        }
    }
    // call this and pass in an element which will activate the picker and populate the div if there aren't
    // already videos in it (denoted by a class of "video_picker_activated")
    // You *MUST* specify a dom element that is the video picker and also an "onVideoClick" function which
    // will be called when a live, active video is clicked.
    publicObj.activate = function (el, opts) {
        processOpts(opts);
        if (!$(el)) {
            throw new Error("the element specified for the video picker does not exist");
        }
        pickerElement = $(el);
        formElementsContainer = $(opts.formElementsContainer);
        nextElement = $$(".video_picker_next").first();
        previousElement = $$(".video_picker_previous").first();
        subscribeToEvents();
        deactivatePrevious();
        if (!pickerElement.hasClassName(activatedClassName)) {
            pickerElement.addClassName(activatedClassName);
            deactivateNext();
            populateInitial();
        } else {
            debug("videos length:" + MOBX.Video.count());
            hideUploadPrompt();
            currentOffset = MOBX.Video.count();
            if (currentOffset <= limit) {
                deactivateNext();
            }
            fetchNextPage();
            validateVideos();
            markSelectedVideos();
            whenSelectedLoaded(clickSelectedVideos);
        }
		if (opts.inlineUploader){
			MOBX.Upload.init('select_files');
		}
    };
    
    function debug(txt) {
        //console.log(txt);
    }
    
    return publicObj;
})();
