package filterPoly

import (
	"bytes"
	"encoding/base64"
	"github.com/golang/freetype"
	"image"
	"image/draw"
	"image/jpeg"
	"image/png"
	"io/ioutil"
	"math/rand"
	"os"
	"time"
)

type WordClickVerify struct {
	Words     []rune  //生成图片的文字
	Key       string  //点选字
	ShowIndex []int   //点选顺序
	imgBase64 string  //图片
	CheckArr  [][]int //点选核对信息
}
type FontDot struct {
	Dx   int
	Dy   int
	Text string
}

const (
	width    = 320 //验证码宽度
	height   = 100 //验证码高度
	fontDPI  = 72  //屏幕每英寸的分辨率
	fontSize = 24  //字体大小

	bgImgFileDir  = "./resources/verify/imageBg/"
	fontBgFileDir = "./resources/verify/fontBg/"
	fontFileDir   = "./resources/verify/fontType/"

	verifyTextLen  = 7 //验证码文字长度
	verifyCheckNum = 3 //验证码点击选择数量
)

var (
	bgImgList   []image.Image
	fontImgList []image.Image
	fontList    [][]byte
	vCodeChan   = make(chan *WordClickVerify, 30)
)

func init() {
	// 背景图片读入内存
	bg_list, _ := ioutil.ReadDir(bgImgFileDir)
	for _, val := range bg_list {
		fileData, _ := os.Open(bgImgFileDir + val.Name())
		imgData, _ := jpeg.Decode(fileData)
		bgImgList = append(bgImgList, imgData)
		_ = fileData.Close()
	}
	//字体背景读入内存
	font_list, _ := ioutil.ReadDir(fontBgFileDir)
	for _, val := range font_list {
		fontfileData, _ := os.Open(fontBgFileDir + val.Name())
		fontimgData, _ := jpeg.Decode(fontfileData)
		fontImgList = append(fontImgList, fontimgData)
		_ = fontfileData.Close()
	}
	// 字体库读入内存
	fontInfoList, _ := ioutil.ReadDir(fontFileDir)
	for _, val := range fontInfoList {
		fontBytes, _ := ioutil.ReadFile(fontFileDir + val.Name())
		fontList = append(fontList, fontBytes)
	}
	go func() {
		for {
			vObj := CreateVerify()
			if vObj == nil {
				continue
			}
			vCodeChan <- vObj
		}
	}()
}

//CreateVerify 生成验证码
func CreateVerify() (verify *WordClickVerify) {
	verify = &WordClickVerify{}
	verify.Words = []rune(GenRandZh(verifyTextLen)) //验证码文字
	//verify.Words = []rune("一弍褕呚騍鯽三") //琖弍褕呚騍鯽鳑
	rand.Seed(time.Now().UnixNano())
	//文字背景
	fontBg := image.NewNRGBA(image.Rect(0, 0, width, height))      //字体背景
	img := fontImgList[rand.Intn(len(fontImgList))].(*image.YCbCr) //获取背景图片
	draw.Draw(fontBg, fontBg.Bounds(), img, image.ZP, draw.Src)
	//文字图层
	wordCanvas := image.NewNRGBA(image.Rect(0, 0, width, height))
	fontBytes := fontList[rand.Intn(len(fontList))]
	font, err := freetype.ParseFont(fontBytes)
	if err != nil {
		return nil
	}

	c := freetype.NewContext()
	c.SetDPI(fontDPI)
	c.SetClip(wordCanvas.Bounds())
	c.SetDst(wordCanvas)
	c.SetSrc(image.Black)

	allDots := make(map[int]FontDot) //各个文字点位置
	for i := 0; i < len(verify.Words); i++ {
		c.SetFontSize(fontSize)
		c.SetFont(font)
		w := (width - fontSize) / len(verify.Words)
		x := i*w + rand.Intn(w-fontSize)
		y := rand.Intn(height-fontSize) + fontSize
		pt := freetype.Pt(x, y) // 字出现的位置
		text := string(verify.Words[i])
		if _, err := c.DrawString(text, pt); err != nil {
			return nil
		}
		allDots[i] = FontDot{x + fontSize/2, y - fontSize/2, text}
	}
	//确定点击顺序和点击文字
	verify.CheckArr = [][]int{}
	for _, i := range rand.Perm(len(allDots)) {
		dot := allDots[i]
		if len(verify.CheckArr) >= verifyCheckNum {
			break
		}
		verify.Key += dot.Text
		verify.ShowIndex = append(verify.ShowIndex, i)
		verify.CheckArr = append(verify.CheckArr, []int{dot.Dx, dot.Dy})
	}
	imgRes := bgImgList[rand.Intn(len(bgImgList))]
	verifyBg := image.NewNRGBA(image.Rect(0, 0, width, height))
	draw.Draw(verifyBg, verifyBg.Bounds(), imgRes, image.ZP, draw.Src)
	//合并图层
	draw.DrawMask(verifyBg, verifyBg.Bounds(), fontBg, image.ZP, wordCanvas, image.ZP, draw.Over)
	buf := new(bytes.Buffer)
	_ = png.Encode(buf, verifyBg)
	verify.imgBase64 = base64.StdEncoding.EncodeToString(buf.Bytes())
	return
}

// GenRandZh 获取验证码随机文字
func GenRandZh(leng int) string {
	randInt := generateRandomNumber(19968, 40869, leng)
	str := ""
	for _, v := range randInt {
		str += string(v)
	}
	return str
}

//generateRandomNumber 从start-end中获取count个随机数
func generateRandomNumber(start int, end int, count int) []int {
	nums := make([]int, 0)
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for len(nums) < count {
		num := r.Intn((end - start)) + start
		//查重
		exist := false
		for _, v := range nums {
			if v == num {
				exist = true
				break
			}
		}
		if !exist {
			nums = append(nums, num)
		}
	}
	return nums
}
