package gaf3.core.services.verify.util

import java.awt.Color
import java.awt.Font
import java.awt.Graphics
import java.awt.RenderingHints
import java.awt.geom.AffineTransform
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.util.*
import javax.imageio.ImageIO
import kotlin.math.PI
import kotlin.math.sin

object VerifyCodeHelper {

    private val VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"

    //生成验证码字符串，可以设置字符串长度
    fun generateCode(size: Int = 4, source: String = VERIFY_CODES): String{
        val result = StringBuilder()
        for(i in 1..size)
            result.append(source.random())
        return result.toString()
    }

    //生成验证码图片
    fun generateImage(code:String, width: Int = 200, height: Int = 80): ByteArray {
        val image = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
        val g2d = image.createGraphics()
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)

        g2d.color = Color.GRAY
        g2d.fillRect(0, 0, width, height)

        val c = getRandomColor(200, 250)
        g2d.color = c
        g2d.fillRect(0,2,width, height-4)

        g2d.color = getRandomColor(160, 200)
        for(i in 0 until 20){
            val x = (1..width).random()
            val y = (1..height).random()
            val xl = (1..6).random()
            val yl = (1..12).random()
            g2d.drawLine(x, y, x + xl + 40, y + yl + 20)
        }

        val yawpRate = 0.05f
        val area = (yawpRate * width * height).toInt()
        for(i in 0 until area){
            val x = (0 until width).random()
            val y = (0 until height).random()
            val rgb = getRandomIntColor()
            image.setRGB(x, y, rgb)
        }

        shear(g2d, width, height, c)

        g2d.color = getRandomColor(100, 160)
        val fontSize = height - 4
        g2d.font = Font("Algerian", Font.ITALIC, fontSize)
        val codeSize = code.length
        for(i in (0 until codeSize)){
            val affine = AffineTransform()
            val random = Random()
            affine.setToRotation(PI / 4 * random.nextDouble() * (if(random.nextBoolean()) 1 else -1), (width.toDouble() / codeSize) * i + fontSize/2, height.toDouble()/2)
            g2d.transform = affine
            g2d.drawChars(code.toCharArray(), i, 1,((width-10) / codeSize) * i + 5, height/2 + fontSize/2 - 10)
        }
        g2d.dispose()
        val output = ByteArrayOutputStream()
        ImageIO.write(image, "jpg", output);
        val result = output.toByteArray()
        output.close()
        return result
    }

    private fun getRandomColor(fc: Int, bc: Int): Color {
        val f = if(fc > 255) 255 else fc
        val b = if(bc > 255) 255 else bc
        return Color((f..b).random(), (f..b).random(), (f..b).random())
    }

    private fun getRandomIntColor(): Int{
        var color = 0
        val rgb = arrayOf((0 until 255).random(), (0 until 255).random(), (0 until 255).random())
        for(c in rgb){
            color = (color shl 8) and c
        }
        return color
    }

    private fun shear(g: Graphics, w: Int, h: Int, color: Color){
        shearX(g, w, h, color)
        shearY(g, w, h, color)
    }

    private fun shearX(g: Graphics, w: Int, h: Int, color: Color) {
        val period = (0..1).random()
        val frames = 1;
        val phase = (0..1).random()

        for (i in 0 until h) {
            val d = (period ushr 1).toDouble() * sin(i.toDouble() / period.toDouble() + (6.2831853071795862 * phase.toDouble()) / frames.toDouble());
            g.copyArea(0, i, w, 1, d.toInt(), 0);
            g.setColor(color);
            g.drawLine(d.toInt(), i, 0, i);
            g.drawLine(d.toInt() + w, i, w, i);
        }
    }

    private fun shearY(g: Graphics, w: Int, h: Int, color: Color) {
        val period = (0 until 40).random()+ 10;
        val frames = 20;
        val phase = 7;
        for (i in 0 until w) {
            val d = (period ushr 1).toDouble() * Math.sin(i.toDouble() / period.toDouble() + (6.2831853071795862 * phase.toDouble()) / frames.toDouble());
            g.copyArea(i, 0, 1, h, 0, d.toInt());
            g.setColor(color);
            g.drawLine(i, d.toInt(), i, 0);
            g.drawLine(i, d.toInt() + h, i, h);
        }

    }

}