Numbas.addExtension('postmessage-to-parent',['jme'],function(extension) {
  var funcObj = Numbas.jme.funcObj;
  var TDict = Numbas.jme.types.TDict;
  var TString = Numbas.jme.types.TString;
  var TNothing = Numbas.jme.types.TNothing;
  var TAny = Numbas.jme.types.TAny;
  var frameID = '';

  // Add a post_messsage JME function
  extension.scope.addFunction(new funcObj('post_message',['?', TDict],'?',null,{
    evaluate: function(args, scope) {
      var options = args[1].value;
      var postmsg_opts = {
        "origin": "*"
      };
      var data = {
        "message": args[0].value,
        "frame_id": frameID
      };
      Object.keys(options).forEach(function (k) { postmsg_opts[k] = options[k].value; });
      setTimeout(function(){
        window.top.postMessage(JSON.stringify(data), postmsg_opts["origin"]);
      },0);
      return args[0];
    }
  }));
  
  // Send exam data message
  function sendExamData(message){
    var data = {
      "message": message,
      "frame_id": frameID,
      "numbas_version": "TBC",
      //"exam": {
      //  "marks": Numbas.exam.mark
      //}
    };
    setTimeout(function(){
      window.top.postMessage(JSON.stringify(data), "*");
    },0);
  }
  
  // Hook the submit function for all question parts so that a message is posted to the outer window
  // with information whenever a part is submitted
  Numbas.signals.on('exam ready',function() {
    sendExamData("exam_ready");
    Numbas.exam.questionList.forEach(function (question) {
      question.allParts().forEach(function(p) {
        var oldsubmit = p.submit;
        p.submit = function(){
          var scope = p.scope;
          var path = scope.variables.part_path.value;
          var part;
          Object.keys(scope.question.parts).forEach(function(x){
            part = scope.question.parts[x].path === path ? scope.question.parts[x]: part;
          });
          setTimeout(function(){
            var question_data = {
              "number": scope.question.number,
              "marks": scope.question.marks,
              "score": scope.question.score,
            };
            var exam_data = {
              "marks": Numbas.exam.mark,
              "score": Numbas.exam.score,
              "percent": Numbas.exam.percentScore
            };
            var part_data = {
              "marks": part.marks,
              "score": part.score,
              "feedback": part.markingFeedback,
              "name": part.name,
              "path": part.path,
              "studentAnswer": part.stagedAnswer
            };
            var data = {
              "message": "part_answered",
              "exam": exam_data,
              "question": question_data,
              "part": part_data,
              "frame_id": frameID
            };
            window.top.postMessage(JSON.stringify(data), "*");
          },0);
          oldsubmit.apply(p);
        };
      });
    });
  });
  


  // Post height update messages whenever the height of this window changes
  function onElementHeightChange(elm, callback){
    var lastHeight = elm.clientHeight, newHeight;
    (function run(){
      newHeight = elm.clientHeight;
      if( lastHeight != newHeight ) callback();
      lastHeight = newHeight;
      if( elm.onElementHeightChangeTimer )
        clearTimeout(elm.onElementHeightChangeTimer);
      elm.onElementHeightChangeTimer = setTimeout(run, 200);
     })();
  }
  onElementHeightChange(document.body, function(){
    var documentHeight = Math.max(
      document.body.scrollHeight,
      document.documentElement.scrollHeight,
      document.body.offsetHeight,
      document.documentElement.offsetHeight,
      document.body.clientHeight,
      document.documentElement.clientHeight
    );
    var data = {
      "message": "height_changed",
      "documentHeight":documentHeight,
      "frame_id": frameID
    };
    window.top.postMessage(JSON.stringify(data), "*");
  });

  // Listen for an identifying key from the outer window and replay it back in future messages.
  // This allows the outer frame to identify this one, even with cross-origin restrictions in place
  window.addEventListener('message', function(event){
    var data = JSON.parse(event.data);
    if('message' in data) {
      switch(data['message']){
        case 'send_id':
          frameID = data['id'];
          sendExamData("exam_data");
          break;
      }
    }
  });
});