var fs= require('fs');
var crypto = require('crypto');
var sys = require('sys');

var utils = module.exports;

/**
 * 判断数组中是否存在指定的元素
 * @param  {[type]} obj [description]
 * @return {[type]}     [description]
 */
Array.prototype.contains = function (obj) {
  var i = this.length;
  while (i--) {
    if (this[i] === obj) {
        return true;
    }
  }
  return false;
};

/**
 * 删除数组中指定的元素
 * @param  {[type]} obj [description]
 * @return {[type]}     [description]
 */
Array.prototype.remove = Array.prototype.deleteMember = function (obj) {
  for(var i=0;i<this.length;i++) {
    if (this[i] === obj) {
        this.splice(i,1);
        return;
    }
  }
};

Date.prototype.format = function (style) {
  return utils.formatDate(this, style);
};

Date.prototype.diff = function (endTime, diffType) {
  return utils.dateDiff(this, endTime, diffType);
};

Date.prototype.toJSON = function () {
  return utils.formatDate(this, 'yyyy-MM-dd hh:mm:ss');
};

String.prototype.trim = function () {
  return this.replace(/(^\s*)|(\s*$)/g,"");
};

String.prototype.toDate = function () {
  return new Date(this.replace(/-/g, '/'));
};

String.prototype.toIntArray = function (exclude) {
  return utils.strToIntArray(this, exclude);
};

String.prototype.endWith = function (strSub) {
  return utils.endWith(this, strSub);
};

String.prototype.startWith = function (strSub) {
  return utils.startWith(this, strSub);
};

String.prototype.haveEmoji = function () {
  return utils.isExistEmoji(this);
};

String.prototype.haveEspecialChar = function () {
  return utils.haveEspecialChar(this);
};

//计算字符串长度(英文占1个字符，中文汉字占2个字符)
String.prototype.gblen = function() {
  var len = 0;
  for (var i=0; i<this.length; i++) {
    if (this.charCodeAt(i)>127 || this.charCodeAt(i)==94) {
      len += 2;
    } else {
      len ++;
    }
  }
  return len;
};

/**
 * 格式化日期
 * @param  {Date}   date  日期对象
 * @param  {String} style 日期格式，例如：yyyy-MM-dd hh:mm:ss
 * @return {String}       格式化之后的字符串
 */
utils.formatDate = function(date, style) {
  var y = date.getFullYear();
  var M = "0" + (date.getMonth() + 1);
  M = M.substring(M.length - 2);
  var d = "0" + date.getDate();
  d = d.substring(d.length - 2);
  var h = "0" + date.getHours();
  h = h.substring(h.length - 2);
  var m = "0" + date.getMinutes();
  m = m.substring(m.length - 2);
  var s = "0" + date.getSeconds();
  s = s.substring(s.length - 2);
  return style.replace('yyyy', y).replace('MM', M).replace('dd', d).replace('hh', h).replace('mm', m).replace('ss', s).replace('S', date.getMilliseconds());
};

/**
 * 字符串转换为日期
 * @param  {[type]} str [description]
 * @return {[type]}     [description]
 */
utils.strToDate = function(str) {
  return new Date(str.replace(/-/g, '/'));
};

/**
 * 出掉字符串首尾的空格
 * @param  {String} str 处理前的字符串
 * @return {String}     处理后的字符串
 */
utils.trimStr = function(str) {
  return str.replace(/(^\s*)|(\s*$)/g,"");
};

/**
 * 判断是否是整数
 * @param  {[type]}  i [description]
 * @return {Boolean}   [description]
 */
utils.isInt = function(i) {
  return i == parseInt(i);
};

/**
 * 判断是否是有效的手机号
 * @param  {String}  num 手机号字符串
 * @return {Boolean}     判断结果
 */
utils.isPhone = function(num) {
  var partten = /^1\d{10}$/;
  if(partten.test(num)) return true;
  return false;
};

/**
 * 判断是否是有效的邮箱地址
 * @param  {String}  mail 邮箱地址字符串
 * @return {Boolean}      判断结果
 */
utils.isEmail = function(mail) {
  var partten = /^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
  if(partten.test(mail)) return true;
  return false;
};

utils.saveJSONToFile = function(jsonObj, filePath, cb) {
  try {
    if(fs.existsSync(filePath)) fs.unlinkSync(filePath);
    var writeStream = fs.createWriteStream(filePath, {flags: 'a'});
    try {
      writeStream.write(JSON.stringify(jsonObj,null,2));
    } catch(err) {
      global.logger.logError(err);
    }
    writeStream.end();
    writeStream = null;
  } catch(e) {
    global.logger.logError(e);
    if(cb) cb(e);
    return;
  }
  if(cb) cb();
};

/**
 * 字符串是否以指定的子字符串结尾
 * @param  {String} strSrc 源字符串
 * @param  {String} strSub 子字符串
 * @return {bool}          判断结果
 */
utils.endWith = function(strSrc, strSub){
  if(strSub==null||strSub==""||strSrc.length==0||strSub.length>strSrc.length)
    return false;
  if(strSrc.substring(strSrc.length-strSub.length)==strSub)
    return true;
  else
    return false;
  return true;
};

/**
 * 字符串是否以指定的子字符串开头
 * @param  {String} strSrc 源字符串
 * @param  {String} strSub 子字符串
 * @return {bool}          判断结果
 */
utils.startWith = function(strSrc, strSub){
  if(strSub==null||strSub==""||strSrc.length==0||strSub.length>strSrc.length)
    return false;
  if(strSrc.substr(0,strSub.length)==strSub)
    return true;
  else
    return false;
  return true;
};

/**
 * 将字符串转换成二进制数组
 * @param str
 * @returns {Array}
 */
utils.strToBinArr = function(str){
  var rs = [];
  ([].slice.call(str)).forEach(function(char){
      rs.push(char.charCodeAt(0).toString(2));
  });
  return rs;
};

/**
 * 将二进制数组转换成字符串
 * @param bin
 * @returns {number}
 */
utils.binArrToStr = function(bin){
  var ds = 0;
  for(var i = bin.length - 1; i >= 0; --i){
     ds += parseInt(bin[i], 10) * Math.pow(2, bin.length - 1 - i);
  }
  return ds;
};

/**
 * 将已逗号分隔的字符串拆分为整数数组，并去掉重复项
 * @param  {[type]} str     [description]
 * @param  {[type]} exclude [description]
 * @return {[type]}         [description]
 */
utils.strToIntArray = function(str, exclude) {
  var ret = [];
  if(!str || utils.trimStr(str)=='') return ret;
  if((''+str).indexOf(',')<0) return [parseInt(str)];
  var arr = (''+str).split(',');
  for(var i=0;i<arr.length;i++) {
    if(!utils.isInt(arr[i])) continue;
    var value = parseInt(arr[i]);
    if(exclude && value==exclude) continue;
    var isExist = false;
    for(var j=0;j<ret.length;j++) {
      if(value==ret[j]) {
        isExist = true;
        break;
      }
    }
    if(isExist) continue;
    ret.push(value);
  }
  return ret;
};

/**
 * 对象属性比较，用来进行数组排序
 * @param  {[type]} propertyName [description]
 * @return {[type]}              [description]
 */
utils.compare = function(propertyName) {
  return function (object1, object2) {
    var value1 = object1[propertyName];
    var value2 = object2[propertyName];
    if (value2 < value1) {
      return 1;
    }
    else if (value2 > value1) {
      return -1;
    }
    else {
      return 0;
    }
  }
};

//字符中是否存在Emoji表情字符
utils.isExistEmoji = function(str) {
  for (var i=0; i<str.length; i++) {
    var c = str.charCodeAt(i);
    if ((c >= 0xE001 && c <= 0xE05A) || (0xE101<=c && c<=0xE15A)
      || (c >= 0xE201 && c <= 0xE253) || (0xE301<=c && c<=0xE34D)
      || (c >= 0xE401 && c <= 0xE44C) || (0xE501<=c && c<=0xE537)) {
      return true;
    }
  }
  return false;
};

/**
 * 获得时间差,时间格式为 年-月-日 小时:分钟:秒 或者 年/月/日 小时：分钟：秒
 * 返回精度为：second，minute，hour，day
 */
utils.dateDiff = function(startTime, endTime, diffType) {
    //将计算间隔类性字符转换为小写
    diffType = diffType.toLowerCase();
    var sTime = new Date(startTime); //开始时间
    var eTime = new Date(endTime); //结束时间
    //作为除数的数字
    var divNum = 1;
    switch (diffType) {
        case "second":
            divNum = 1000;
            break;
        case "minute":
            divNum = 1000 * 60;
            break;
        case "hour":
            divNum = 1000 * 3600;
            break;
        case "day":
            divNum = 1000 * 3600 * 24;
            break;
        default:
            break;
    }
    return parseInt((eTime.getTime() - sTime.getTime()) / parseInt(divNum));
};

//是否包含特殊字符
utils.haveEspecialChar = function(str) {
  if(str.indexOf('*')>0||str.indexOf('%')>0||str.indexOf('~')>0||str.indexOf('!')>0
    ||str.indexOf('@')>0||str.indexOf('#')>0||str.indexOf('$')>0||str.indexOf('^')>0
    ||str.indexOf('&')>0||str.indexOf('(')>0||str.indexOf(')')>0||str.indexOf('`')>0
    ||str.indexOf('{')>0||str.indexOf('}')>0||str.indexOf('[')>0||str.indexOf(']')>0
    ||str.indexOf(':')>0||str.indexOf('"')>0||str.indexOf(';')>0||str.indexOf('\'')>0
    ||str.indexOf('<')>0||str.indexOf('>')>0||str.indexOf('?')>0||str.indexOf(',')>0
    ||str.indexOf('.')>0||str.indexOf('/')>0||str.indexOf('|')>0||str.indexOf('\\')>0
    ||str.indexOf('  ')>0) return true;
  return false;
};

//MD5加密
utils.md5 = function(str) {
  var md5 = crypto.createHash('md5');
  md5.update(str, 'utf8');
  return md5.digest('hex');
};

//DES加密
utils.encryptDES = utils.EncryptDES = function (data, key) {
  var cipher = crypto.createCipheriv('des-ecb', key, new Buffer(0));
  cipher.setAutoPadding(true);
  var ciph = cipher.update(data, 'utf8', 'hex');  
  ciph += cipher.final('hex');
  return ciph.toUpperCase();
};

//DES解密
utils.decryptDES = utils.DecryptDES = function (data, key) {    
  var decipher = crypto.createDecipheriv('des-ecb', key, new Buffer(0)); 
  var txt = decipher.update(data, 'hex', 'utf8');  
  txt += decipher.final('utf8');                
  return txt;
};

/**
 * AES加密
 * @param {[type]} dataStr [description]
 * @param {[type]} key     [description]
 * @param {[type]} iv      [description]
 */
utils.encryptAES = function(dataStr, key, iv) {
  let cipherChunks = [];
  let cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
  cipher.setAutoPadding(true);
  cipherChunks.push(cipher.update(dataStr, 'utf8', 'base64'));
  cipherChunks.push(cipher.final('base64'));
  return cipherChunks.join('');
};

/**
 * AES解密
 * @param {[type]} dataStr [description]
 * @param {[type]} key     [description]
 * @param {[type]} iv      [description]
 */
utils.decryptAES = function(dataStr, key, iv) {
  let cipherChunks = [];
  let decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
  decipher.setAutoPadding(true);
  cipherChunks.push(decipher.update(dataStr, 'base64', 'utf8'));
  cipherChunks.push(decipher.final('utf8'));
  return cipherChunks.join('');
};

var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var base64DecodeChars = new Array(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
/**
 * base64编码
 * @param {Object} str
 */
utils.base64encode = function(str){
    var out, i, len;
    var c1, c2, c3;
    len = str.length;
    i = 0;
    out = "";
    while (i < len) {
        c1 = str.charCodeAt(i++) & 0xff;
        if (i == len) {
            out += base64EncodeChars.charAt(c1 >> 2);
            out += base64EncodeChars.charAt((c1 & 0x3) << 4);
            out += "==";
            break;
        }
        c2 = str.charCodeAt(i++);
        if (i == len) {
            out += base64EncodeChars.charAt(c1 >> 2);
            out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
            out += base64EncodeChars.charAt((c2 & 0xF) << 2);
            out += "=";
            break;
        }
        c3 = str.charCodeAt(i++);
        out += base64EncodeChars.charAt(c1 >> 2);
        out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
        out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
        out += base64EncodeChars.charAt(c3 & 0x3F);
    }
    return out;
}

/**
 * base64解码
 * @param {Object} str
 */
utils.base64decode = function(str){
    var c1, c2, c3, c4;
    var i, len, out;
    len = str.length;
    i = 0;
    out = "";
    while (i < len) {
        /* c1 */
        do {
            c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
        }
        while (i < len && c1 == -1);
        if (c1 == -1)
            break;
        /* c2 */
        do {
            c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
        }
        while (i < len && c2 == -1);
        if (c2 == -1)
            break;
        out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
        /* c3 */
        do {
            c3 = str.charCodeAt(i++) & 0xff;
            if (c3 == 61)
                return out;
            c3 = base64DecodeChars[c3];
        }
        while (i < len && c3 == -1);
        if (c3 == -1)
            break;
        out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
        /* c4 */
        do {
            c4 = str.charCodeAt(i++) & 0xff;
            if (c4 == 61)
                return out;
            c4 = base64DecodeChars[c4];
        }
        while (i < len && c4 == -1);
        if (c4 == -1)
            break;
        out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
    }
    return out;
}

utils.Base64Binary = {
  _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

  /* will return a  Uint8Array type */
  decodeArrayBuffer: function(input) {
    var bytes = (input.length/4) * 3;
    var ab = new ArrayBuffer(bytes);
    this.decode(input, ab);

    return ab;
  },

  removePaddingChars: function(input){
    var lkey = this._keyStr.indexOf(input.charAt(input.length - 1));
    if(lkey == 64){
      return input.substring(0,input.length - 1);
    }
    return input;
  },

  decode: function (input, arrayBuffer) {
    //get last chars to see if are valid
    input = this.removePaddingChars(input);
    input = this.removePaddingChars(input);

    var bytes = parseInt((input.length / 4) * 3, 10);

    var uarray;
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    var i = 0;
    var j = 0;

    if (arrayBuffer)
      uarray = new Uint8Array(arrayBuffer);
    else
      uarray = new Uint8Array(bytes);

    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

    for (i=0; i<bytes; i+=3) {
      //get the 3 octects in 4 ascii chars
      enc1 = this._keyStr.indexOf(input.charAt(j++));
      enc2 = this._keyStr.indexOf(input.charAt(j++));
      enc3 = this._keyStr.indexOf(input.charAt(j++));
      enc4 = this._keyStr.indexOf(input.charAt(j++));

      chr1 = (enc1 << 2) | (enc2 >> 4);
      chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
      chr3 = ((enc3 & 3) << 6) | enc4;

      uarray[i] = chr1;
      if (enc3 != 64) uarray[i+1] = chr2;
      if (enc4 != 64) uarray[i+2] = chr3;
    }

    return uarray;
  }
}
