/*

  GridCell class wraps and provides functionality to a data cell in the grade center.
  
  Each HTML cell controller will contain a GridCell to allow manipulating the data cell 
  that is currently assigned to it.
  When data cells are retrieved for processing they are wrapped in a GridCell.
 
  brichard
*/


Gradebook.GridCell = Class.create();

Gradebook.GridCell.prototype = {
  initialize: function( data ) {
    if (data) this.setData(data);
  },

  setData: function(data) {
    this.data = data;
    this.colDef = data.colDef;
    this.metaData = data.metaData;
    if (this.colDef.id == 'UN'){
      this.metaData.userNameDataCell = data;
    }
  },

  passesFilter: function( f ) {
    var ng = this.needsGrading();
    var ip = this.attemptInProgress();
    var or = this.isOverride();
    var x = this.isExempt();
    var sv = this.colDef.getSortValue( this );
    var svn = sv == '-';
    var svnn = sv != '-';
    var na = svn && !ip && !ng;
    var c = !ip && !ng && !x && svnn;
    var nn = ip || ng || or ||  svnn;
    if (f == 'IP')         return ip;
    else if (f == 'NG')    return ng && !or;
    else if (f == 'EM')    return or;
    else if (f == 'X')     return x;
    else if (f == "NA")    return na;   // notAttempted
    else if (f == "NN")    return nn;   // not null
    else if (f == "C")     return c || or;    // completed/graded
    else              return true; // all 
  },
  
  getUserId: function() {
    return this.metaData.uid;
  },
  
  getKey: function() {
    this.colDef.id + '_' + this.metaData.uid;
  },

  getUserName: function() {
    return this.metaData.userNameDataCell.v;
  },

  isHidden: function() {
    return this.metaData.isHidden;
  },
  
  setHidden: function( h ) {
    this.metaData.isHidden = h;
  },
  
  isRowChecked: function() {
    return this.metaData.isRowChecked;
  },
  
  isExcluded: function() {
    return this.data.excluded || ( this.colDef.limitedAttendance && !this.isPersisted() ) ;
  },
  
  isPersisted: function() {
    return ("v" in this.data );
  },
  
  setRowChecked: function(c) {
    this.metaData.isRowChecked = c;
  },
  
  isAvailable: function() {
    return this.metaData.isAvailable;
  },

  isGrade: function() {
    return (this.colDef.isGrade());
  },
  
  isOverride: function() {
    return (this.data.or && this.data.or == "y" && !this.colDef.isCalculated() );
  },
  
  needsGrading: function() {
    return (this.data.ng != null && this.data.ng && this.data.ng == "y");
  },
  
  attemptInProgress: function() {
    return (this.data.ip != null && this.data.ip && this.data.ip == "y");
  },

  isGraded: function() {
    var tv = this.getTextValue();
    return (tv != '-' && tv.length > 0);
  },
  
  isComplete: function() {
    if (this.colDef.primarySchema instanceof Gradebook.CompleteIncompleteSchema){
      return this.isGraded();
    }else{      
      return false;
    }
  },

  isExempt: function() {
    return (this.data.x && this.data.x == "y");
  },
  
  hasMultipleAttempts: function() {
    return (this.data.numAtt && this.data.numAtt == "M");
  },

  hasOneAttempt: function() {
    return (!this.data.numAtt || this.data.numAtt == "1");
  },
  
  hasAttempts: function() {
    return this.hasOneAttempt() || this.hasMultipleAttempts();
  },

  validate: function(newValue, matchPartial) {
    return this.colDef.validate(newValue, matchPartial);
  },
  
  update: function(newValue) {
    this.colDef.updateGrade( newValue, this.getUserId() );
  },
  
  clearAll: function( isDelete ) {
    this.colDef.model.clearAll( isDelete, this.getUserId(), this.colDef.id );
  },
  
  clearSelected: function( attemptIds, isDelete ) {
    this.colDef.model.clearSelected( attemptIds, isDelete, this.getUserId(), this.colDef.id );
  },

  // called by CellController.renderHTML to get value for spreadsheet
  getCellValue: function() {
    return this.colDef.getCellValue( this );
  },

  // called by GridCell.getAltValue to get alt (mouse over) value for rendering in spreadsheet
  getAltValue: function(){
      if ( this.isGrade() && !this.isGraded() ) return GradebookUtil.getMessage('noGradeMsg');
    return this.colDef.getAltValue( this );
  },

  // called by CellController.startEdit to get input value for editing
  getEditValue: function() {
    return this.colDef.getEditValue( this );
  },

  getSortValue: function() {
    return this.colDef.getSortValue( this );
  },

  getPointsPossible: function() {
    if (this.data.mp){
      return this.data.mp;
    } else if (this.colDef.points){
      return this.colDef.points;
    } else {
      return 0;
    }
  },

  getTextValue: function() {
    if (this.data.tv) {
      return this.data.tv;
    } else {
      return '-';
    }
  },

  getValue: function() {
    if (this.data.v!==undefined && this.data.v!=null) {
      return this.data.v;
    } else {
      return '-';
    }
  },
  
  canEdit: function(){
    return (this.isGrade() && !this.isExcluded() && !this.colDef.isCalculated() && !this.colDef.isHideAttemptScore());
  },

  showGradeDetails: function( evt ){
    if ( evt ) Event.stop( evt );
    this.colDef.showGradeDetails( this.getUserId() );
  },
  
  onAddComment: function(evt){
    this.cellController.addGradeComment(evt, this);
    this.colDef.onAddComment( this.getUserId() );
  },
  
  exemptGrade: function(evt){
    Event.stop( evt );
    this.cellController.stopEdit(false, true);
    this.colDef.exemptGrade( this.getUserId(), this );
    this.cellController.closePopupsAndRestoreFocus(evt)
  },
  
  clearExemption: function(evt){
    Event.stop( evt );
    this.colDef.clearExemption( this.getUserId() );
    this.cellController.closePopupsAndRestoreFocus(evt);
  },
  
  setComments: function(studentComments, instructorComments){
    this.colDef.setComments( this.getUserId(), studentComments, instructorComments );
  },
  
  loadAttemptsInfo: function( callbackFunction ) {
    var currentCell = this;
    this.colDef.model.gradebookService.loadAttemptsInfo( this.getUserId(), this.colDef.id,
                                                     function( attempts ) { currentCell.loadAttemptsInfoCallback.call( currentCell, attempts, callbackFunction ) } );
  },
  
  loadAttemptsInfoCallback: function( attempts, callbackFunction ) {
    this.data.attemptsInfo = new Array();
    for ( var i=0; i<attempts.length; ++i) { 
      this.data.attemptsInfo.push( new Gradebook.AttemptInfo( this, attempts[i] ) ); 
    }
    callbackFunction( this );
  },
  
  getMenuDynItems: function()
  {
    var dynItems = new Array();
    var gradeCell = this;
    for ( var i=0; i<this.data.attemptsInfo.length; ++i )
    {
      var attemptId = gradeCell.data.attemptsInfo[i].id;
      var groupAttemptId = gradeCell.data.attemptsInfo[i].groupAttemptId;
      // note that we cannot create a function as a direct closure here
      // since it would rely on this function scope which actually changes
      // as we iterate i.e. all functions will point to the same scope which ends
      // up being the scope as at the last iteration.
      // To 'freeze' the scope we create a new local scope calling another
      // function using current parameters.
      dynItems.push( { id: "attemptDynItem",
        name: this.data.attemptsInfo[i].getText(),
        onclick: this.getGotoAttemptFunction(  attemptId, groupAttemptId) } );
    }  
    return dynItems;
  },
  
  getGotoAttemptFunction: function( attemptId, groupAttemptId )
  {
    var gradeCell = this;
    var currentAttemptId = attemptId;
    var currentGroupAttemptId = groupAttemptId;
    return function() { gradeCell.gotoAttempt.call( gradeCell, attemptId, groupAttemptId ) };
  },
  
  
  gotoAttempt: function( attemptId, groupAttemptId ) {
    if ( this.colDef.groupActivity && !groupAttemptId ) {
      this.showGradeDetails();
      return;
    }
    var scoreProvider = this.colDef.getScoreProvider();
    var attemptURL = ( scoreProvider?scoreProvider.gradeAction:this.colDef.extAttemptHandler );
    var firstSep = '&';
    if (attemptURL.indexOf('?') == -1) {
      firstSep = '?';
    }
    attemptURL = attemptURL.concat(
                firstSep,
                "course_id=", this.colDef.model.courseId,
                "&outcomeDefinitionId=", this.colDef.id,
                "&courseMembershipId=", this.getUserId(),
                "&attempt_id=", attemptId?attemptId:"",
                "&cancelGradeUrl=/webapps/gradebook/do/instructor/enterGradeCenter&source=cp_gradebook&linkRefId=", this.colDef.linkrefid );
    if ( groupAttemptId ) attemptURL += ( "&group_attempt_id=" + groupAttemptId );
    this.colDef.model.gradebookService.gotoURL( attemptURL );
  },
  
  gotoActivity: function() {
    this.gotoAttempt();
  },
  
  hasContextMenuInfo: function(cellController) {
    if (this.isGrade())
    {
      return !this.isExcluded() && !this.colDef.isCalculated();
    } 
    else
    {
      return true;
    }
  },
  
  getContextMenuInfo: function(cellController) {
    if (this.isGrade())
    {
      return this.getGradeContextMenuInfo(cellController);
    } 
    else
    {
      return this.getStudentContextMenuInfo(cellController);
    }
  },
  
  getGradeContextMenuInfo: function(cellController) {
    if (this.isExcluded() || this.colDef.isCalculated()){
      return null;
    }
    this.cellController = cellController;
    // Can add comments for non-null user created column grades & 
    // system column grades that have been overridden
    var isUserCreated = this.colDef.isUserCreated();
    var isExempt = this.isExempt();
    var canAddComment = (isUserCreated && this.isPersisted() && this.data.v != '-') || (!isUserCreated && this.isOverride()) || isExempt;
    var isActivity = this.colDef.src && !this.colDef.getScoreProvider().attemptBased;
    var menu = {
      id: "gradeCM",
      items: [
        {id: "g_360View", visible:true,
          onclick: this.showGradeDetails.bindAsEventListener(this)},
        {id: "g_addComment", visible: canAddComment,
          onclick: this.onAddComment.bindAsEventListener(this)},
        {id: "g_exemptGrade", visible: !isExempt,
          onclick: this.exemptGrade.bindAsEventListener(this)},
        {id: "g_clearExemption", visible: isExempt,
          onclick: this.clearExemption.bindAsEventListener(this)},
          {id: "g_viewActivity", visible: isActivity,
            onclick: this.gotoActivity.bindAsEventListener(this)}
        ]};
    if ( !isActivity && ( this.colDef.src || this.colDef.extAttemptHandler ) && ( this.hasAttempts() || this.data.ax /* has exempted attempt */ )) {
      if ( this.data.attemptsInfo ) {
        menu.dynItems = this.getMenuDynItems();
      } else {
        menu.dynAppender = this.loadAttemptsInfo.bindAsEventListener(this);
      }
    }
    return menu;      
  },
  
  getStudentContextMenuInfo: function(cellController) {
    this.cellController = cellController;
    var menu = {
      id: "studentInfoCM",
      items: [
        {id: "si_studentHideOtherStudents", visible:this.colDef.model.isolatedStudentId?false:true,
          onclick: this.onHideOtherStudents.bindAsEventListener(this)},
          {id: "si_showAllRows", visible:this.colDef.model.isolatedStudentId?true:false,
            onclick: this.onShowAllRows.bindAsEventListener(this)},
        {id: "si_studentStats", visible:true,
          onclick: this.onStudentStats.bindAsEventListener(this)},
        {id: "si_adaptiveReleaseColumn", visible:true,
          onclick: this.onAdaptiveReleaseUser.bindAsEventListener(this)},
        {id: "si_sendEmail", visible:true,
          onclick: this.onSendEmail.bindAsEventListener(this)},
        {id: "si_hideUser", visible:!this.isHidden(),
          onclick: this.onHideUser.bindAsEventListener(this),
          receipt: 'hideStudentInlineMsg'},
        {id: "si_showUser", visible:this.isHidden(),
          onclick: this.onShowUser.bindAsEventListener(this)}
        ]};
    return menu;      
  },
  
  onSendEmail: function( evt ){
    Event.stop( evt );
    var ids = new Array();
    ids[0] = this.getUserId();
    this.cellController.sendEmail('S',ids);
  },
  
  onShowUser: function( evt ){
    Event.stop( evt );
      this.colDef.updateUserVisibility( this.getUserId(), true );
  },
    
  onHideOtherStudents: function( evt ){
    Event.stop( evt );
      this.colDef.model.viewSingleStudentGrades( this.getUserId() );
  },
  
  onShowAllRows: function( evt ){
    Event.stop( evt );
      this.colDef.model.restoreFromSingleStudentView();
  },
  
  onStudentStats: function( evt ){
    Event.stop( evt );
      this.colDef.model.viewStudentStats( this.getUserId() );
  },
    
  onHideUser: function( evt ){
    Event.stop( evt );
      this.colDef.updateUserVisibility( this.getUserId(), false );
  },
  
  onAdaptiveReleaseUser: function( evt ){
    Event.stop( evt );
      this.colDef.model.viewAdaptiveRelease( this.getUserName() );
  }
};

Gradebook.AttemptInfo = Class.create();

Object.extend( Gradebook.AttemptInfo.prototype, { 
  
  initialize: function( gradeCel, attemptData ) {
    this.gradeCel = gradeCel;
      this.id = attemptData.id;
      this.date = attemptData.date;
    this.score = attemptData.score;
    this.status = attemptData.status;
    this.exempt = attemptData.exempt;
    if ( attemptData.groupAttemptId )
    {
      this.groupAttemptId = attemptData.groupAttemptId;
      this.groupName = attemptData.groupName;
      this.override = attemptData.override;
      this.groupScore = attemptData.groupScore;
      this.groupStatus = attemptData.groupStatus;
    }
  },
  
  getScoreDisplayValue: function() {
    if ( this.status )
    {
      if ( this.status == "ip" ) 
        return this.gradeCel.colDef.model.gridImages.attemptInProgress;
      return this.gradeCel.colDef.model.gridImages.needsGrading;
    }
    var primaryValue = this.gradeCel.colDef.getDisplayValue( this.score );
    var secondaryValue = this.gradeCel.colDef.getSecondaryDisplayValue( this.score );
    if ( secondaryValue ) primaryValue += " ("+secondaryValue+")";
    return primaryValue; 
  },
  
  getGroupScoreDisplayValue: function() {
    if ( this.groupStatus )
    {
      if ( this.groupStatus == "ip" ) 
        return this.gradeCel.colDef.model.gridImages.attemptInProgress;
      return this.gradeCel.colDef.model.gridImages.needsGrading;
    }
    var primaryValue = this.gradeCel.colDef.getDisplayValue( this.groupScore );
    var secondaryValue = this.gradeCel.colDef.getSecondaryDisplayValue( this.groupScore );
    if ( secondaryValue ) primaryValue += " ("+secondaryValue+")";
    return primaryValue; 
  },
  
  getText: function( ) {
    var exemptIcon = "";
    if ( this.exempt )
    {
      var altText = this.gradeCel.colDef.model.getMessage( 'exemptAttemptMsg' );
      exemptIcon = "<img src='/images/ci/gradebook/exempt.gif' alt='" + altText + "' title='" + altText + "'>";
    }
    if ( !this.groupAttemptId )
    {
      if ( ! Gradebook.GridCell.attemptTemplate ) Gradebook.GridCell.attemptTemplate = new Template( this.gradeCel.colDef.model.getMessage( 'attemptInfoMsg' ) );
      return Gradebook.GridCell.attemptTemplate.evaluate( { date:this.date, score:this.getScoreDisplayValue(), exempt:exemptIcon } );
    }
    if ( !this.override )
    {
      if ( ! Gradebook.GridCell.groupAttemptTemplate ) Gradebook.GridCell.groupAttemptTemplate = new Template( this.gradeCel.colDef.model.getMessage( 'groupAttemptInfoMsg' ) );
      return Gradebook.GridCell.groupAttemptTemplate.evaluate( { date:this.date, score:this.getScoreDisplayValue(), groupName:this.groupName, exempt:exemptIcon } );
    }
    if ( ! Gradebook.GridCell.groupAttemptOverrideTemplate ) Gradebook.GridCell.groupAttemptOverrideTemplate = new Template( this.gradeCel.colDef.model.getMessage( 'groupAttemptInfoWithOverrideMsg' ) );
    return Gradebook.GridCell.groupAttemptOverrideTemplate.evaluate( { date:this.date, score:this.getScoreDisplayValue(), groupName:this.groupName, groupScore:this.getGroupScoreDisplayValue() , exempt:exemptIcon } );
  }
  
} );

