import nj from '../core';
import * as tools from '../utils/tools';
import { Template } from '../interface';
const REGEX_NUM = /^(-?([0-9]+[.]?[0-9]+)|[0-9])$/;

//提取style内参数
export function styleProps(obj) {
  //If the parameter is a style object,then direct return.
  if (tools.isObject(obj) || tools.isArray(obj) || tools.isNumber(obj)) {
    return obj;
  }

  //参数为字符串
  const pattern = /([^\s:]+)[\s]?:[\s]?([^;]+)[;]?/g;
  let matchArr, ret;

  while ((matchArr = pattern.exec(obj))) {
    let key = matchArr[1];
    const value = matchArr[2];

    if (!ret) {
      ret = {};
    }

    //Convert to lowercase when style name is all capital.
    if (/^[A-Z-]+$/.test(key)) {
      key = key.toLowerCase();
    }

    //将连字符转为驼峰命名
    key = tools.camelCase(key);

    ret[key] = REGEX_NUM.test(value) ? Number(value) : value;
  }

  return ret;
}

//Get value from multiple datas
export function getData(this: Template.Context | void, prop, data, hasSource?) {
  let value, obj;
  if (!data) {
    data = (this as Template.Context).data;
  }

  for (let i = 0, l = data.length; i < l; i++) {
    obj = data[i];
    if (obj) {
      value = obj[prop];
      if (value !== undefined) {
        if (hasSource) {
          return {
            source: obj,
            value,
            prop,
            _njSrc: true
          };
        }

        return value;
      }
    }
  }
}

export function getAccessorData(fn, context) {
  if (fn == null) {
    return fn;
  }

  if (fn._njTmpl) {
    //模板函数
    return fn.call(context);
  } else {
    //普通函数
    return fn.call(context.$this, context);
  }
}

export function getElement(name, global, nameO, context, subName?) {
  let element;
  if (!context.icp) {
    element = global.cp[name];
  } else {
    element = getData(nameO, context.icp);
    if (!element) {
      element = global.cp[name];
    }
  }

  if (subName != null && element) {
    element = element[subName];
  }

  return element ? element : nameO;
}

export function getElementRefer(refer, name, global, nameO, context) {
  return refer != null
    ? tools.isString(refer)
      ? getElement(refer.toLowerCase(), global, refer, context)
      : refer
    : getElement(name, global, nameO, context);
}

export function getElementName(refer, name) {
  return refer != null && refer !== '' ? refer : name;
}

export function addArgs(props, dataRefer) {
  const { args } = props;
  if (args) {
    for (let i = args.length; i--; ) {
      dataRefer.unshift(args[i]);
    }
  }
}

//Rebuild local variables in the new context
export function newContext(context, params) {
  if (!params) {
    return context;
  }

  return tools.assign({}, context, {
    data: params.data ? tools.arrayPush(params.data, context.data) : context.data,
    parent: params.newParent ? context : context.parent,
    index: params.index != null ? params.index : context.index,
    item: params.item != null ? params.item : context.item
  });
}

//修正属性名
export function fixPropName(name) {
  switch (name) {
    case 'class':
      name = 'className';
      break;
    case 'for':
      name = 'htmlFor';
      break;
  }

  return name;
}

//合并字符串属性
export function assignStrProps(target, ...params) {
  let ret = '';
  const retObj = tools.assign(target, ...params);

  for (const k in retObj) {
    const v = retObj[k];
    ret += ' ' + k + (k !== v ? '="' + v + '"' : ' ');
  }
  return ret;
}

//创建扩展标签子节点函数
export function exRet(global, context, fn) {
  return function(param) {
    return fn(global, context, param);
  };
}

function _getLocalComponents(localConfigs) {
  let icp;
  if (localConfigs && localConfigs.components) {
    icp = localConfigs.components;
    if (!tools.isArray(icp)) {
      icp = [icp];
    }
  }

  return icp;
}

//构建可运行的模板函数
export function tmplWrap(configs, main) {
  return function(this: Template.Context, param1, param2) {
    const ctx = this,
      data = tools.arraySlice(arguments);

    return main(
      configs,
      ctx && ctx._njCtx
        ? tools.assign({}, ctx, {
            data: tools.arrayPush(data, ctx.data)
          })
        : {
            data,
            getData,
            get $this() {
              return this.data[this.data.length - 1];
            },
            d: getData,
            icp: _getLocalComponents(param1 && param1._njParam ? param2 : param1),
            _njCtx: true
          }
    );
  };
}

function levelSpace(context) {
  if (context.level == null) {
    return '';
  }

  let ret = '';
  for (let i = 0; i < context.level; i++) {
    ret += '  ';
  }
  return ret;
}

function firstNewline(context) {
  return context.index == null ? '' : context.index == 0 ? '' : '\n';
}

function createElementApply(p) {
  return nj.createElement.apply(null, p);
}

function callFilter(filter) {
  return filter.source ? filter.value.bind(filter.source) : filter;
}

//创建模板函数
export function template(fns, tmplKey?) {
  const configs: any = {
    tmplKey,
    us: fns.useString,
    x: nj.extensions,
    f: nj.filters,
    np: tools.noop,
    tf: tools.throwIf,
    wn: tools.warn,
    n: newContext,
    c: getAccessorData,
    sp: styleProps,
    r: exRet,
    e: getElement,
    er: getElementRefer,
    en: getElementName,
    aa: addArgs,
    an: tools.assign,
    g: nj.global,
    cf: callFilter
  };

  if (!configs.us) {
    configs.h = nj.createElement;
    configs.H = createElementApply;
    configs.cp = nj.components;
  } else {
    configs.ans = assignStrProps;
    configs.es = nj.escape;
    configs.ls = levelSpace;
    configs.fl = firstNewline;
  }

  tools.each(
    fns,
    (v, k) => {
      if (k === 'main') {
        //将每个主函数构建为可运行的模板函数
        configs[k] = tmplWrap(configs, v);
        tools.defineProps(configs[k], {
          _njTmpl: {
            value: true
          }
        });
        configs['_' + k] = v;
      } else if (k.indexOf('fn') === 0) {
        //扩展标签函数
        configs[k] = v;
      }
    },
    false
  );

  return configs;
}
