Show:
/**
 * @module Shared
 */

/**
 * I am the Database.
 * I store all data coming from the server. The data model objects (like {{#crossLink "HypervideoModel"}}HypervideoModel{{/crossLink}})
 * get their data from me. When they are done with manipulating the data, I can store the data back to the server.
 *
 * Note: All data objects inside me must be passed by reference, so that data can be manipulated in place, and insertions and deletions
 * should alter immediatly the database. In this way, data is kept consistent across the app 
 * (see {{#crossLink "Annotation/FrameTrail.newObject:method"}}FrameTrail.newObject('Annotation', data){{/crossLink}}).
 *
 * @class Database
 * @static
 */

 FrameTrail.defineModule('Database', function(){


    var projectID    = FrameTrail.module('RouteNavigation').projectID || '',
        hypervideoID = '',
        project      = {},
        hypervideos  = {},
        hypervideo   = {},
        sequence     = {},
        
        overlays     = [],
        links        = [],
        resources    = {},

        annotations            = {},
        annotationfileIDs      = {},

        subtitles              = {},
        subtitlesLangMapping   = {
            'en': 'English',
            'de': 'Deutsch',
            'fr': 'Français'
        },

        users  = {};
    
    /**
     * I load the project index data (../_data/projects/_index.json) from the server
     * and save the data for my projectID (from {{#crossLink "RouteNavigation/projectID:attribute"}}RouteNavigation/projectID{{/crossLink}})
     * in my attribute {{#crossLink "Database/project:attribute"}}Database/project{{/crossLink}}.
     * I call my success or fail callback respectively.
     *
     * @method loadProjectData
     * @param {Function} success
     * @param {Function} fail
     * @private
     */
    function loadProjectData(success, fail) {


        $.ajax({

            type:   "GET",
            url:    '../_data/projects/_index.json',
            cache:  false,
            dataType: "json",
            mimeType: "application/json" 
        }).done(function(data){

            project = data.projects[projectID];

            if(!project){
                return fail('This project does not exist.');
            }

            success.call(this);

        }).fail(function(){

            fail('No project index file.');

        });


    };


    /**
     * I load the resource index data (../_data/projects/ {{#crossLink "RouteNavigation/projectID:attribute"}}RouteNavigation/projectID{{/crossLink}} /resources/_index.json) from the server
     * and save the data in my attribute {{#crossLink "Database/resources:attribute"}}Database/resources{{/crossLink}}.
     * I call my success or fail callback respectively.
     * 
     * @method loadResourceData
     * @param {Function} success
     * @param {Function} fail
     */
    function loadResourceData(success, fail) {

        $.ajax({

            type:   "GET",
            url:    ('../_data/projects/' + projectID + '/resources/_index.json'),
            cache:  false,
            dataType: "json",
            mimeType: "application/json"      
        }).done(function(data){
        
            resources = data.resources;
            
            success.call(this);

        }).fail(function(){

            fail('No resources index file.');

        });

    };


    /**
     * I load the user.json of the current project from the server
     * and save the  data in my attribute {{#crossLink "Database/users:attribute"}}Database/users{{/crossLink}}.
     * I call my success or fail callback respectively.
     * 
     * @method loadUserData
     * @param {Function} success
     * @param {Function} fail
     */
    function loadUserData(success, fail) {

        if (!FrameTrail.module('RouteNavigation').environment.server) {
            
            $.ajax({
                type:   "GET",
                url:    ('../_data/projects/' + projectID + '/users.json'),
                cache:  false,
                dataType: "json",
                mimeType: "application/json"      
            }).done(function(data){
            
                users = data.user;
                
                success.call(this);

            }).fail(function(){

                fail('No user index file.');

            });
        } else {

            $.ajax({

                type:   "POST",
                url:    ('../_server/ajaxServer.php'),
                cache:  false,
                dataType: "json",
                mimeType: "application/json",
                data:   {

                    a:          'userGet',
                    projectID:  projectID

                }
            
            }).done(function(data){

                users = data.response.user;
                
                success.call(this);

            }).fail(function(){

                fail('No user index file.');

            });

        }

        

    };


    /**
     * I load the hypervideo index data (../_data/projects/ {{#crossLink "RouteNavigation/projectID:attribute"}}RouteNavigation/projectID{{/crossLink}} /hypervideos/_index.json) from the server
     * and save the data in my attribute {{#crossLink "Database/hypervideos:attribute"}}Database/hypervideos{{/crossLink}}.
     * I call my success or fail callback respectively.
     *
     * @method loadHypervideoData
     * @param {Function} success
     * @param {Function} fail
     * @private
     */
    function loadHypervideoData(success, fail) {

        $.ajax({

            type:   "GET",
            url:    ('../_data/projects/' + projectID + '/hypervideos/_index.json'),
            cache:  false,
            dataType: "json",
            mimeType: "application/json"
        }).done(function(data){
        
            hypervideos = data.hypervideos;
            
            success.call(this);

        }).fail(function(){

            fail('No hypervideo index file.');

        });

    };



    /**
     * I load the hypervideo sequence data (../_data/projects/ {{#crossLink "RouteNavigation/projectID:attribute"}}RouteNavigation/projectID{{/crossLink}} 
     * /hypervideos/ {{#crossLink "RouteNavigation/hypervideoID:attribute"}}RouteNavigation/hypervideoID{{/crossLink}} /hypervideo.json) from the server
     * and save the data in my attribute {{#crossLink "Database/hypervideo:attribute"}}Database/hypervideos{{/crossLink}}.
     * I call my success or fail callback respectively.
     *
     * @method loadSequenceData
     * @param {Function} success
     * @param {Function} fail
     * @private
     */
    function loadSequenceData(success, fail) {
        
        $.ajax({

            type: "GET",
            url: ('../_data/projects/' + projectID + '/hypervideos/' + hypervideoID + '/hypervideo.json'),
            cache: false,
            dataType: "json",
            mimeType: "application/json"
        }).done(function(data){
        
            sequence = data;
            success.call(this);

        }).fail(function() {

            fail('No hypervideo data.');

        });


    };


    /**
     * I load the overlay data (../_data/projects/ {{#crossLink "RouteNavigation/projectID:attribute"}}RouteNavigation/projectID{{/crossLink}} 
     * /hypervideos/ {{#crossLink "RouteNavigation/hypervideoID:attribute"}}RouteNavigation/hypervideoID{{/crossLink}} /hypervideo.json) from the server
     * and save the data in my attribute {{#crossLink "Database/overlays:attribute"}}Database/overlays{{/crossLink}}.
     * I call my success or fail callback respectively.
     *
     * @method loadOverlayData
     * @param {Function} success
     * @param {Function} fail
     * @private
     */
    function loadOverlayData(success, fail) {

        $.ajax({

            type: "GET",
            url: ('../_data/projects/' + projectID + '/hypervideos/' + hypervideoID + '/overlays.json'),
            cache: false,
            dataType: "json",
            mimeType: "application/json"
        }).done(function(data){
        
            overlays = data;
            success.call(this);

        }).fail(function() {

            fail('No Overlay data.');

        });

    };



    /**
     * I load the video link data (../_data/projects/ {{#crossLink "RouteNavigation/projectID:attribute"}}RouteNavigation/projectID{{/crossLink}} 
     * /hypervideos/ {{#crossLink "RouteNavigation/hypervideoID:attribute"}}RouteNavigation/hypervideoID{{/crossLink}} /hypervideo.json) from the server
     * and save the data in my attribute {{#crossLink "Database/links:attribute"}}Database/links{{/crossLink}}.
     * I call my success or fail callback respectively.
     *
     * @method loadLinksData
     * @param {Function} success
     * @param {Function} fail
     * @private 
     */
    function loadLinksData(success, fail) {

        $.ajax({

            type: "GET",
            url: ('../_data/projects/' + projectID + '/hypervideos/' + hypervideoID + '/links.json'),
            cache: false,
            dataType: "json",
            mimeType: "application/json"
        }).done(function(data){

            links = data;
            success.call(this);

        }).fail(function() {

            fail('No Links data.');

        });

    };


    /**
     * I load the annotation data (../_data/projects/ {{#crossLink "RouteNavigation/projectID:attribute"}}RouteNavigation/projectID{{/crossLink}} 
     * /hypervideos/ {{#crossLink "RouteNavigation/hypervideoID:attribute"}}RouteNavigation/hypervideoID{{/crossLink}} /hypervideo.json) from the server
     * and save the data in my attribute {{#crossLink "Database/annotations:attribute"}}Database/annotations{{/crossLink}},
     * and the respective annotationfileIDs in my attribute {{#crossLink "Database/annotationfileIDs:attribute"}}Database/annotationfileIDs{{/crossLink}},
     * 
     *
     * I call my success or fail callback respectively.
     *
     * @method loadAnnotationData
     * @param {Function} success
     * @param {Function} fail
     * @private
     */
    function loadAnnotationData(success, fail) {

        var annotationsCount = 0;

        // clear previous data
        annotationfileIDs = {};
        annotations  = {};


        for (var id in hypervideo.annotationfiles) {
            annotationsCount ++;
        }

        for (var id in hypervideo.annotationfiles) {
            
            (function(id){

                $.ajax({
                    type: "GET",
                    url: ('../_data/projects/' + projectID + '/hypervideos/' + hypervideoID + '/annotationfiles/' + id + '.json'),
                    cache: false,
                    dataType: "json",
                    mimeType: "application/json"
                }).done(function(data){
                

                    annotations[hypervideo.annotationfiles[id].ownerId]       = data;
                    annotationfileIDs[hypervideo.annotationfiles[id].ownerId] = id;

                    annotationsCount--;
                    if(annotationsCount === 0){
                        
                        // all annotation data loaded from server
                        success.call(this);

                    }


                }).fail(function() {

                    fail('Missing annotation file.');

                });

            }).call(this, id)

        }


    };




    /**
     * I load the subtitles data (../_data/projects/ {{#crossLink "RouteNavigation/projectID:attribute"}}RouteNavigation/projectID{{/crossLink}} 
     * /hypervideos/ {{#crossLink "RouteNavigation/hypervideoID:attribute"}}RouteNavigation/hypervideoID{{/crossLink}} /subtitles/...) from the server
     * and save the data in my attribute {{#crossLink "Database/subtitles:attribute"}}Database/subtitles{{/crossLink}}
     *
     * I call my success or fail callback respectively.
     *
     * @method loadSubtitleData
     * @param {Function} success
     * @param {Function} fail
     * @private
     */
    function loadSubtitleData(success, fail) {

        var subtitleCount = 0;

        subtitles = {};

        if (hypervideo.subtitles && hypervideo.subtitles.length > 0) {
            
            for (var idx in hypervideo.subtitles) {
                subtitleCount ++;
            }

            for (var i = 0; i < hypervideo.subtitles.length; i++) {
            
                (function(i){

                    var currentSubtitles = hypervideo.subtitles[i];

                    $.ajax({

                        type: "GET",
                        url: ('../_data/projects/' + projectID + '/hypervideos/' + hypervideoID + '/subtitles/' + currentSubtitles.src),
                        cache: false

                    }).done(function(data){
                        
                        var parsedCues = [];

                        // parse webvtt contents
                        var parser = new WebVTT.Parser(window, WebVTT.StringDecoder());
                        parser.onregion = function(region) {};
                        parser.oncue = function(cue) {
                            parsedCues.push(cue);
                        };
                        parser.onparsingerror = function(e) {
                            console.log(e);
                        };
                        parser.parse(data);
                        parser.flush();

                        var langLabel;
                        if (subtitlesLangMapping[currentSubtitles.srclang]) {
                            langLabel = subtitlesLangMapping[currentSubtitles.srclang];
                        } else {
                            langLabel = currentSubtitles.srclang;
                        }

                        // write parsed contents in subtitles var
                        subtitles[currentSubtitles.srclang] = {};
                        subtitles[currentSubtitles.srclang]['label'] = langLabel;
                        subtitles[currentSubtitles.srclang]['cues'] = parsedCues;

                        subtitleCount--;
                        if(subtitleCount === 0){
                            
                            // all subtitle data loaded from server
                            success.call(this);

                        }


                    }).fail(function() {

                        fail('Missing subtitle file.');

                    });

                }).call(this, i)

            }

        } else {

            // no subtitles found, continue
            success.call(this);

        }


    };




    
    /**
     * I initialise the load process of the database
     *
     * First I look for the {{#crossLink "RouteNavigation/projectID:attribute"}}RouteNavigation/projectID{{/crossLink}} and
     * {{#crossLink "RouteNavigation/hypervideoID:attribute"}}RouteNavigation/hypervideoID{{/crossLink}}.
     *
     * Then I call the nested load functions to fetch all data from the server.
     * I call my success or fail callback respectively.
     *
     * @method loadData
     * @param {Function} success
     * @param {Function} fail
     */
    function loadData(success, fail) {


        projectID    = FrameTrail.module('RouteNavigation').projectID;
        hypervideoID = FrameTrail.module('RouteNavigation').hypervideoID;
        

        if(projectID === undefined){

            return fail('No Project was selected.');

        }


        if(hypervideoID === undefined){

            //FrameTrail.module('InterfaceModal').showStatusMessage('No Hypervideo is selected.');

            hypervideo   = null;
            sequence     = {};
            annotations  = {};
            overlays     = [];
            links        = [];

            return  loadProjectData(function(){

                        loadResourceData(function(){

                            loadUserData(function(){

                                loadHypervideoData(function(){
                                
                                    success.call();

                                }, fail);

                            }, fail);

                        }, fail);

                    }, fail);

        }


        loadProjectData(function(){

            loadResourceData(function(){

                loadUserData(function(){

                    loadHypervideoData(function(){

                    
                        hypervideo = hypervideos[hypervideoID];

                        if(!hypervideo){

                            return fail('This hypervideo does not exist.');
                            
                        }

                        loadSequenceData(function(){

                            loadSubtitleData(function(){
                                
                                loadOverlayData(function(){

                                    loadLinksData(function(){

                                        loadAnnotationData(function(){

                                            success.call();

                                        }, fail);

                                    }, fail);

                                }, fail);

                            }, fail);

                        }, fail);


                    }, fail);


                }, fail);
            
            }, fail);

        }, fail);


    };



    /**
     * I update the hypervideo data inside the database
     *
     * @method updateHypervideoData
     * @param {Function} success
     * @param {Function} fail
     */
    function updateHypervideoData(success, fail) {

        hypervideoID = FrameTrail.module('RouteNavigation').hypervideoID;

        loadHypervideoData(function(){

            hypervideo = hypervideos[hypervideoID];
            
            if(!hypervideo){

                return fail('This hypervideo does not exist.');
                
            }

            loadSequenceData(function(){

                loadSubtitleData(function(){
                    
                    loadOverlayData(function(){

                        loadLinksData(function(){

                            loadAnnotationData(function(){

                                success.call();

                            }, fail);

                        }, fail);

                    }, fail);

                }, fail);

            }, fail);


        }, fail);

    };




    /**
     * I save the overlay data back to the server.
     *
     * My success callback gets one argument, which is either
     *
     *     { success: true }
     *
     * or
     *
     *     { failed: 'overlays', error: ... }
     *
     * @method saveOverlays
     * @param {Function} callback
     */
    function saveOverlays(callback) {

        $.ajax({
            type:   'POST',
            url:    '../_server/ajaxServer.php',
            cache:  false,

            data: {

                a:              'hypervideoChangeFile',
                projectID:      projectID,
                hypervideoID:   hypervideoID,
                type:           'overlays',

                src:            JSON.stringify(overlays)

            }

        }).done(function(data) {
            
            if (data.code === 0) {

                callback.call(window, { success: true });

            } else {

                callback.call(window, { 
                    failed: 'overlays', 
                    error: 'ServerError', 
                    code: data.code 
                });

            }

        }).fail(function(error){

            callback.call(window, { 
                failed: 'overlays', 
                error: error 
            });

        });

    };


    /**
     * I save the video link data back to the server.
     *
     * My success callback gets one argument, which is either
     *
     *     { success: true }
     *
     * or
     *
     *     { failed: 'links', error: ... }
     *
     * @method saveLinks
     * @param {} callback
     */
    function saveLinks(callback) {

        $.ajax({
            type:   'POST',
            url:    '../_server/ajaxServer.php',
            cache:  false,

            data: {

                a:              'hypervideoChangeFile',
                projectID:      projectID,
                hypervideoID:   hypervideoID,
                type:           'links',

                src:            JSON.stringify(links)

            }

        }).done(function(data) {
            
            if (data.code === 0) {

                callback.call(window, { success: true });

            } else {

                callback.call(window, { 
                    failed: 'links', 
                    error: 'ServerError', 
                    code: data.code 
                });

            }

        }).fail(function(error){

            callback.call(window, { 
                failed: 'links', 
                error: error 
            });

        });


    };


    /**
     * I save the annotation data back to the server.
     *
     * I choose by myself the appropriate server method ($_POST["action"]: "save" or "saveAs")
     * wether the user's annotation file does already exist, or has to be created.
     *
     * My success callback gets one argument, which is either
     *
     *     { success: true }
     *
     * or
     *
     *     { failed: 'annotations', error: ... }
     *
     * @method saveAnnotations
     * @param {Function} callback
     */
    function saveAnnotations(callback) {

        var userID              = FrameTrail.module('UserManagement').userID,
            action              = annotationfileIDs.hasOwnProperty(userID)
                                    ? 'save'
                                    : 'saveAs',

            annotationfileID    = annotationfileIDs[userID],

            name                = FrameTrail.getState('username'),
            description         = FrameTrail.getState('username') + '\'s annotations',
            hidden              = false;



        $.ajax({
            type:   'POST',
            url:    '../_server/ajaxServer.php',
            cache:  false,

            data: {

                a:                'annotationfileSave',
                projectID:        projectID,
                hypervideoID:     hypervideoID,
                action:           action,
                annotationfileID: annotationfileID,
                name:             name,
                description:      description,
                hidden:           hidden,

                src:              JSON.stringify(annotations[userID])

            }

        }).done(function(data) {
            
            if (data.code === 0) {

                if (action === 'saveAs') {
                    annotationfileIDs[userID] = data.annotationID.toString();
                }

                callback.call(window, { success: true });

            } else {

                callback.call(window, { 
                    failed: 'annotations', 
                    error: 'ServerError', 
                    code: data.code 
                });

            }

        }).fail(function(error){

            callback.call(window, { 
                failed: 'annotations', 
                error: error 
            });

        });


    };









    /**
     * I search the resource database for a given data object and return its id.
     *
     * @method getIdOfResource
     * @param {} resourceData
     * @return String or null
     */
    function getIdOfResource(resourceData) {

        for (var id in resources) {
            if (resources[id] === resourceData){
                return id;
            }
        }
        return null;
    };


    /**
     * I search the hypervideo database for a given data object and return its id.
     *
     * @method getIdOfHypervideo
     * @param {} data
     * @return String or null
     */
    function getIdOfHypervideo(data) {

        for (var id in hypervideos) {
            if (hypervideos[id] === data){
                return id;
            }
        }
        return null;
    };



    return {

        /**
         * I store the project index data (from the server's ../_data/projects/_index.json)
         * @attribute project
         */
        get project()       { return project },

        /**
         * I store the hypervideo index data (from the server's ../_data/projects/<ID>/hypervideos/_index.json)
         * @attribute hypervideos
         */
        get hypervideos()   { return hypervideos },
        /**
         * I store the hypervideo index data for the current hypervideo
         * @attribute hypervideo
         */
        get hypervideo()     { return hypervideo },

        //TODO Check if setting hypervideo data on update necessary
        set hypervideo(data) { return hypervideo = data },

        /**
         * I store the hypervideo sequence data (from the server's ../_data/projects/<ID>/hypervideos/<ID>/hypervideo.json)
         * @attribute sequence
         */
        get sequence()      { return sequence },
        /**
         * I store the overlays data (from the server's ../_data/projects/<ID>/hypervideos/<ID>/overlays.json)
         * @attribute overlays
         */
        get overlays()      { return overlays },
        /**
         * I store the video links data (from the server's ../_data/projects/<ID>/hypervideos/<ID>/links.json)
         * @attribute links
         */
        get links()         { return links },

        /**
         * I store the annotation data (from all json files from the server's ../_data/projects/<ID>/hypervideos/<ID>/annotationfiles/).
         *
         * I am a map of keys (userIDs) to an array of all annotations from that user.
         *
         *     {
         *         "userID": [ annotationData, annotationData, ... ]
         *     }
         * 
         * 
         * @attribute annotations
         */
        get annotations()        { return annotations       },
        /**
         * I store the file IDs of the user's annotation sets.
         *
         * The server manages file names automatically without influence of the client. That is why the client has to remeber the file ID 
         * of the several sets of annotations, which belong to a single user.
         *
         *     {
         *       "userID": "fileID"
         *     }
         * 
         * @attribute annotationfileIDs
         */
        get annotationfileIDs()  { return annotationfileIDs },

        /**
         * I store the subtitle data (from all .vtt files from the server's ../_data/projects/<ID>/hypervideos/<ID>/subtitles/).
         * 
         * @attribute annotations
         */
        get subtitles()        { return subtitles       },

        /**
         * I store a map of subtitle language codes and labels.
         * 
         * @attribute subtitlesLangMapping
         */
        get subtitlesLangMapping() { return subtitlesLangMapping },

        /**
         * I store the resource index data (from the server's ../_data/projects/<ID>/resources/_index.json)
         * @attribute resources
         */
        get resources()     { return resources },

        /**
         * I store the user data (from the projects user.json). The keys are the userIDs, and the values are maps of the user's attributes.
         * @attribute users
         */
        get users()     { return users },


        getIdOfResource:       getIdOfResource,
        getIdOfHypervideo:     getIdOfHypervideo,

        loadData:              loadData,
        loadProjectData:       loadProjectData,
        loadResourceData:      loadResourceData,

        loadHypervideoData:    loadHypervideoData,
        updateHypervideoData:  updateHypervideoData,
        loadSequenceData:      loadSequenceData,
        loadSubtitleData:      loadSubtitleData,

        saveOverlays:          saveOverlays,
        saveLinks:             saveLinks,
        saveAnnotations:       saveAnnotations

    }
    


});