(function () {

'use strict';

/* 
 * Same domain task proxy implementation for Bebras task API - v1.0 - 08/2014
 *
 * Depends on jQuery.
 *
 */

var functionsToTrigger = {load: true, unload:true, reloadAnswer:true, showViews: true, reloadState: true};

function taskCaller(task, request, content, error) {
   if (!error) {
      error = function() {};
   }
   // TODO: handle case where iframe_loaded is false and caller expects a result...
   if (!task.iframe_loaded) {
      setTimeout(function() {
         taskCaller(task, request, content);
      }, 100);
   } else {
      if (task.distantTask && typeof task.distantTask[request] === 'function') {
         if (typeof content === 'string' || (typeof content === 'object' && Object.prototype.toString.call(content) !== '[object Array]')) {
            content = [content];
         }
         var askedCallbackIdx = (typeof content[content.length - 2] === 'function') ? content.length - 2 : content.length - 1;
         if (typeof content[askedCallbackIdx] !== 'function') {
            error('calls to task must contain a callback');
            return;
         }
         var oldCallback = content[askedCallbackIdx];
         var timeout = window.setTimeout(function() {
            error('timeout reached for '+request);
         }, 20000);
         var newCallback = function() {
            window.clearTimeout(timeout);
            if (functionsToTrigger[request]) {
               task.distantPlatform.trigger(request, content);
            }
            oldCallback(arguments[0], arguments[1], arguments[3]);
         };
         content[askedCallbackIdx] = newCallback;
         if (typeof window.taskPrNoCatch !== 'undefined') {
            task.distantTask[request].apply(task.distantTask, content);
         } else {
            try {
               task.distantTask[request].apply(task.distantTask, content);
            } catch (e) {
               window.clearTimeout(timeout);
               error(e.name+': '+e.message);
            }
         }
      } else if (task.distantTask) {
         error("Task "+task.Id+" doesn't implement "+request);
      } else {
         error('call to '+request+' on task with no distant task');
      }
   }
}

/*
 * Task object, created from an iframe DOM element
 */
function Task(iframe, callback) {
   this.iframe = iframe;
   this.iframe_loaded = false;
   this.distantTask = null;
   this.Id = iframe.attr('id');
   this.elementsLoaded = false;
   this.platform = null;
   var that = this;
   this.setPlatform = function(platform) {
      this.platform = platform;
      if (this.iframe_loaded) {
         this.distantPlatform.setPlatform(platform);
      }
   };
   this.iframeLoaded = function() {
      if (that.iframe_loaded) {
         return;
      }
      that.iframe_loaded = true;
      that.distantTask = that.iframe[0].contentWindow.task;
      that.distantPlatform = that.iframe[0].contentWindow.platform;
      that.iframe[0].contentWindow.platform.parentLoadedFlag = true;
      if (that.platform) {
         that.distantPlatform.setPlatform(that.platform);
      }
      callback();    
   };
   // checking if platform is already available
   var iframeDoc = that.iframe[0].contentDocument || that.iframe[0].contentWindow.document;
   if (iframeDoc && iframeDoc.readyState  == 'complete' && that.iframe[0].contentWindow.platform && !that.iframe[0].contentWindow.platform.parentLoadedFlag) {
      that.iframe[0].contentWindow.platform.initCallback(that.iframeLoaded);
   } else {
      if(document.addEventListener) { // ie > 8
         $(that.iframe).on("load", function() {
            that.iframe[0].contentWindow.platform.initCallback(that.iframeLoaded);
         });
      } else {
         setTimeout(function() {
            that.iframeLoaded();
         }, 100);
      }
   }
}

Task.prototype.getSourceId = function() {
   return this.Id;
};

Task.prototype.getTargetUrl = function() {
   return this.iframe.attr('src');
};

Task.prototype.getTarget = function() {
   return this.iframe[0].contentWindow;
};

Task.prototype.getDomain = function() {
   var url = this.getTargetUrl();
   return url.substr(0, url.indexOf('/', 7));
};

/**
 * Task API functions
 */
Task.prototype.load = function(views, success, error) {
    return taskCaller(this, 'load', [views, success, error], error);
};
Task.prototype.unload = function(success, error) {
    return taskCaller(this, 'unload', [success, error], error);
};
Task.prototype.getHeight = function(success, error) {
    return taskCaller(this, 'getHeight', [success, error], error);
};
Task.prototype.updateToken = function(token, success, error) {
    return taskCaller(this, 'updateToken', [token, success, error], error);
};
Task.prototype.getAnswer = function(success, error) {
    return taskCaller(this, 'getAnswer', [success, error], error);
};
Task.prototype.reloadAnswer = function(answer, success, error) {
    return taskCaller(this, 'reloadAnswer', [answer, success, error], error);
};
Task.prototype.getState = function(success, error) {
    return taskCaller(this, 'getState', [success, error], error);
};
Task.prototype.reloadState = function(state, success, error) {
    return taskCaller(this, 'reloadState', [state, success, error], error);
};
Task.prototype.getViews = function(success, error) {
    return taskCaller(this, 'getViews', [success, error], error);
};
Task.prototype.getMetaData = function(success, error) {
    return taskCaller(this, 'getMetaData', [success, error], error);
};
Task.prototype.showViews = function(views, success, error) {
    return taskCaller(this, 'showViews', [views, success, error], error);
};
// TODO: put error back in argument list, for this 2015 tasks must be changed
Task.prototype.gradeAnswer = function(answer, answerToken, success, error) {
    return taskCaller(this, 'gradeAnswer', [answer, answerToken, success], error);
};
Task.prototype.getResources = function(success, error) {
    return taskCaller(this, 'getResources', [success, error], error);
};
// for grader.gradeTask
// TODO: put error back in argument list, for this 2015 tasks must be changed
Task.prototype.gradeTask = function(answer, answerToken, success, error) {
    return taskCaller(this, 'gradeTask', [answer, answerToken, success], error);
};

window.TaskProxyManager = {
   tasks: {},
   platforms: {},
   getTaskProxy: function(idFrame, callback, force) {
      if (!force && TaskProxyManager.tasks[idFrame]) {
         callback(TaskProxyManager.tasks[idFrame]);
      } else {
         if (force) {
            TaskProxyManager.deleteTaskProxy(idFrame);
         }
         $('#'+idFrame).each(function() {
            TaskProxyManager.tasks[idFrame] = new Task($(this), function() {setTimeout(function() {
               if (idFrame in TaskProxyManager.platforms) {
                  TaskProxyManager.tasks[idFrame].setPlatform(TaskProxyManager.platforms[idFrame]);
               }
               callback(TaskProxyManager.tasks[idFrame]);
            });});
         });
      }
   },
   setPlatform: function(task, platform) {
      TaskProxyManager.platforms[task.Id] = platform;
      TaskProxyManager.tasks[task.Id].setPlatform(platform);
   },
   deleteTaskProxy: function(idFrame) {
      delete(TaskProxyManager.tasks[idFrame]);
      delete(TaskProxyManager.platforms[idFrame]);
   },
   getUrl: function(taskUrl, sToken, sPlatform, prefix) {
      return taskUrl+'?sToken='+encodeURIComponent(sToken)+'&sPlatform='+encodeURIComponent(sPlatform);
   }
};

window.TaskProxyManager.getGraderProxy = TaskProxyManager.getTaskProxy;

}());

/*
 * Platform object definition, created from a Task object (see below)
 */

function Platform(task) {
   this.task = task;
}
Platform.prototype.getTask = function() {
   return this.task;
};

/* 
 * Simple prototypes for platform API functions, to be overriden by your
 * platform's specific functions (for each platform object)
 */

Platform.prototype.validate = function(mode) {};
Platform.prototype.showView = function(views) {};
Platform.prototype.askHint = function(platformToken) {};
Platform.prototype.updateHeight = function(height) {this.task.iframe.height(parseInt(height)+40);};
Platform.prototype.getTaskParams = function(key, defaultValue, success, error) {
   var res = {minScore: -3, maxScore: 10, randomSeed: 0, noScore: 0, readOnly: false, options: {}};
   if (key) {
      if (key !== 'options' && key in res) {
         res = res[key];
      } else if (res.options && key in res.options) {
         res = res.options[key];
      } else {
         res = (typeof defaultValue !== 'undefined') ? defaultValue : null;
      }
   }
   success(res);
};

Platform.prototype.openUrl = function(url) {
   // TODO
};
