/**
 * Created by shadeeka on 7/9/18.
 */
importScripts('https://unpkg.com/mathjs@5.0.2/dist/math.min.js');

// create a parser
const parser = self.math.parser();

self.addEventListener('message', function (event) {
  const request = JSON.parse(event.data);
  var result = null;
  var err = null;
  // build a response
  const response = {
    id: request.id,
    result: result,
    err: err
  };
  try {
    //{'cmd': 'stop', 'msg': 'Bye'}
    switch (request.cmd) {
      case 'evaluate':
        response.result = evaluate(request.expr, request.scope);
        break;
      case 'prepare':
        response.result = prepareRows(request.dataSet, request.exprs);
        colCalc(response.result, null, request.exprs);
        break;
      case 'prepareTotal':
        response.result = prepareRowsTotal(request.dataSet, request.exprs);
        colCalc(response.result, null, request.exprs);
        break;
      case 'calcin':
        response.result = colCalc(request.dataSet, request.lastRow, request.exprs);
        break;
      case 'calcinAll':
        console.debug("Request", request);
        response.result = colCalcAll(request.dataSet, request.lastRow, request.exprs);
        break;
      case 'stop':
        self.postMessage('WORKER STOPPED: ' + request.msg +
          '. (buttons will no longer work)');
        self.close(); // Terminates the worker.
        break;
      default:
        self.postMessage('Unknown command: ' + request.msg);
    }
    ;
  } catch (e) {
    // return the error
    response.err = e;
  }

  // send the response back
  self.postMessage(JSON.stringify(response))
}, false);


function evaluate(equation, scope) {
  try {
    //console.log(equation);
    //console.log(scope);
    const h = self.math.parse(equation);
    const code2 = h.compile();
    return code2.eval(scope);
  } catch (e) {
    console.log(e);
  }

  //console.log(code2.eval({x: 3,y:4})); // '7'
}

function prepareRows(data, formulaCols) {
  var rows = [];
  //  console.log('worker: prepare row',formulaCols);
  //   console.debug('worker: prepare row',data);
  for (var i = 0; i < data.length; i++) {
    var node = data[i];
    var rowNode = {};
    for (var j = 0; j < formulaCols.length; j++) {
      var val = null;
      if (formulaCols[j].calc_type == 'abs') {
        val = node[formulaCols[j].prop];
        val = formatNumber(formulaCols[j], val);

      } else if (formulaCols[j].calc_type == 'formula') {
        // console.debug('worker',formulaCols[j]);
        //  console.debug('worker',node);
        val = evaluate(formulaCols[j].formula, node);
        val = formatNumber(formulaCols[j], val);
      }
      rowNode[formulaCols[j].prop] = val;

    }
    rows.push(rowNode);
  }
  // console.debug('worker',rows);
  return rows;
}

function prepareRowsTotal(data, formulaCols) {
  //TODO: redo the logic
  var rows = [];

  // console.log(' uniq worker: prepare row',formulaCols);
  //  console.debug('uniq worker: prepare row',data);
  var calculate_data_sum = 0;
  var calculate_data_sum_s = 0;
  var calculate_data_avg = 0;
  var val = 0;

  for (var i = 0; i < formulaCols.length; i++) {

    for (var j = 0; j < data.length; j++) {

      if (formulaCols[i]['total_calc_type'] === "sum") {

        if (data[j][formulaCols[i]['totalProp']]) {
          calculate_data_sum += data[j][formulaCols[i]['totalProp']];
          val = calculate_data_sum;
        }

        if (data[j]['key'] === 'Total') {
          data[j][formulaCols[i]['totalProp']] = val;
        }

      }
      //This is hack to get the trend sum - Change this
      if (formulaCols[i]['total_calc_type'] === "trendSum") {

        if (data[j]['key'] === 'Total') {
          data[j][formulaCols[i]['totalProp']] = val;
        }else{
          if (data[j][formulaCols[i]['totalProp']]) {
            calculate_data_sum_s += data[j][formulaCols[i]['totalProp']];
            val = calculate_data_sum_s;
          }
        }

      }
      if (formulaCols[i]['total_calc_type'] === "avg") {

        if (data[j][formulaCols[i]['totalProp']]) {

          calculate_data_avg += data[j][formulaCols[i]['totalProp']];
          val = calculate_data_avg / (data.length - 1);

        }
        if (data[j]['key'] === 'Total') {
          data[j][formulaCols[i]['totalProp']] = val;
        }

      }
console.debug(data[j])
      rows.push(data[j]);
    }

  }


  var uniq = rows.reduce(function (a, b) {
    if (a.indexOf(b) < 0) a.push(b);
    return a;
  }, []);

  return uniq;
}

function colCalc(data, lastRow, formulaCols) {
  // add last row before the new rows;
  var i = 0;
  if (lastRow) {
    data.splice(0, 0, lastRow);
    i = 1;
  }
  for (i; i < data.length; i++) {
    var DataRow = data[i];
    var PrevRow = data[i - 1];
    // console.log('inline----------->');
    // console.log(data[i]);
    for (var j = 0; j < formulaCols.length; j++) {
      var colConfig = formulaCols[j];

      if (colConfig.calc_type === 'inline') {
        // console.log("inline "+ colConfig.calc_type)
        const nodeTree = self.math.parse(colConfig.formula);
        var evalScope = {};
        // console.log(colConfig);
        var transformed = nodeTree.transform(function (node, path, parent) {
          //  console.log('transform----------->'+node.name);
          //  console.log(DataRow);
          if (node.isSymbolNode && DataRow.hasOwnProperty(node.name)) {
            //return new math.expression.node.ConstantNode(3);
            console.log('inline node----------->');
            evalScope[node.name] = DataRow[node.name];
            return node;
          }
          else if (node.isSymbolNode && node.name.indexOf('last_') > -1) {
            //  console.log('inline node----------->'+node.name);
            if (i == 0) {
              evalScope[node.name] = 0;
            } else {
              var props = node.name.split('last_');
              evalScope[node.name] = PrevRow[props[1]];
            }
            return node;
          } else {
            // console.log(node);
            return node;
          }
        });
        //evaluate formula
        val = evaluate(colConfig.formula, evalScope);
        val = formatNumber(colConfig, val);
        DataRow[colConfig.prop] = val;

      }
    }
  }
  return data[data.length - 1]; //return only last
}

function colCalcAll(data, lastRow, formulaCols) {
  // add last row before the new rows;
  var i = 0;
  if (lastRow) {
    data.splice(0, 0, lastRow);
    i = 1;
  }
  for (i; i < data.length; i++) {
    var DataRow = data[i];
    var PrevRow = data[i - 1];
    //console.log('inline----------->');
    //console.log(data[i]);
    for (var j = 0; j < formulaCols.length; j++) {
      var colConfig = formulaCols[j];

      if (colConfig.calc_type === 'inline') {
        //console.log("inline "+ colConfig.calc_type)
        const nodeTree = self.math.parse(colConfig.formula);
        var evalScope = {};
        //console.log(colConfig);
        var transformed = nodeTree.transform(function (node, path, parent) {
          console.log('transform----------->' + node.name);
          console.log(DataRow);
          if (node.isSymbolNode && DataRow.hasOwnProperty(node.name)) {
            //return new math.expression.node.ConstantNode(3);
            console.log('inline node----------->');
            evalScope[node.name] = DataRow[node.name];
            return node;
          }
          else if (node.isSymbolNode && node.name.indexOf('last_') > -1) {
            console.log('inline node----------->' + node.name);
            if (i == 0) {
              evalScope[node.name] = 0;
            } else {
              var props = node.name.split('last_');
              evalScope[node.name] = PrevRow[props[1]];
            }
            return node;
          } else {
            console.log(node);
            return node;
          }
        });
        //evaluate formula
        val = evaluate(colConfig.formula, evalScope);
        val = formatNumber(colConfig, val);
        DataRow[colConfig.prop] = val;

      }
    }
  }
  return data; //return all
}

function formatNumber(colConf, val) {
  if (val) {
    var roundby = (colConf.roundBy != undefined) ? colConf.roundBy : 2;

    if (typeof val === 'string') {

    } else if (val) {

      val = val.toFixed(roundby);

      if (roundby === 0) {
        val = parseInt(val);
      }
      /*else {
       val = parseFloat(val);
       }*/
    }
  }

  return val;
}
