
//        _
//       (_)________  ____
//      / / ___/ __ \/ __ \
//     / (__  ) /_/ / / / /
//  __/ /____/\____/_/ /_/
// /___/
var _i18n = require('./i18n');

module.exports = {
  langs:[
    {
      'lc':'lat',
      'label':'Latin (Carl Gebhardt)',
      'db':'spinoza-ethica-lat-gebhardt.json'
    },
    {
      'lc':'fr',
      'label':'Français (Traduction par Charles Appuhn)',
      'db':'spinoza-ethica-fr-appuhn.json'
    },
    {
      'lc':'bra',
      'label':'Brazilian (Tradução Roberto Brandão)',
      'db':'spinoza-ethica-bra-brandao.json'
    },
    {
      'lc':'en',
      'label':'English (Translated by R. H. M. Elwes)',
      'db':'spinoza-ethica-en-elwes.json'
    }
  ],
  data:[],
  loaded_dbs:0,
  data_byid:[],
  data_bytype:[], // not sure we'll use it ...
  types:[],
  active_type_filter:null,
  data_strct:{},
  rx_id:/^(\d)(app|agd|\d\d|pr|ad|ap|c|p|d|a)(cd|sc|\d\d|d|c|a|l|p|\d)?(e|\d|sc)?(d|c|a|sc)?$/,
  id_strct:[
    {full:'partie',dim:'part.'},
    {
      'prop':{full:'proposition',   dim:'prop.'}, // \d\d
      'app' :{full:'appendice',     dim:'app.'},
      'agd' :{full:'def-gen-affect'},
      'pr'  :{full:'preface',       dim:'pref.'},
      'ad'  :{full:'def-affect'},
      'ap'  :{full:'appendice',     dim:'app.'},
      'c'   :{full:'corollaire',    dim:'cor.'},
      'p'   :{full:'postulat',      dim:'post.'},
      'd'   :{full:'definition',    dim:'def.'},
      'a'   :{full:'axiom',        dim:'ax.'},
    },
    {
      // \d\d
      // \d
      'cd'  :{full:'corollaire-demo'},
      'sc'  :{full:'scolie',        dim:'scol.'},
      'd'   :{full:'demonstration', dim:'demo.'},
      'c'   :{full:'corollaire',    dim:'cor.'},
      'a'   :{full:'axiom',        dim:'ax.'},
      'l'   :{full:'lemme',         dim:'lem.'},
      'p'   :{full:'postulat',      dim:'post.'},
      'e'   :{full:'explication',   dim:'exp.'},
    },
    {
      // \d
      'e'   :{full:'explication',   dim:'exp.'},
      'sc'  :{full:'scolie',        dim:'scol.'},
      'c'   :{full:'corollaire',    dim:'cor.'},
    },
    {
      'd'   :{full:'demonstration', dim:'demo.'},
      'c'   :{full:'corollaire',    dim:'cor.'},
      'a'   :{full:'axiom',        dim:'ax.'},
      'sc'  :{full:'scolie',        dim:'scol.'},
    }
  ],
  // loading progress
  loaded_by_file:{},
  // totalloaded:0,
  loader: document.getElementById('db-loaded'),

  load(callback) {
    // load all dbs, when all loaded call main app callback function
    for (var i = 0; i < this.langs.length; i++) {
      this.loaded_by_file[this.langs[i].lc] = 0;
      this.loadJSON(this.langs[i].lc, '/assets/jsondb/'+this.langs[i].db, callback)
    }
  },
  loadJSON(lc, file, callback){

    var xobj = new XMLHttpRequest();
    xobj.overrideMimeType("application/json");
    // TODO: load and unzip gziped json
    // xobj.setRequestHeader('Accept-Encoding', 'gzip');

    // Display loading progress
    xobj.addEventListener("progress", function(oEvent){
      if (oEvent.lengthComputable) {
        var percentComplete = oEvent.loaded / oEvent.total * 100;
        // console.log(lc+' loaded :',percentComplete);
        this.loaded_by_file[lc] = percentComplete;
        var totalloaded = 0;
        for (var i = 0; i < this.langs.length; i++) {
          totalloaded += this.loaded_by_file[this.langs[i].lc];
        }
        this.loader.style.width = (totalloaded/this.langs.length)+"%";
      } else {
        // Unable to compute progress information since the total size is unknown
        console.log('no progress');
      }
    }.bind(this));

    xobj.onreadystatechange = function () {
      // console.log('onreadystatechange', xobj.readyState);
      switch(xobj.readyState){
        case 3:
          // console.log('loading');
        break;
        break;
        case 4:
          if (xobj.status === 200) {
              this.onJSONLoaded(lc, xobj.responseText, callback);
          } else {
              console.log("Status de la réponse: %d (%s)", xobj.status, xobj.statusText);
          }
        break;
      }
    }.bind(this);

    xobj.open('GET', file, true);
    xobj.send(null);
  },
  onJSONLoaded(lc, json, callback){
    // console.log('onDBLoaded '+lc);
    this.data[lc] = JSON.parse(json);
    this.loaded_dbs ++;
    //
    if (this.loaded_dbs == this.langs.length) {
      // console.log('All db loaded : data', this.data);
      this.parseByID(callback);
    }

  },
  parseByID(callback){
    // TODO: make this function recursive to avoid code repetition
    // create a id keyed array of contents
    var e_id, c_id, cc_id,
        dottype;
    // loop through languages
    for(let l in this.data){
      // console.log('l', l);
      this.data_byid[l] = {};
      this.data_bytype[l] = {};
      this.types[l] = [];
      _i18n.setLang(l);

      // loop through parts
      for (let p in this.data[l]) {
        if(this.data[l][p].type !== "intro"){
          // console.log(this.data[l][p]);
          // loop through enonces
          for (let e in this.data[l][p].enonces) {
            // console.log('e',e);
            if(this.data[l][p].enonces[e].id){
              e_id = this.data[l][p].enonces[e].id;
              // guess the type from the id
              // console.log(this.data[l][p].enonces[e].id);
              dottype = this.setupType(e_id);
              this.data[l][p].enonces[e].dottype = dottype;
              // breadcrumb (full tree title)
              this.data[l][p].enonces[e].breadcrumb = this.setupBreadcrumb(e_id);
              // records childs in array keyed by ids
              this.data_byid[l][e_id] = this.data[l][p].enonces[e];
              // data by dottype
              if (dottype) {
                if(typeof this.data_bytype[l][dottype] == "undefined") this.data_bytype[l][dottype] = {};
                if(this.types[l].indexOf(dottype) == -1) this.types[l].push(dottype);
                this.data_bytype[l][dottype][e_id] = this.data[l][p].enonces[e];
              }
            }
            // loop through childs
            for (let c in this.data[l][p].enonces[e].childs){
              // console.log(_db[p][e][c]);
              if(this.data[l][p].enonces[e].childs[c].id){
                c_id = this.data[l][p].enonces[e].childs[c].id;
                // guess the type from the id
                dottype = this.setupType(c_id);
                this.data[l][p].enonces[e].childs[c].dottype = dottype;
                // breadcrumb (full tree title)
                this.data[l][p].enonces[e].childs[c].breadcrumb = this.setupBreadcrumb(c_id);
                // records childs in array keyed by ids
                this.data_byid[l][c_id] = this.data[l][p].enonces[e].childs[c];
                // data by dottype
                if (dottype) {
                  if(typeof this.data_bytype[l][dottype] == "undefined") this.data_bytype[l][dottype] = {};
                  if(this.types[l].indexOf(dottype) == -1) this.types[l].push(dottype);
                  this.data_bytype[l][dottype][c_id] = this.data[l][p].enonces[e].childs[c];
                }
              }
              // loop through childs of childs
              for (let cc in this.data[l][p].enonces[e].childs[c].childs){
                // console.log(_db[p][e][c]);
                if(this.data[l][p].enonces[e].childs[c].childs[cc].id){
                  cc_id = this.data[l][p].enonces[e].childs[c].childs[cc].id;
                  // console.log('cc_id', cc_id);
                  // guess the type from the id
                  dottype = this.setupType(cc_id);
                  this.data[l][p].enonces[e].childs[c].childs[cc].dottype = dottype;
                  // breadcrumb (full tree title)
                  this.data[l][p].enonces[e].childs[c].childs[cc].breadcrumb = this.setupBreadcrumb(cc_id);
                  // records childs in array keyed by ids
                  this.data_byid[l][cc_id] = this.data[l][p].enonces[e].childs[c].childs[cc];
                  // data by dottype
                  if (dottype) {
                    if(typeof this.data_bytype[l][dottype] == "undefined") this.data_bytype[l][dottype] = {};
                    if(this.types[l].indexOf(dottype) == -1) this.types[l].push(dottype);
                    this.data_bytype[l][dottype][cc_id] = this.data[l][p].enonces[e].childs[c].childs[cc];
                  }
                }
              }
            }
          }
        }
      }
    }
    // console.log('this.data_byid', this.data_byid);
    this.parseStrct(callback);
  },
  setupBreadcrumb(id){
    // /^(\d)(app|agd|\d\d|pr|ad|ap|c|p|d|a)(cd|sc|\d\d|d|c|a|l|p|\d)?(e|\d|sc)?(d|c|a|sc)?$/
    var breadcrumb_array = [];
    var m = id.match(this.rx_id);
    var mode = 'full';
    if(m){
      m.shift();
      // removing undefined
      let m_clean = [];
      for (let i = 0; i < m.length; i++) {
        if(typeof m[i] !== 'undefined'){
          m_clean.push(m[i]);
        }
      }
      // console.log('m_clean', m_clean);

      for (let i = 0; i < m_clean.length; i++) { // we loop through match result variables
      // for (let i = m_clean.length-1; i >= 0; i--) { // we loop through match result variables in reverse order
        // console.log('m_clean['+i+']', m_clean[i]);
        if(i == 0){ // first digit is part number
          breadcrumb_array.unshift(`${this.id_strct[i]['dim']} ${m_clean[i]}`);
        }else{
          // display mode
          // mode = i !== m_clean.length-1 ? 'dim' : 'full';
          mode = 'full';
          if(typeof m_clean[i] !== 'undefined' && isNaN(m_clean[i])){ // if not a number we get the label
            breadcrumb_array.unshift(_i18n.t(`${this.id_strct[i][m_clean[i]][mode]}`));
            // breadcrumb_array.splice(1, 0, `${m_clean[i]}`);
          }else{ // if its a number
            if(i == 1){ // we just add the number to the breadcrumb preceded by 'Proposition'
              breadcrumb_array.unshift(_i18n.t(`${this.id_strct[i]['prop'][mode]}`) + ` ${m_clean[i]}`);
            }else{ // we just add the number to the breadcrumb behind the last added item
              // breadcrumb_array.unshift(`${m_clean[i]}`);
              // debugger;
              breadcrumb_array.splice(1, 0, `${m_clean[i]}`);

            }
          }
        }
      }
    }

    // console.log('breadcrumb_array', breadcrumb_array);
    return breadcrumb_array.join(' ');
  },
  setupType(id){
    // console.log('setupType',id);
    // /^(\d)(app|agd|\d\d|pr|ad|ap|c|p|d|a)(cd|sc|\d\d|d|c|a|l|p|\d)?(e|\d|sc)?(d|c|a|sc)?$/
    var m = id.match(this.rx_id);
    if(m){
      switch(true){
        case /^\d{2}$/.test(m[2]): // proposition
          switch(true){
            case /^cd$/.test(m[3])    : return 'corollaire-demo';
            case /^sc$/.test(m[3])    : return 'scolie';
            case /^d$/.test(m[3])     : return 'demonstration';
            case /^c$/.test(m[3])     :
              switch(true){
                case /^sc$/.test(m[4]): return 'scolie';
                case /^d$/.test(m[4]) : return 'demonstration';
                case /^sc$/.test(m[5]): return 'scolie';
                case /^\d$/.test(m[4]): return 'corollaire';
                case !m[4]            : return 'corollaire';
              }
            case /^a$/.test(m[3])     : return 'prop-axiom';
            case /^l$/.test(m[3])     :
              switch(true){
                case /^d$/.test(m[5]) : return 'lemme-demonstration';
                case /^sc$/.test(m[5]): return 'lemme-scolie';
                case /^c$/.test(m[5]) : return 'lemme-corrollaire';
                case !m[5]            : return 'lemme';
              }
            case /^p$/.test(m[3])     : return 'postulat';
            case /^\d$/.test(m[3])    : return '??';
            case /^\d{2}$/.test(m[3]) : return '??';
            case !m[3]                : return 'proposition';
          }
        case /^app|ap$/.test(m[2])    : return 'appendice';
        case /^agd$/.test(m[2])       : return 'def-gen-affect';
        case /^pr$/.test(m[2])        : return 'preface';
        case /^ad$/.test(m[2])        :
          switch(true){
            case /^e$/.test(m[4])     :return 'explication';
            case !m[4]                :return 'def-affect';
          }
        case /^c$/.test(m[2])         : return 'chapitre';
        case /^p$/.test(m[2])         : return 'postulat';
        case /^d$/.test(m[2])         :
          switch(true){
            case /^e$/.test(m[4])     : return 'explication';
            case !m[4]                : return 'definition';
          }
        case /^a$/.test(m[2])         : return 'axiom';
      }
      // }
    }
    // console.log(`${this.id} -> ${this.dottype}`,m);

    // TODO: fix false ids
    // we have app and ap for appendice (1app | 4ap)
    // 213def ??
    // 209cd demo ou corollaire-demo ??
    // 210csc scolie ou corollaire-scolie ??
    // 213l1d demo ??
  },
  parseStrct(callback){

    var id, item, obj, links_match, link, tid;
    for (id in this.data_byid[this.langs[0].lc]) {
      item = this.data_byid[this.langs[0].lc][id];
      // console.log(item);
      // skeep titles as they don't have structure data
      if(item.type == "title") continue;

      obj = {
        'to':[],
        'from':[],
      };
      // get links
      links_match = item.text.match(/\[[^\]]+\]\([^\)]+\)/g);
      // console.log(links_match);
      // if links exist on text
      if(links_match){
        for(link of links_match){
        // for(i in links_match){
        //   link = links_match[i];
          // console.log(link);
          // get the target id
          tid = link.match(/\((.+)\)/)[1];
          // avoid duplicates
          if (obj.to.indexOf(tid) == -1)
            obj.to.push(tid);

          // add id to "from" links in target
          // if target exists
          if(typeof this.data_strct[tid] !== 'undefined'){
            // avoid duplicates
            if (this.data_strct[tid].from.indexOf(tid) == -1)
              this.data_strct[tid].from.push(id);
          }else{
            // if targets does not exists, the db has an issue, warn about that
            // console.log(`!! warning : ${tid} target id does not exists`);
          }
        }
      }
      // add the item links to the main links listings
      this.data_strct[id] = obj;
    }
    // console.log('data_strct',this.data_strct);

    callback();
  }
}
