package utils

import (
	"reflect"
	"regexp"
	"strings"
	"net/http"
	"strconv"
	"time"
	"math"
)

// IsIDCard 检查是否是身份证
func IsIDCard(str string, args ...bool) bool {
	if m, _ := regexp.MatchString("^(?:[\\dxX]{15}|[\\dxX]{18})$", str); !m {
		return false
	}

	areaMap := map[int]string{
		11 : "北京",
		12 : "天津",
		13 : "河北",
		14 : "山西",
		15 : "内蒙古",
		21 : "辽宁",
		22 : "吉林",
		23 : "黑龙江",
		31 : "上海",
		32 : "江苏",
		33 : "浙江",
		34 : "安徽",
		35 : "福建",
		36 : "江西",
		37 : "山东",
		41 : "河南",
		42 : "湖北",
		43 : "湖南",
		44 : "广东",
		45 : "广西",
		46 : "海南",
		50 : "重庆",
		51 : "四川",
		52 : "贵州",
		53 : "云南",
		54 : "西藏",
		61 : "陕西",
		62 : "甘肃",
		63 : "青海",
		64 : "宁夏",
		65 : "新疆",
		71 : "台湾",
		81 : "香港",
		82 : "澳门",
		91 : "国外",
	}

	allowLen15 := true
	if len(args) == 1 {
		allowLen15 = args[0]
	}

	province := Atoi(Substr(str, 0, 2))
	if _, ok := areaMap[province]; !ok {
		return false
	}

	//如果是15位身份证
	if len(str) == 15 && allowLen15 {
		// 如果身份证顺序码是996 997 998 999，这些是为百岁以上老人的特殊编码
		code := Substr(str, 12, 3)
		if code == "996" || code == "997" || code == "998" || code == "999" {
			str = Substr(str, 0, 6) + "18" + Substr(str, 6, 9)
		} else {
			str = Substr(str, 0, 6) + "19" + Substr(str, 6, 9)
		}
		str += idcardVerifyNumber(str)
	}
	if len(str) != 18 || !checkIdcardFormat(str) {
		return false
	}
	return idcardVerifyNumber(Substr(str, 0, 17)) == strings.ToUpper(Substr(str, 17, 1))
}

// checkIdcardFormat 检查身份证号码的格式
// 针对近期出现身份证号码使用210000000000000000注册的用户
// 验证地区码(3-6位)以及生日(6-14)位
func checkIdcardFormat(str string) bool {
	code := Atoi(Substr(str, 3, 3))
	if code == 0 {
		return false
	}
	if len(str) == 18 {
		birthYear := Atoi(Substr(str, 6, 4))
		birthMonth := Atoi(Substr(str, 10, 2))
		birthDay := Atoi(Substr(str, 10, 2))
		if birthYear < 1900 || (birthMonth == 0 || birthMonth > 12) || (birthDay == 0 || birthDay > 31) {
			return false
		}
		return true
	}
	return false
}

// idcardVerifyNumber 计算身份证校验码，根据国家标准GB 11643-1999
func idcardVerifyNumber(str string) string {
	if len(str) != 17 {
		return ""
	}

	//加权因子
	factor := []int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
	//校验码对应值
	verifyNumberList := []string{"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"}

	checkSum := 0
	for i, l := 0, len(str); i < l; i++ {
		checkSum += Atoi(Substr(str, i, 1)) * factor[i]
	}
	checkSum = checkSum % 11
	return verifyNumberList[checkSum]
}

// 是否为空
func IsEmpty(val interface{}) (b bool) {
	defer func() {
		if err := recover(); err != nil {
			b = true
		}
	}()
	v := reflect.ValueOf(val)

	switch v.Kind() {
	case reflect.Bool:
		b = (val.(bool) == false)
	case reflect.String:
		b = (val.(string) == "")
	case reflect.Array:
		fallthrough
	case reflect.Slice:
		fallthrough
	case reflect.Map:
		b = (v.Len() == 0)
	default:
		b = (v.Interface() == reflect.ValueOf(0).Interface() || v.Interface() == reflect.ValueOf(0.0).Interface())
	}

	return b
}

// 判断是否正确的ip地址
func IsIp(ip string) bool {
	ips := strings.Split(ip, ".")
	if len(ips) != 4 {
		return false
	}
	for _, v := range ips {
		i := Atoi(v, -1)
		if i < 0 || i > 255 {
			return false
		}
	}

	return true
}

//检测是否为手机设备
func IsMobileDevice(r *http.Request) bool {
	agent := r.UserAgent()
	if strings.Contains(agent,"iPhone") || strings.Contains(agent,"iPad") || strings.Contains(agent,"iOS") || strings.Contains(agent,"Android") {
		return true;
	}
	return false;
}

// 检查字符串是否为纯数字组成
func IsNumStr(s string) bool {
	if _, err := strconv.Atoi(s); err == nil {
		return true
	}
	return false
}

// 检查字符串是否为纯数字组成
func IsLongNumStr(s string) bool {
	if m, _ := regexp.MatchString("^\\d+$", s); m {
		return true
	}
	return false
}

// IsMobileNum 检查是否是手机号
func IsMobileNum(str string) bool {
	if m, _ := regexp.MatchString("^((13[\\d])|(147)|(15[\\d])|17[\\d]|(18[\\d]))[0-9]{8}$", str); m {
		return true
	}
	return false
}

// IsVersionStr 检查字符串是否是一个版本格式
func IsVersionStr(str string) bool {
	return IsVersion(str, "{2}")
}

// IsVersion 检查字符串是否是一个版本格式
func IsVersion(str string, length ...string) bool {
	vL := "{1,3}"
	if len(length) == 1 {
		vL = length[0]
	}
	if m, _ := regexp.MatchString("^\\d+(?:\\.\\d+)" + vL + "$", str); m {
		return true
	}
	return false
}

// IsDeviceCode 检查字符串是否是一个机器码
func IsDeviceCode(str string) bool {
	if m, _ := regexp.MatchString("^[a-zA-Z0-9]{40}$", str); m {
		return true
	}
	return false
}

// RegexpOK 检查字符串被正则匹配
func RegexpOK(pattern, str string) bool {
	if m, _ := regexp.MatchString(pattern, str); m {
		return true
	}
	return false
}

// DiffDay 比较两个日志间隔的天数
// 第1个日期小于第2个日期时返回正数
// 第1个日期大于第2个日期时返回负数
func DiffDay(t1, t2 string, fm ...string) (int, error) {
	format := "2006-01-02"
	if len(fm) == 1 {
		format = fm[0]
	}
	tm1, err := time.ParseInLocation(format, t1, time.Local)
	if err != nil {
		return 0, err
	}
	tm2, err := time.ParseInLocation(format, t2, time.Local)
	if err != nil {
		return 0, err
	}
	hour := tm2.Sub(tm1).Hours()
	if -24 >= hour || hour >= 24 {
		return int(math.Ceil(hour / 24)), nil
	}
	return 0, nil
}