blockchainExplorer = function(){

  // Init array
  var _known_blocks   = null;
  var _liste_blocks   = null;
  var _classes        = ['bg-grey-even','bg-grey-odd'];
  var _cur_class      = 0;
  var _cur_methode    = 'hasard';
  var _mode          = 1;

  function _precisionRound(number) {
      var precision = 4;
      var factor = Math.pow(10, precision);
      return Math.round((number/100000000) * factor) / factor;
  }

  function _getblockNameFromHash(hash)
  {
    var retour = '';
    if (_known_blocks != null)
      _known_blocks.forEach(function(item){
        if (hash == item.hash) retour = item.name;
      });
    return retour;
  }

  function _getblocHashFromName(name)
  {
    var retour = '';
    if (_known_blocks != null)
     _known_blocks.forEach(function(item){
       if (name == item.name) retour = item.hash;
     });
    return retour;
  }

  function _convertUTCDateToLocalDate(date) {
    var newDate = new Date(date.getTime()+date.getTimezoneOffset()*60*1000);

    var offset = date.getTimezoneOffset() / 60;
    var hours = date.getHours();

    newDate.setHours(hours - offset);

    return newDate;   
  }

  function _formatDate(unixtime)
  {
    var ladate = new Date(unixtime*1000);
    var ladateStr = "";

    ladate = _convertUTCDateToLocalDate(ladate);

    ladateStr += ladate.getFullYear();
    ladateStr += "/" +("00"+(ladate.getMonth()+1)).slice(-2);
    ladateStr += "/" +("00" + ladate.getDate()).slice(-2);
    ladateStr += " " +("00" + ladate.getHours()).slice(-2);
    ladateStr += ":" +("00" + ladate.getMinutes()).slice(-2);
    ladateStr += ":" +("00" + ladate.getSeconds()).slice(-2);

    return ladateStr;
  }

  function _addInfoForBlock(block)
  {
    var contenu = '';
    var downloadingImage = new Image();
    var div_label = block.height;

    _cur_class  = 1 - _cur_class;

    blockName = _getblockNameFromHash(block.hash);
    // if (blockName != '') blockName = ' ( '+blockName+' )';

    if (_mode > 1)
        if (block.hash == _liste_blocks['TOP'])
        {
            div_label = 'LAST';
            if (blockName == '') blockName = 'LAST';
        }
    contenu += '      <div style="height:220px;width:100%;display:block;position:absolute">';
    if ((_mode % 2) != 0)
    {
      contenu += '<div style="color:black;opacity:1.0;margin-right:30px">';
      // contenu += '      <h2> <span style="font-size:12px">block</span> '+block.height+blockName+'</h2>';
      //contenu += '<span style="font-size:4px">&nbsp;</span>';
      contenu += '      <table width="100%" style="margin-top:3px">';
      //contenu += '       <tr><td>hash</td><td align="right"><b>'+block.hash+'</b></td></tr>';
      //contenu += '       <tr><td>index</td><td align="right"><b>'+block.block_index+'</b></td></tr>';
      if (_mode == 3 ) contenu += '        <tr><td>name</td><td align="right"><b>'+blockName+'</b></td></tr>';
      contenu += '        <tr><td>height</td><td align="right"><b>'+block.height+'</b></td></tr>';
      contenu += '        <tr><td>timestamp</td><td align="right"><b>'+_formatDate(block.time)+'</b></td></tr>';
      contenu += '        <tr><td>nonce</td><td align="right"><b>'+block.nonce+'</b></td></tr>';
      contenu += '        <tr><td>nb tx</td><td align="right"><b>'+block.n_tx+'</b></td></tr>';
      contenu += '        <tr><td>outputs</td><td align="right"><b>'+_precisionRound(block.topisto_outputs).toFixed(4)+'</b></td></tr>';
      contenu += '        <tr><td>inputs</td><td align="right"><b>'+_precisionRound(block.topisto_inputs).toFixed(4)+'</b></td></tr>';
      if (_mode != 3 ) contenu += '        <tr><td>fees</td><td align="right"><b>'+_precisionRound(block.topisto_fees).toFixed(4)+'</b></td></tr>';
      contenu += '        <tr><td>reward</td><td align="right"><b>'+_precisionRound(block.topisto_reward).toFixed(4)+'</b></td></tr>';
      contenu += '      </table>';
      contenu += '</div>';
    }
    contenu += '      </div>';

    $('#info_'+div_label).html(contenu);

    contenu = '';
    contenu += '<div id="img_'+block.height+'" style="';
    contenu += 'height:220px;';
    contenu += 'content: "";';
    contenu += 'top: 0;';
    contenu += 'left: 0;';
    contenu += 'bottom: 0;';
    contenu += 'right: 0;';
    contenu += 'position: relative;';
    contenu += 'z-index: -1;   ';
    contenu += '"></div>'

    $('#info_'+div_label).append(contenu);

    downloadingImage.onload = function(){

      // $('#img_'+block.height).attr('src', this.src);   
/*
      $('#img_'+block.height).attr('height', _height);
      $('#img_'+block.height).attr('width','auto');
*/

      var div0 = document.getElementById('img_'+block.height);

      div0.style.backgroundImage = "url(" + this.src + ")";
      div0.style.backgroundRepeat = "no-repeat";
      div0.style.backgroundPosition = "center";
      div0.style.backgroundSize = "auto 100%";
      if ((_mode %2) == 1) div0.style.opacity=0.3;

      flag_nav = true;

    };
    downloadingImage.src = 'images/block_image.php?methode='+_cur_methode+'&hash='+block.hash;

    return true;
  }

  function _addDiv(block_height)
  {
    var contenu = '';

    contenu += '<div id="block_'+block_height+'" class="container-fluid '+_classes[_cur_class]+'">';
    contenu += '  <div class="row">';
    contenu += '###BLOCK_DESC###';
    contenu += '  </div>';
    contenu += '</div>';

    return contenu;
  }

  function _addDivForBlock(block_height, onTop = false)
  {
    var lediv = document.getElementById('info_'+block_height);
    if (lediv === null)
    {
      var tmp;
      var contenu = _addDiv(block_height);
      var block_desc = '';

      block_desc += '    <div class="col-sm-12" id="info_'+block_height+'">';
      block_desc += '      <h2>BLOCK '+block_height+' ...</h2>';
      block_desc += '    </div>';

      tmp = contenu.replace('###BLOCK_DESC###',block_desc)

      if (onTop) {
        tmp += $('#blockchain').html();
        $('#blockchain').html(tmp);
      } else {
        $('#blockchain').append(tmp);
      }
    }
    return true;
  }

  function _addDivForVoid()
  {
    return _addDivForBlock('void');
  }

  function _gotoBlock(block_name)
  {
    $(document).scrollTop( $("#explorer").offset().top );
    // Bloquer la navigation pendant le calcul
    if (!flag_nav)
    {
      window.alert('A block image is currently computed, please wait ...');
      return false;
    }
    flag_nav   = false;

    if (block_name == 'NEXT')
    {
      flag_nav   = true;

      // Supprimer un block
      if (cur_height.length < 3)
      {
        window.alert('No Next Block, you are at '+$('#blockSelector').val()+' block !');
        return false;
      }

      _liste_blocks['PREVIOUS'] = _liste_blocks['BLOCK_'+cur_height[cur_height.length-1]];
      cur_height.pop();
        
      _toggleForwardBtn();

      $('#block_'+cur_height[cur_height.length-2]).slideDown(400, function() {
        // Animation complete.
        $('#blockchain').children('div:last').remove();
        _cur_class = 1 - _cur_class;
      });

    } else {
      // Ajouter un block
      _addDivForBlock(cur_height[cur_height.length-1] - 1);

      // Décaler d'un block vers le haut
      if (cur_height.length > 1) $('#block_'+cur_height[cur_height.length-2]).slideUp();

      block_hash = '';
      if (block_name != 'LAST') block_hash = '?block_hash='+_liste_blocks[block_name];
      $.getJSON('data/getBlockInfo.php'+block_hash, function( data ) {
        _liste_blocks['PREVIOUS'] = data.prev;
        _liste_blocks['BLOCK_'+data.height] = data.hash;
        _addInfoForBlock(data);

        _toggleForwardBtn();
      });
    }

    return true;
  }

  function _addTopBlock(data, flag = false)
  {
    if (flag) return "ajouterTopBlock";

    // Maintenir la liste des blocks
    _liste_blocks['TOP'] = data.hash;
    _liste_blocks['BLOCK_'+data.height] = data.hash;

    // Ajouter un div
    if (_mode < 2) _addDivForBlock(data.height,true);
    
    // Mettre les infos dans le div
    _addInfoForBlock(data);

    topistoConsole.log('Last Block on top : '+data.hash);

  }

  function _addBottomBlock()
  {   
    // Mettre les infos du block
    block_hash = '?block_hash='+_liste_blocks['BOTTOM'];
    $.getJSON('data/getBlockInfo.php'+block_hash, function( data ) {

        // Ajouter un div
        _addDivForBlock(data.height, false);
    
        // Mettre les infos dans le div
        _addInfoForBlock(data);

        _liste_blocks['BLOCK_'+data.height] = data.hash;
        _liste_blocks['LENGTH'] += 1;

        switch(_mode)
        {
            case 0:
            case 1:
                // Récursivité pour précharger par paquets
                if (data.height > 0) 
                    _liste_blocks['BOTTOM'] = data.prev;
                break;
            case 2:
            case 3:
                if (data.height > 0)
                    _liste_blocks['BOTTOM'] = _known_blocks[_liste_blocks['LENGTH']-1].hash;
                break;
        }

        if (_liste_blocks['LENGTH'] % 10) _addBottomBlock();
        
    });
  }

  function _initBlockchain(block_name)
  {
    var block_hash = '';

    _liste_blocks = [];
    _liste_blocks['LENGTH'] = 0;

    $(document).scrollTop( $("#explorer").offset().top );
    $('#blockchain').html('');

    if (block_name != 'LAST') block_hash = '?block_hash='+_getblocHashFromName(block_name);
    $.getJSON('data/getBlockInfo.php'+block_hash, function( data ) {

      _liste_blocks['TOP'] = data.hash;
      _liste_blocks['BLOCK_'+data.height] = data.hash;
      _liste_blocks['LENGTH'] += 1;

      div_label = data.height;
      switch(_mode)
      {
          case 0:
          case 1:
              // Récursivité pour précharger par paquets
              _liste_blocks['BOTTOM'] = data.prev;
              break;
          case 2:
          case 3:
              div_label = 'LAST';
              _liste_blocks['BOTTOM'] = _known_blocks[0].hash;
              break;
      }

      _addDivForBlock(div_label);        
      _addInfoForBlock(data);

      blockchainListener.addBlockHook(_addTopBlock);

      _addBottomBlock();
    });

    return true;
  }

  function _blockSelectorChange()
  {
    _initBlockchain($('#blockSelector').val());
  }

  function _initBlockSelector()
  {
    // Init the selector
    var select = $('#blockSelector');
      
    $.each(_liste_blocks, function (key, bloc) {
      select.append(new Option(bloc.name, bloc.name));
    });
  }

  function _init(mode = 1)
  {
    _mode = mode;
    if (_known_blocks == null)
    {
      $.getJSON('data/getKnownBlocksList.php', function( data ) {

        _known_blocks = data;
        _known_blocks.sort(function(a,b){
          // sort desc ...
          if (a.height < b.height) return 1;
          if (a.height > b.height) return -1;
          return 0;
        });

        _initBlockchain('LAST');

        return true;
      });
    } else {
      _initBlockchain('LAST');
      return true;
    }
  }

  return {

    addTopBlock: _addTopBlock,
    addBottomBlock: _addBottomBlock,
    getblocHashFromName: _getblocHashFromName,

    init: _init
  };
}();
