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;
  var _infos          = false;
  var _flag_nav        = true;

  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;

    _flag_nav = false;

    blockName = _getblockNameFromHash(block.hash);
    if (block.hash == _liste_blocks['TOP']) {
      blockName = 'LAST BLOCK';
      if (_mode == 1) div_label = 'LAST';
    }
    if (blockName == '') blockName = 'block';
    else blockName = '<b>'+blockName+'</b>';

    visibility='hidden';
    if (_infos) visibility='visible';

    contenu += '<div id="img_'+block.height+'" style="color:black;height:220px">';
    contenu += '      <table class="block_infos" width="100%" align="center" height="220px" style="margin-top:3px;visibility:'+visibility+';background:rgba(255,255,255,0.7)">';
    contenu += '        <tr><td>'+blockName+'</td><td align="right"><b>'+block.height+'</b></td></tr>';
    contenu += '        <tr><td>timestamp&nbsp;&nbsp;&nbsp;</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>';
    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>';
    

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

    downloadingImage.onload = function(){
      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%";
      _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 += '      <div class="loading">LOADING ...</div>';
      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)
  {
    var old_hash = _liste_blocks['TOP'];

    if (old_hash == data.hash) return false;

    if (flag) return "ajouterTopBlock";

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

    // Rafraîchir l'ancien block TOP
    if (_mode == 0) {
      $.getJSON('data/getBlockInfo.php?block_hash='+old_hash, function( old ) {
        _addInfoForBlock(old);
      });
      _addDivForBlock(data.height,true);
    }

    // Mettre les infos dans le div
    _addInfoForBlock(data);

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

  function _maintenirListe(data){
    _liste_blocks['BLOCK_'+data.height] = data.hash;
    _liste_blocks['LENGTH'] += 1;

    // Récursivité pour précharger par paquets
    // Tant qu'on n'est pas sur le GENESIS
    if (data.height > 0)
      switch(_mode)
      {
          case 0:
              // En mode "Full Blockchain", on suit le chainage
              _liste_blocks['BOTTOM'] = data.prev;
              break;
          case 1:
              // En mode "Blocks Connus", on prend le prochain de la liste
              _liste_blocks['BOTTOM'] = _known_blocks[0].hash;
              for(i = 0; i < _known_blocks.length; i++){ 
                if (data.height == _known_blocks[i].height)
                  _liste_blocks['BOTTOM'] = _known_blocks[i+1].hash;
              }
              //_liste_blocks['BOTTOM'] = _known_blocks[_liste_blocks['LENGTH']-1].hash;
              break;
      }

      if (data.height > 0)
        if (_liste_blocks['LENGTH'] % 15)
          _addBottomBlock();
  }

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

        console.log('add '+ data.height + ' : ' + _liste_blocks['BOTTOM']);

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

        // Maintenir la Liste
        _maintenirListe(data);
    });
  }

  function _initBlockchain(block_name)
  {
    var block_hash_url = 'data/getBlockInfo.php';

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

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

    if (block_name != 'LAST') block_hash_url += '?block_hash='+_getblocHashFromName(block_name);

    $.getJSON(block_hash_url, function( data ) {

      div_label = data.height;
      if (_mode == 1) 
        if (block_name == 'LAST') 
          div_label = 'LAST';

      _liste_blocks['TOP'] = data.hash;

      _addDivForBlock(div_label);        

      _addInfoForBlock(data);

      _maintenirListe(data);

      // Cela fout le boxon pour l'instant
      blockchainListener.addBlockHook(_addTopBlock);
    });

    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, infos)
  {
    _mode = mode;
    _infos = infos;

    if (_known_blocks == null)
    {
      $.getJSON('data/getKnownBlocksList.php', function( data ) {

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

    return _initBlockchain('LAST');
  }

  function _toggleInfos()
  {
    var infos = Array.from(document.getElementsByClassName('block_infos'));
    _infos = ! _infos;
    infos.forEach((item, index) => {
      if (_infos) item.style.visibility = 'visible'; 
      else item.style.visibility = 'hidden';
    });
  }

  function _getLength() {
    if ( _liste_blocks === null) return 0;
    return _liste_blocks['LENGTH'];
  }

  return {

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

    toggleInfos: _toggleInfos,
    getLength: _getLength,

    init: _init
  };
}();
