import { Converter } from "./Converter";
import CSVError from "./CSVError";
import { CellParser, ColumnParam } from "./Parameters";
import set from "lodash/set";
import { ParseRuntime } from "./ParseRuntime";

var numReg = /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/;

export default function (csvRows: string[][], conv: Converter): JSONResult[] {
  const res: JSONResult[] = [];
  for (let i = 0, len = csvRows.length; i < len; i++) {
    const r = processRow(csvRows[i], conv, i);
    if (r) {
      res.push(r);
    }
  }
  return res;
};
export type JSONResult = {
  [key: string]: any
}

function processRow(row: string[], conv: Converter, index): JSONResult | null {

  if (conv.parseParam.checkColumn && conv.parseRuntime.headers && row.length !== conv.parseRuntime.headers.length) {
    throw (CSVError.column_mismatched(conv.parseRuntime.parsedLineNumber + index))
  }

  const headRow = conv.parseRuntime.headers || [];
  const resultRow = convertRowToJson(row, headRow, conv);
  if (resultRow) {
    return resultRow;
  } else {
    return null;
  }
}

function convertRowToJson(row: string[], headRow: string[], conv: Converter): { [key: string]: any } | null {
  let hasValue = false;
  const resultRow = {};
  
  for (let i = 0, len = row.length; i < len; i++) {
    let item = row[i];

    if (conv.parseParam.ignoreEmpty && item === '') {
      continue;
    }
    hasValue = true;

    let head = headRow[i];
    if (!head || head === "") {
      head = headRow[i] = "field" + (i + 1);
    }
    const convFunc = getConvFunc(head, i, conv);
    if (convFunc) {
      const convRes = convFunc(item, head, resultRow, row, i);
      if (convRes !== undefined) {
        setPath(resultRow, head, convRes, conv,i);
      }
    } else {
      // var flag = getFlag(head, i, param);
      // if (flag === 'omit') {
      //   continue;
      // }
      if (conv.parseParam.checkType) {
        const convertFunc = checkType(item, head, i, conv);
        item = convertFunc(item);
      }
      if (item !== undefined) {
        setPath(resultRow, head, item, conv,i);
      }
    }
  }
  if (hasValue) {
    return resultRow;
  } else {
    return null;
  }
}

const builtInConv: { [key: string]: CellParser } = {
  "string": stringType,
  "number": numberType,
  "omit": function () { }
}
function getConvFunc(head: string, i: number, conv: Converter): CellParser | null {
  if (conv.parseRuntime.columnConv[i] !== undefined) {
    return conv.parseRuntime.columnConv[i];
  } else {
    let flag = conv.parseParam.colParser[head];
    if (flag === undefined) {
      return conv.parseRuntime.columnConv[i] = null;
    }
    if (typeof flag === "object") {
      flag = (flag as ColumnParam).cellParser || "string";
    }
    if (typeof flag === "string") {
      flag = flag.trim().toLowerCase();
      const builtInFunc = builtInConv[flag];
      if (builtInFunc) {
        return conv.parseRuntime.columnConv[i] = builtInFunc;
      } else {
        return conv.parseRuntime.columnConv[i] = null;
      }
    } else if (typeof flag === "function") {
      return conv.parseRuntime.columnConv[i] = flag;
    } else {
      return conv.parseRuntime.columnConv[i] = null;
    }
  }
}
function setPath(resultJson: any, head: string, value: any, conv: Converter,headIdx:number) {
  if (!conv.parseRuntime.columnValueSetter[headIdx]) {
    if (conv.parseParam.flatKeys) {
      conv.parseRuntime.columnValueSetter[headIdx] = flatSetter;
    } else {
      
      if (head.indexOf(".") > -1) {
        const headArr=head.split(".");
        let jsonHead=true;
        while(headArr.length>0){
          const headCom=headArr.shift();
          if (headCom!.length===0){
            jsonHead=false;
            break;
          }
        }
        if (!jsonHead || conv.parseParam.colParser[head] && (conv.parseParam.colParser[head] as ColumnParam).flat) {
          conv.parseRuntime.columnValueSetter[headIdx] = flatSetter;
        } else {
          conv.parseRuntime.columnValueSetter[headIdx] = jsonSetter;
        }
      } else {
        conv.parseRuntime.columnValueSetter[headIdx] = flatSetter;
      }
    }
  }
  if (conv.parseParam.nullObject ===true && value ==="null"){
    value=null;
  }
  conv.parseRuntime.columnValueSetter[headIdx](resultJson, head, value);
  // flatSetter(resultJson, head, value);

}
function flatSetter(resultJson: any, head: string, value: any) {
  resultJson[head] = value;
}
function jsonSetter(resultJson: any, head: string, value: any) {
  set(resultJson, head, value);
}


function checkType(item: string, head: string, headIdx: number, conv: Converter): Function {
  if (conv.parseRuntime.headerType[headIdx]) {
    return conv.parseRuntime.headerType[headIdx];
  } else if (head.indexOf('number#!') > -1) {
    return conv.parseRuntime.headerType[headIdx] = numberType;
  } else if (head.indexOf('string#!') > -1) {
    return conv.parseRuntime.headerType[headIdx] = stringType;
  } else if (conv.parseParam.checkType) {
    return conv.parseRuntime.headerType[headIdx] = dynamicType;
  } else {
    return conv.parseRuntime.headerType[headIdx] = stringType;
  }
}

function numberType(item) {
  var rtn = parseFloat(item);
  if (isNaN(rtn)) {
    return item;
  }
  return rtn;
}

function stringType(item: string): string {
  return item.toString();
}

function dynamicType(item) {
  var trimed = item.trim();
  if (trimed === "") {
    return stringType(item);
  }
  if (numReg.test(trimed)) {
    return numberType(item);
  } else if (trimed.length === 5 && trimed.toLowerCase() === "false" || trimed.length === 4 && trimed.toLowerCase() === "true") {
    return booleanType(item);
  } else if (trimed[0] === "{" && trimed[trimed.length - 1] === "}" || trimed[0] === "[" && trimed[trimed.length - 1] === "]") {
    return jsonType(item);
  } else {
    return stringType(item);
  }
}

function booleanType(item) {
  var trimed = item.trim();
  if (trimed.length === 5 && trimed.toLowerCase() === "false") {
    return false;
  } else {
    return true;
  }
}

function jsonType(item) {
  try {
    return JSON.parse(item);
  } catch (e) {
    return item;
  }
}
