/* ######################################################################### */
/** Class to manipulate html */
var Html = function(columnCount) {
  /* #############################  INSTANCE VARIABLES  ###################### */
  this.columnCount = columnCount;
  /** Map sort icons to sort direction */
  this.sortIcon = {
    '1': 'fas fa-sort-up',
    '0': 'fas fa-sort',
    '-1': 'fas fa-sort-down'
  };

  /* #################################  FUNCTIONS  ########################### */

  /* ################################  TABLE STUFF  ########################## */

  /**
   * Draw table cells for all given sequences
   * @param {object} sequences sequences to be drawn
   */
  this.drawTable = function(sequences) {
    var res = '';
    var self = this;
    sequences.forEach(function(seq) {
      res +=
        '<tr>' +
        self.drawQueryCell(seq) +
        self.drawLengthCell(seq) +
        self.drawOrganismCell(seq) +
        self.drawGeneCell(seq) +
        self.drawGOCell(seq) +
        self.drawBpocCell(seq.nonviral_adhesion) +
        self.drawBpocCell(seq.viral_adhesion) +
        self.drawBpocCell(seq.antibiotic_resistance) +
        self.drawBpocCell(seq.avirulence_plant) +
        self.drawBpocCell(seq.counter_immunoglobulin) +
        self.drawBpocCell(seq.cytotoxicity) +
        self.drawBpocCell(seq.develop_in_host) +
        self.drawBpocCell(seq.disable_organ) +
        self.drawBpocCell(seq.plant_rna_silencing) +
        self.drawBpocCell(seq.induce_inflammation) +
        self.drawBpocCell(seq.nonviral_invasion) +
        self.drawBpocCell(seq.viral_invasion) +
        self.drawBpocCell(seq.degrade_ecm) +
        self.drawBpocCell(seq.host_cytoskeleton) +
        self.drawBpocCell(seq.host_cell_cycle) +
        self.drawBpocCell(seq.host_cell_death) +
        self.drawBpocCell(seq.host_gtpase) +
        self.drawBpocCell(seq.host_transcription) +
        self.drawBpocCell(seq.host_translation) +
        self.drawBpocCell(seq.host_ubiquitin) +
        self.drawBpocCell(seq.host_xenophagy) +
        self.drawBpocCell(seq.nonviral_movement) +
        self.drawBpocCell(seq.viral_movement) +
        self.drawBpocCell(seq.toxin_synthase) +
        self.drawBpocCell(seq.resist_complement) +
        self.drawBpocCell(seq.resist_oxidative) +
        self.drawBpocCell(seq.secreted_effector) +
        self.drawBpocCell(seq.secretion) +
        self.drawBpocCell(seq.suppress_detection) +
        self.drawBpocCell(seq.bacterial_counter_signaling) +
        self.drawBpocCell(seq.viral_counter_signaling) +
        self.drawBpocCell(seq.virulence_regulator) +
        self.drawBpocCell(seq.virulence_activity) +
        '</tr>' +
        self.drawCollapsible(seq)+
        self.drawGOTermCollapsible(seq);
    });

    $('#results-table').html(res);
  };

  /** Draw query cell for sequence with optional link to alignment modal */
  this.drawQueryCell = function(seq) {
    var a = seq.query;
    if (seq.hasAlignment == 'true') {
      a =
        '<a href="#" data-toggle="modal" data-target="#alignment" data-seq="' +
        a + "-" + seq.qID +
        '">' +
        a +
        '</a>';
    }

    return (
      '<td class="align-middle wrap" style="overflow-wrap: break-word;">' +
      a +
      '<span title="Show Sequence" class="faArrowIcon collapsed" ' +
      'data-toggle="collapse" data-target="#accordion-' +
      seq.qID +
      '"></span>'
    );
  };

  /** Draw length cell for sequence */
  this.drawLengthCell = function(seq) {
    return '<td class="align-middle centeredText wrap">' + seq.length + '</td>';
  };

  /** Draw organism cell for sequence */
  this.drawOrganismCell = function(seq) {
    return '<td class="align-middle centeredText wrap">' + seq.taxID + '</td>';
  };

  /** Draw gene cell for sequence */
  this.drawGeneCell = function(seq) {
    return '<td class="align-middle wrap">' + seq.geneName + '</td>';
  };

  /** Draw goTerm cell with first 3 go terms and link to visualization page*/
  this.drawGOCell = function(seq) {

    result =''
    if (seq.go_terms) {
      var goTerms = '<ol>';
      goTerms += seq.go_terms.slice(0, 3).reduce(function(res, term, idx) {
        return (res += '<li>'+ term.name + '</li>');
      }, '');
      goTerms +="</ol>"
      if(!(seq.go_terms.length==1 && seq.go_terms[0].id=="-"))
        result = '<a target="_blank"style="display:inline-block; width:95%" href="go.html?query=' +
        seq.query + '">' + goTerms +
        '</a>' + '<button onclick="changeText(this.id)" id="showMore'+seq.qID+'"type="button" title="Show GO-Term Breakdown" class="collapsed btn btn-light btn-xs" ' + 
        'data-toggle="collapse" data-target="#accordion-go-' +seq.qID+ 
        '">show more</button>'
      else 
        result = seq.go_terms[0].name
    }
    else result ='-'    
    
    return (
      '<td class="align-middle wrap" style="position:relative;">' +
      result+'</td>'
    );
  };

  /** Draw bpoc cell for sequence */
  this.drawBpocCell = function(bpoc) {
    var color;
    bpoc == '1' ? (color = '#ff99a1') : (color = null);
    return (
      '<td class="align-middle" >' +
      '<div style="min-width:20px; background:' +
      color +
      '; text-align:center; border-radius:20%">' +
      bpoc +
      '</div> </td>'
    );
  };

  /** Draw collapsible sequence cell for sequence */
  this.drawCollapsible = function(seq) {
    return (
      '<tr><td colspan="' +
      columnCount +
      '" class="hiddenRow bg-black ">' +
      '<div id="accordion-' +
      seq.qID +
      '" class="p-3 collapse collapseQuery">' +
      '</div></td></tr>'
    );
  };

  /** Fill collapsible sequence cell dynamically - is more efficient than filling all at once */
  this.fillCollapsible = function(seq) {
    var sequence = seq.sequence.split('').reduce(function(res, char) {
      return (res += '<span class="' + char + '">' + char + '</span>');
    }, '');

    var html =
      '<p class="text-break word-break text-monospace"' + sequence + '</p>';

    $('#accordion-' + seq.qID).html(html);
  };

  /** Draw collapsible confidence cell for sequence */
  this.drawGOTermCollapsible = function(seq) {
    var goTerms = '';
    if (seq.go_childs != null)
      seq.go_childs.forEach(function (goTerm) {
        goTerms +=
          '<span style="font-weight:bold">' +
          goTerm.id +
          '</span> - ' +
          goTerm.name +
          '<br>';
      });
    return '<tr><td colspan="' + columnCount + '" class="hiddenRow">' +
            '<div id="accordion-go-' + seq.qID + '" class="p-3 collapse"><h4> GO Terms</h4>' +                        
            goTerms + "</div></td></tr>";
    };

  /** Close all collapsibles */
  this.closeSecretRows = function() {
    $('collapse').collapse('hide');
  };

  /* ###############################  SORTING STUFF  ######################### */

  /** Reset all sortIcons to neutral state */
  this.resetSortIcons = function() {
    $('.sortTag').html('<i class="' + this.sortIcon[0] + '"></i>');
  };

  /** Set sortIcon at @param col to @param dir */
  this.setSortIcon = function(col, dir) {
    $('#columnResponse_'+col)[0].innerHTML = '<i class="' + this.sortIcon[dir] + '"></i>';
  };

  /* ################################  FILTER STUFF  ######################### */
  var checkBoxNames = {
    5: 'nonviral_adhesion',
    6: 'viral_adhesion', 
    7: 'antibiotic_resistance',
    8: 'avirulence_plant',
    9: 'counter_immunoglobulin',
    10: 'cytotoxicity', 
    11: 'develop_in_host',
    12: 'disable_organ', 
    13: 'plant_rna_silencing',
    14: 'induce_inflammation', 
    15: 'nonviral_invasion',
    16: 'viral_invasion', 
    17: 'degrade_ecm',
    18: 'host_cytoskeleton', 
    19: 'host_cell_cycle',
    20: 'host_cell_death',
    21: 'host_gtpase',
    22: 'host_transcription',
    23: 'host_translation',
    24: 'host_ubiquitin',
    25: 'host_xenophagy',
    26: 'nonviral_movement',
    27: 'viral_movement',
    28: 'toxin_synthase', 
    29: 'resist_complement',
    30: 'resist_oxidative',
    31: 'secreted_effector',
    32: 'secretion',
    33: 'suppress_detection',
    34: 'bacterial_counter_signaling',
    35: 'viral_counter_signaling',
    36: 'virulence_regulator',
    37: 'virulence_activity',
  };
  /**
   * Draw all enabled filters as <ul> in #filterList div
   * @param {object} filters {col: content}
   */
  this.drawFilters = function(filters) {
    var res = '';
    var self = this;
    var clearButton ='';

    if (!$.isEmptyObject(filters)) {
      /** general open span element */
      var span = '<span class="badge badge-secondary">';
      // declare reduce functions here for readability
      res +=
        '<h4>Enabled Filters:' +
        '</h4><ul>';
      for (var f in filters) {
        res += '<li>';

        if (f == '0') {
          res += 'Query: ' + span + filters[f] + '</span>';
        }
        if (f == '1') {
          res += 'Sequence Length: ';
          if (filters[f].lower)
            res += span + 'Min ' + filters[f].lower + '</span> ';
          if (filters[f].upper)
            res += span + 'Max ' + filters[f].upper + '</span>';
        }
        if (f == '2') {
          res += 'Organism: ' + span + filters[f] + '</span>';
        }
        if (f == '3') {
          res += 'Gene Name: ' + span + filters[f] + '</span>';
        }
        if (f == '4') {
          res += 'GO Term: ' + span + filters[f] + '</span>';
        }
        if (parseInt(f) > 4 && parseInt(f) < 38) {
          res += checkBoxNames[parseInt(f)] + ': ' + span + '1 </span>';
        }
        res += '</li>';
      }
      res += '</ul>';
      clearButton +=
        "<button id='clearAll' class='btn'>" +
        'Clear Filters ('+
        " <i class='fas fa-times'/>" +
        ' )'+
        ' </button>';
    }

    $('#filterList').html(res);
    $('#clearFilter').html(clearButton);
  };

  /**
   * Handle filter modal:
   *
   * Set inputs to old or empty value
   * Call reset() on clearButton
   * Get inputs and call success with non-empty inputs on applyButton
   */
  this.showFilter = function(prev, success, reset) {
    // Buttons
    var apply = $('#modalApply');
    var clear = $('#modalClear');
    // Filter contents
    var query = $('#modalQuery');
    var bounds = {
      lower: $('#modalMin'),
      upper: $('#modalMax')
    };
    var organism = $('#modalOrganism');
    var geneName = $('#modalGeneName');
    var goTerm = $('#modalGOTerm');

    var nonviral_adhesion  = $('#modalBpoc0');
    var viral_adhesion = $('#modalBpoc1');
    var antibiotic_resistance = $('#modalBpoc2');
    var avirulence_plant  = $('#modalBpoc3');
    var counter_immunoglobulin = $('#modalBpoc4');
    var cytotoxicity = $('#modalBpoc5');
    var develop_in_host = $('#modalBpoc6');
    var disable_organ = $('#modalBpoc7');
    var plant_rna_silencing = $('#modalBpoc8');
    var induce_inflammation = $('#modalBpoc9');
    var nonviral_invasion = $('#modalBpoc10');
    var viral_invasion = $('#modalBpoc11');
    var degrade_ecm = $('#modalBpoc12');
    var host_cytoskeleton = $('#modalBpoc13');
    var host_cell_cycle = $('#modalBpoc14');
    var host_cell_death= $('#modalBpoc15');
    var host_gtpase = $('#modalBpoc16');
    var host_transcription= $('#modalBpoc17');
    var host_translation = $('#modalBpoc18');
    var host_ubiquitin = $('#modalBpoc19');
    var host_xenophagy = $('#modalBpoc20');
    var nonviral_movement = $('#modalBpoc21');
    var viral_movement = $('#modalBpoc22');
    var toxin_synthase = $('#modalBpoc23');
    var resist_complement = $('#modalBpoc24');
    var resist_oxidative = $('#modalBpoc25');
    var secreted_effector = $('#modalBpoc26');
    var secretion = $('#modalBpoc27');
    var suppress_detection = $('#modalBpoc28');
    var bacterial_counter_signaling = $('#modalBpoc29');
    var viral_counter_signaling = $('#modalBpoc30');
    var virulence_regulator = $('#modalBpoc31');
    var virulence_activity = $('#modalBpoc32');

    // disable clearButton if no previous filter was set
    clear.prop('disabled', $.isEmptyObject(prev));

    // set current (old) filter values or empty
    query.val(prev[0] || '');
    if (!!prev[1]) {
      bounds.lower.val(prev[1].lower || '');
      bounds.upper.val(prev[1].upper || '');
    } else {
      bounds.lower.val('');
      bounds.upper.val('');
    }
    organism.val(prev[2] || '');
    geneName.val(prev[3] || '');
    goTerm.val(prev[4] || '');

    for (let i = 5; i < 38; i++) {
      var name = checkBoxNames[i];
      !!prev[i]
        ? eval(name).prop('checked', prev[i])
        : eval(name).prop('checked', false);
    }
    // clearButton functionality
    clear.off('click');
    clear.click(function() {
      reset();
    });

    // applyButton: send {col: content} as callback
    apply.off('click');
    apply.click(function() {
      // get all values, even if empty
      var contents = {
        0: query.val(),
        1: { lower: bounds.lower.val(), upper: bounds.upper.val() },
        2: organism.val(),
        3: geneName.val(),
    5: nonviral_adhesion.is(':checked'),
    6: viral_adhesion.is(':checked'),
    7: antibiotic_resistance.is(':checked'),
    8: avirulence_plant.is(':checked'),
    9: counter_immunoglobulin.is(':checked'),
    10: cytotoxicity.is(':checked'),
    11: develop_in_host.is(':checked'),
    12: disable_organ.is(':checked'),
    13: plant_rna_silencing.is(':checked'),
    14: induce_inflammation.is(':checked'),
    15: nonviral_invasion.is(':checked'),
    16: viral_invasion.is(':checked'),
    17: degrade_ecm.is(':checked'),
    18: host_cytoskeleton.is(':checked'),
    19: host_cell_cycle.is(':checked'),
    20: host_cell_death.is(':checked'),
    21: host_gtpase.is(':checked'),
    22: host_transcription.is(':checked'),
    23: host_translation.is(':checked'),
    24: host_ubiquitin.is(':checked'),
    25: host_xenophagy.is(':checked'),
    26: nonviral_movement.is(':checked'),
    27: viral_movement.is(':checked'),
    28: toxin_synthase.is(':checked'),
    29: resist_complement.is(':checked'),
    30: resist_oxidative.is(':checked'),
    31: secreted_effector.is(':checked'),
    32: secretion.is(':checked'),
    33: suppress_detection.is(':checked'),
    34: bacterial_counter_signaling.is(':checked'),
    35: viral_counter_signaling.is(':checked'),
    36: virulence_regulator.is(':checked'),
    37: virulence_activity.is(':checked')
        /*                 6: goTerm.val()
         */
      };

      // remove falsy values (e.g. empty string, all checkboxes checked)
      for (let i = 0; i < 38; i++) {
        if (i == 1) {
          if (!contents[1].lower && !contents[1].upper) delete contents[1];
        } else if (!contents[i]) delete contents[i];
      }

      success(contents);
    });
  };

  /* ##############################  ALIGNMENT STUFF  ######################## */

  /** Fill alignment modal with sequence data
   * @param {object} seq selected Sequence
   * @param {$object} modal modal to apply this to
   */
  this.showAlignment = function(seq, modal) {
    $(modal)
      .find('.modal-title')
      .text('Alignment Results for ' + seq.query);

    $(modal)
      .find('#alignGene')
      .text(seq.geneName);
    $(modal)
      .find('#alignUpID')
      .text(seq.uniprot);
    $(modal)
      .find('#alignTaxo')
      .text(seq.taxID);
    //polyfill for "repeat"	  
    if (!String.prototype.repeat) {
        String.prototype.repeat = function(count) {
          'use strict';
          if (this == null)
            throw new TypeError('can\'t convert ' + this + ' to object');
      
          var str = '' + this;
          // To convert string to integer.
          count = +count;
          // Check NaN
          if (count != count)
            count = 0;
      
          if (count < 0)
            throw new RangeError('repeat count must be non-negative');
      
          if (count == Infinity)
            throw new RangeError('repeat count must be less than infinity');
      
          count = Math.floor(count);
          if (str.length == 0 || count == 0)
            return '';
      
          // Ensuring count is a 31-bit integer allows us to heavily optimize the
          // main part. But anyway, most current (August 2014) browsers can't handle
          // strings 1 << 28 chars or longer, so:
          if (str.length * count >= 1 << 28)
            throw new RangeError('repeat count must not overflow maximum string size');
      
          var maxCount = str.length * count;
          count = Math.floor(Math.log(count) / Math.log(2));
          while (count) {
             str += str;
             count--;
          }
          str += str.substring(0, maxCount - str.length);
          return str;
        }
    }
    var hsp = seq.alignment;
    var alignments = '';

    var qStart = parseInt(hsp.queryFrom);
    var hStart = parseInt(hsp.hitFrom);

    for (var index = 0; index < hsp.qSeq.length; index += 50) {
      var end = Math.min(hsp.qSeq.length, index + 50);

      qLine = hsp.qSeq.substr(index, 50);
      mLine = hsp.midLine.substr(index, 50).replace(/ /g, '&nbsp;');
      hLine = hsp.hSeq.substr(index, 50);

      qEnd = qStart + end - index - (qLine.split('-').length - 1); // count occurrences
      hEnd = hStart + end - index - (hLine.split('-').length - 1); // of '-' in string

      // sorry, this is as good as it gets
      alignments +=
        '<li class="list-group-item" style="font-family: monospace">' +
        '<p class="m-0 p-0">' +
        'Query' +
        '&nbsp;'.repeat(3) +
        qStart +
        '&nbsp;'.repeat(5 - qStart.toString().length) +
        qLine +
        '&nbsp;' +
        qEnd +
        '</p><p class="m-0 p-0">' +
        '&nbsp;'.repeat(13) +
        mLine +
        '</p><p class="m-0 p-0">' +
        'Subject&nbsp;' +
        hStart +
        '&nbsp;'.repeat(5 - hStart.toString().length) +
        hLine +
        '&nbsp;' +
        hEnd +
        '</p>' +
        '</li>';

      qStart = qEnd;
      hStart = hEnd;
    }

    $(modal)
      .find('#alignList')
      .html(alignments);
  };
};
