/* Javascript for StaffGradedAssignmentXBlock. */
function StaffGradedAssignmentXBlock(runtime, element) {
  function xblock($, _) {
    var uploadUrl = runtime.handlerUrl(element, 'upload_assignment');
    var finalizeUploadUrl = runtime.handlerUrl(element, 'finalize_uploaded_assignment');
    var downloadUrl = runtime.handlerUrl(element, 'download_assignment');
    var annotatedUrl = runtime.handlerUrl(element, 'download_annotated');
    var getStaffGradingUrl = runtime.handlerUrl(
      element, 'get_staff_grading_data'
    );
    var staffDownloadUrl = runtime.handlerUrl(element, 'staff_download');
    var staffAnnotatedUrl = runtime.handlerUrl(
      element, 'staff_download_annotated'
    );
    var staffUploadUrl = runtime.handlerUrl(element, 'staff_upload_annotated');
    var enterGradeUrl = runtime.handlerUrl(element, 'enter_grade');
    var removeGradeUrl = runtime.handlerUrl(element, 'remove_grade');
    var downloadSubmissionsUrl = runtime.handlerUrl(element, 'download_submissions');
    var prepareDownloadSubmissionsUrl = runtime.handlerUrl(element, 'prepare_download_submissions');
    var downloadSubmissionsStatusUrl = runtime.handlerUrl(element, 'download_submissions_status');
    var template = _.template($(element).find("#sga-tmpl").text());
    var gradingTemplate;
    var preparingSubmissionsMsg = gettext(
      'Started preparing student submissions zip file. This may take a while.'
    );
    var currentIFrameHeight = null;
    var gradePopUpIsOpen = false;

    function render(state) {
      // Add download urls to template context
      state.downloadUrl = downloadUrl;
      state.annotatedUrl = annotatedUrl;
      state.error = state.error || false;

      // Render template
      var content = $(element).find('#sga-content').html(template(state));

      $(content).find('.finalize-upload').on('click', function () {
        $.post(finalizeUploadUrl).success(
          function (state) {
            render(state);
          }
        ).fail(
          function () {
            state.error = gettext('Submission failed. Please contact your course instructor.');
            render(state);
          }
        );
      });

      // Set up file upload
      var fileUpload = $(content).find('.fileupload').fileupload({
        url: uploadUrl,
        add: function (e, data) {
          var do_upload = $(content).find('.upload').html('');
          $(content).find('p.error').html('');
          do_upload.text(gettext('Uploading...'));
          var block = $(element).find(".sga-block");
          var data_max_size = block.attr("data-max-size");
          var size = data.files[0].size;
          if (!_.isUndefined(size)) {
            //if file size is larger max file size define in env(django)
            if (size >= data_max_size) {
              state.error = gettext('The file you are trying to upload is too large.');
              render(state);
              return;
            }
          }
          data.submit();
        },
        progressall: function (e, data) {
          var percent = parseInt(data.loaded / data.total * 100, 10);
          $(content).find('.upload').text(
            'Uploading... ' + percent + '%');
        },
        fail: function (e, data) {
          /**
           * Nginx and other sanely implemented servers return a
           * "413 Request entity too large" status code if an
           * upload exceeds its limit.  See the 'done' handler for
           * the not sane way that Django handles the same thing.
           */
          if (data.jqXHR.status === 413) {
            /* I guess we have no way of knowing what the limit is
             * here, so no good way to inform the user of what the
             * limit is.
             */
            state.error = gettext('The file you are trying to upload is too large.');
          } else {
            // Suitably vague
            state.error = gettext('There was an error uploading your file.');

            // Dump some information to the console to help someone
            // debug.
            console.log('There was an error with file upload.');
            console.log('event: ', e);
            console.log('data: ', data);
          }
          render(state);
        },
        done: function (e, data) {
          /* When you try to upload a file that exceeds Django's size
           * limit for file uploads, Django helpfully returns a 200 OK
           * response with a JSON payload of the form:
           *
           *   {'success': '<error message'}
           *
           * Thanks Obama!
           */
          if (data.result.success !== undefined) {
            // Actually, this is an error
            state.error = data.result.success;
            render(state);
          } else {
            // The happy path, no errors
            render(data.result);
          }
        }
      });

      updateChangeEvent(fileUpload);
      if (state.error) {
        $(content).find('p.error').focus();
      }
    }

    function renderStaffGrading(data) {
      if (data.hasOwnProperty('error')) {
        gradeFormError(data['error']);
      } else {
        gradeFormError('');
        $('.grade-modal').hide();
      }

      if (data.display_name !== '') {
        $('.sga-block .display_name').html(data.display_name);
      }

      // Add download urls to template context
      data.downloadUrl = staffDownloadUrl;
      data.annotatedUrl = staffAnnotatedUrl;

      // Render template
      $(element).find('#grade-info')
        .html(gradingTemplate(data))
        .data(data);

      // Map data to table rows
      data.assignments.map(function (assignment) {
        $(element).find('#grade-info #row-' + assignment.module_id).data(assignment);
      });

      // Set up grade entry modal
      $(element).find('.enter-grade-button')
        .leanModal({ closeButton: '#enter-grade-cancel' })
        .on('click', handleGradeEntry);

      // Set up annotated file upload
      $(element).find('#grade-info .fileupload').each(function () {
        var row = $(this).parents("tr");
        var url = staffUploadUrl + "?module_id=" + row.data("module_id");
        var fileUpload = $(this).fileupload({
          url: url,
          progressall: function (e, data) {
            var percent = parseInt(data.loaded / data.total * 100, 10);
            row.find('.upload').text(interpolate(gettext('Uploading... %(percent)s %'), { percent: percent }, true));
          },
          done: function (e, data) {
            // Add a time delay so user will notice upload finishing
            // for small files
            setTimeout(
              function () { renderStaffGrading(data.result); },
              3000);
          }
        });

        updateChangeEvent(fileUpload);
      });
      $.tablesorter.addParser({
        id: 'alphanum',
        is: function (s) {
          return false;
        },
        format: function (s) {
          var str = s.replace(/(\d{1,2})/g, function (a) {
            return pad(a);
          });

          return str;
        },
        type: 'text'
      });

      $.tablesorter.addParser({
        id: 'yesno',
        is: function (s) {
          return false;
        },
        format: function (s) {
          return s.toLowerCase().trim() === gettext('yes') ? 1 : 0;
        },
        type: 'text'
      });

      function pad(num) {
        var s = '00000' + num;
        return s.substr(s.length - 5);
      }
      $("#submissions").tablesorter({
        headers: {
          2: { sorter: "alphanum" },
          3: { sorter: "alphanum" },
          4: { sorter: "yesno" },
          7: { sorter: "alphanum" }
        }
      });
      $("#submissions").trigger("update");
      var sorting = [[4, 1], [1, 0]];
      $("#submissions").trigger("sorton", [sorting]);
    }

    function isStaff() {
      return $(element).find('.sga-block').attr('data-staff') === 'True';
    }

    /* Just show error on enter grade dialog */
    function gradeFormError(error) {
      var form = $(element).find("#enter-grade-form");
      form.find('.error').html(error);
    }

    /* Click event handler for "enter grade" */
    function handleGradeEntry() {
      var row = $(this).parents("tr");
      var form = $(element).find("#enter-grade-form");
      $(element).find('#student-name').text(row.data('fullname'));
      form.find('#module_id-input').val(row.data('module_id'));
      form.find('#submission_id-input').val(row.data('submission_id'));
      form.find('#grade-input').val(row.data('score'));
      form.find('#comment-input').text(row.data('comment'));
      form.find('#remove-grade').prop('disabled', false);
      form.find('.ccx-enter-grade-spinner').hide();
      form.off('submit').on('submit', function (event) {
        var max_score = row.parents('#grade-info').data('max_score');
        var score = Number(form.find('#grade-input').val());
        event.preventDefault();
        if (isNaN(score)) {
          gradeFormError('<br/>' + gettext('Grade must be a number.'));
        } else if (score !== parseInt(score)) {
          gradeFormError('<br/>' + gettext('Grade must be an integer.'));
        } else if (score < 0) {
          gradeFormError('<br/>' + gettext('Grade must be positive.'));
        } else if (score > max_score) {
          gradeFormError('<br/>' + interpolate(gettext('Maximum score is %(max_score)s'), { max_score: max_score }, true));
        } else {
          // No errors
          form.find('.ccx-enter-grade-spinner').show();
          $.post(enterGradeUrl, form.serialize())
            .success(renderStaffGrading)
            .fail(function () {
              form.find('.ccx-enter-grade-spinner').hide();
            });
        }
      });
      form.find('#remove-grade').off('click').on('click', function (event) {
        $(this).prop('disabled', true);
        form.find('.ccx-enter-grade-spinner').show();
        var url = removeGradeUrl + '?module_id=' +
          row.data('module_id') + '&student_id=' +
          row.data('student_id');
        event.preventDefault();
        if (row.data('score')) {
          // if there is no grade then it is pointless to call api.
          $.get(url).success(renderStaffGrading).fail(function () {
            $(this).prop('disabled', false);
            form.find('.ccx-enter-grade-spinner').hide();
          });
        } else {
          gradeFormError('<br/>' + gettext('No grade to remove.'));
        }
      });
      form.find('#enter-grade-cancel').on('click', function () {
        /* We're kind of stretching the limits of leanModal, here,
         * by nesting modals one on top of the other.  One side effect
         * is that when the enter grade modal is closed, it hides
         * the overlay for itself and for the staff grading modal,
         * so the overlay is no longer present to click on to close
         * the staff grading modal.  Since leanModal uses a fade out
         * time of 200ms to hide the overlay, our work around is to
         * wait 225ms and then just "click" the 'Grade Submissions'
         * button again.  It would also probably be pretty
         * straightforward to submit a patch to leanModal so that it
         * would work properly with nested modals.
         *
         * See: https://github.com/mitodl/edx-sga/issues/13
         */
        setTimeout(function () {
          $('#grade-submissions-button').click();
          gradeFormError('');
          gradePopUpIsOpen = false
        }, 225);
      });
      gradePopUpIsOpen = true;
    }

    function updateChangeEvent(fileUploadObj) {
      fileUploadObj.off('change').on('change', function (e) {
        var that = $(this).data('blueimpFileupload'),
          data = {
            fileInput: $(e.target),
            form: $(e.target.form)
          };

        that._getFileInputFiles(data.fileInput).always(function (files) {
          data.files = files;
          if (that.options.replaceFileInput) {
            that._replaceFileInput(data.fileInput);
          }
          that._onAdd(e, data);
        });
      });
    }

    $(function ($) { // onLoad
      var block = $(element).find('.sga-block');
      var state = block.attr('data-state');
      var parsedState = JSON.parse(state);
      render(parsedState);

      var is_staff = isStaff();
      if (is_staff) {
        gradingTemplate = _.template(
          $(element).find('#sga-grading-tmpl').text());
        block.find('#grade-submissions-button')
          .leanModal()
          .on('click', function () {
            updateIframe()
            $.ajax({
              url: getStaffGradingUrl,
              success: renderStaffGrading
            });
          });
        block.find('#staff-debug-info-button')
          .leanModal();

        $(element).find('#download-init-button').click(function (e) {
          e.preventDefault();
          var self = this;
          $.get(prepareDownloadSubmissionsUrl).then(
            function (data) {
              if (data["downloadable"]) {
                window.location = downloadSubmissionsUrl;
                $(self).removeClass("disabled");
              } else {
                $(self).addClass("disabled");
                $(element).find('.task-message')
                  .show()
                  .html(preparingSubmissionsMsg)
                  .removeClass("ready-msg")
                  .addClass("preparing-msg");
                pollSubmissionDownload();
              }
            }
          ).fail(
            function () {
              $(self).removeClass("disabled");
              $(element).find('.task-message')
                .show()
                .html(
                  interpolate(
                    gettext(
                      'The download file was not created. Please try again or contact %(support_email)s'
                    ),
                    { support_email: $(element).find('.sga-block').attr("data-support-email") },
                    true
                  )
                )
                .removeClass("preparing-msg")
                .addClass("ready-msg");
            }
          );
        });
      }
      $("#lean_overlay").on("click", function (_) {
        if (currentIFrameHeight && !gradePopUpIsOpen) {
          sendResizeMessage(currentIFrameHeight)
          currentIFrameHeight = null
        }
      })
    });

    function updateIframe() {
      if (isInIframe && !currentIFrameHeight) {
        currentIFrameHeight = $("body").height()
        addMaxHeightInIframe()
        if (currentIFrameHeight < 600) {
          sendResizeMessage(600)
        }
      }
    }

    function isInIframe() {
      return window.parent !== window
    }

    function addMaxHeightInIframe() {
      $(".grade-submission").css("max-height", "600px")
    }


    function sendResizeMessage(height) {
      // This blocks checks to see if the xBlock is part 
      // of Learning MFE
      if (window.parent !== window) {
        window.parent.postMessage({
          type: 'plugin.resize',
          payload: {
            height: height,
          }
        }, document.referrer
        );
      }
    }

    function pollSubmissionDownload() {
      pollUntilSuccess(downloadSubmissionsStatusUrl, checkResponse, 10000, 100).then(function () {
        $(element).find('#download-init-button').removeClass("disabled");
        $(element).find('.task-message')
          .show()
          .html(gettext("Student submission file ready for download"))
          .removeClass("preparing-msg")
          .addClass("ready-msg");
      }).fail(function () {
        $(element).find('#download-init-button').removeClass("disabled");
        $(element).find('.task-message')
          .show()
          .html(
            interpolate(
              gettext(
                'The download file was not created. Please try again or contact %(support_email)s'
              ),
              { support_email: $(element).find('.sga-block').attr("data-support-email") },
              true
            )
          );
      });
    }
  }

  function checkResponse(response) {
    return response["zip_available"];
  }

  function pollUntilSuccess(url, checkSuccessFn, intervalMs, maxTries) {
    var deferred = $.Deferred(),
      tries = 1;

    function makeLoopingRequest() {
      $.get(url).success(function (response) {
        if (checkSuccessFn(response)) {
          deferred.resolve(response);
        } else if (tries < maxTries) {
          tries++;
          setTimeout(makeLoopingRequest, intervalMs);
        } else {
          deferred.reject('Max tries exceeded.');
        }
      }).fail(function (err) {
        deferred.reject('Request failed:\n' + err.responseText);
      });
    }
    makeLoopingRequest();

    return deferred.promise();
  }

  function loadjs(url) {
    $('<script>')
      .attr('type', 'text/javascript')
      .attr('src', url)
      .appendTo(element);
  }

  if (require === undefined) {
    /**
     * The LMS does not use require.js (although it loads it...) and
     * does not already load jquery.fileupload.  (It looks like it uses
     * jquery.ajaxfileupload instead.  But our XBlock uses
     * jquery.fileupload.
     */
    loadjs('/static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport.js');
    loadjs('/static/js/vendor/jQuery-File-Upload/js/jquery.fileupload.js');
    xblock($, _);
  } else {
    /**
     * Studio, on the other hand, uses require.js and already knows about
     * jquery.fileupload.
     */
    require(['jquery', 'underscore', 'jquery.fileupload'], xblock);
  }
}
