package com.zhiqiyun.open.core.service.impl.captcha;


import com.zhiqiyun.open.config.RedisKeys;
import com.zhiqiyun.open.core.service.impl.captcha.filter.predefined.*;
import com.zhiqiyun.open.core.service.impl.captcha.font.FontFactory;
import com.zhiqiyun.open.core.service.impl.captcha.font.RandomFontFactory;
import com.zhiqiyun.open.core.service.CaptchaService;
import com.zhiqiyun.open.core.service.impl.captcha.background.BackgroundFactory;
import com.zhiqiyun.open.core.service.impl.captcha.background.SingleColorBackgroundFactory;
import com.zhiqiyun.open.core.service.impl.captcha.color.ColorFactory;
import com.zhiqiyun.open.core.service.impl.captcha.color.SingleColorFactory;
import com.zhiqiyun.open.core.service.impl.captcha.filter.FilterFactory;
import com.zhiqiyun.open.core.service.impl.captcha.text.renderer.BestFitTextRenderer;
import com.zhiqiyun.open.core.service.impl.captcha.text.renderer.TextRenderer;
import com.zhiqiyun.open.core.service.impl.captcha.word.AdaptiveRandomWordFactory;
import com.zhiqiyun.open.core.service.impl.captcha.word.WordFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class CaptchaServiceImpl implements CaptchaService {

    private final FontFactory fontFactory = new RandomFontFactory();
    private final WordFactory wordFactory = new AdaptiveRandomWordFactory();
    private final BackgroundFactory backgroundFactory = new SingleColorBackgroundFactory();
    private final TextRenderer textRenderer = new BestFitTextRenderer();
    private final ColorFactory colorFactory = new SingleColorFactory(new Color(25, 60, 170));

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 产生一个验证码图片二进制流
     */
    @Override
    public String generateCaptchaBase64(String token) {
        return this.generateCaptchaBase64(token, 120, 60);
    }

    @Override
    public String generateCaptchaBase64(String token, int width, int height) {
        try {
            BufferedImage image = generateCaptcha(token, width, height);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(image, "png", baos);
            String data = Base64.encodeBase64String(baos.toByteArray());
            data = "data:image/png;base64," + data;
            return data;
        } catch (Exception e) {
            log.error("convert image to byte error: ", e);
        }
        return null;
    }

    /**
     * 比较验证码
     *
     * @param captcha
     * @param token
     * @param destroySession
     */
    @Override
    public boolean compareCaptcha(String captcha, String token, boolean destroySession) {
        String redisKey = String.format(RedisKeys.CAPTCHA, token);
        log.debug(redisKey);
        BoundValueOperations<String, String> boundValueOperations = this.redisTemplate.boundValueOps(redisKey);

        String extistCaptcha = boundValueOperations.get();
        if (destroySession) {
            this.redisTemplate.delete(redisKey);
        }
        log.debug("SESSION captcha " + extistCaptcha);
        log.debug("INPUT captcha " + captcha);
        return StringUtils.equalsIgnoreCase(extistCaptcha, captcha);
    }


    private BufferedImage generateCaptcha(String token, int width, int height) {
        try {

            Random random = new Random();
            FilterFactory filterFactory = new CurvesRippleFilterFactory(colorFactory);
            switch (random.nextInt(4)) {
                case 0:
                    filterFactory = new CurvesRippleFilterFactory(colorFactory);
                    break;
                case 1:
                    filterFactory = new MarbleRippleFilterFactory();
                    break;
                case 2:
                    filterFactory = new DoubleRippleFilterFactory();
                    break;
                case 3:
                    filterFactory = new WobbleRippleFilterFactory();
                    break;
                case 4:
                    filterFactory = new DiffuseRippleFilterFactory();
                    break;
                default:
                    break;

            }

            BufferedImage bufImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            backgroundFactory.fillBackground(bufImage);
            String word = wordFactory.getNextWord();
            textRenderer.draw(word, bufImage, fontFactory, colorFactory);
            bufImage = filterFactory.applyFilters(bufImage);

            Captcha captcha = new Captcha(word, bufImage);
            String redisKey = String.format(RedisKeys.CAPTCHA, token);
            BoundValueOperations<String, String> boundValueOperations = this.redisTemplate.boundValueOps(redisKey);
            boundValueOperations.set(captcha.getChallenge());
            boundValueOperations.expire(5, TimeUnit.MINUTES);

            return captcha.getImage();
        } catch (Exception e) {
            log.error("error", e);
            return null;
        }
    }
}
