package com.qmth.boot.tools.models;

import com.qmth.boot.tools.codec.CodecUtils;
import com.qmth.boot.tools.io.IOUtils;
import com.qmth.boot.tools.ssl.AllAcceptSSLContext;

import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * byte[]包装类，简化Codec和IO等工具类的使用，提供链式调用支持
 */
public class ByteArray {

    private byte[] value;

    private ByteArray(byte[] value) {
        this.value = value;
    }

    /**
     * 直接返回byte[]
     *
     * @return
     */
    public byte[] value() {
        return value;
    }

    /**
     * 根据classpath路径初始化，路径不需要"classpath:"前缀，且不能以/开头
     *
     * @param path 从classpath中寻找的资源文件路径
     * @return
     */
    public static ByteArray fromResource(String path) throws IOException {
        InputStream ins = ByteArray.class.getClassLoader().getResourceAsStream(path);
        try {
            return ByteArray.fromInputStream(ins);
        } finally {
            IOUtils.closeQuietly(ins);
        }
    }

    /**
     * 根据URL初始化
     *
     * @param url
     * @return
     */
    public static ByteArray fromUrl(String url) throws Exception {
        URL urlObj = new URL(url);
        InputStream ins = null;
        if (urlObj.getProtocol().equalsIgnoreCase("http")) {
            ins = new URL(url).openStream();
        } else if (urlObj.getProtocol().equalsIgnoreCase("https")) {
            HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection();
            conn.setSSLSocketFactory(AllAcceptSSLContext.get().getSocketFactory());
            conn.setHostnameVerifier((s, sslSession) -> true);
            conn.setInstanceFollowRedirects(false);
            ins = conn.getInputStream();
        } else {
            throw new RuntimeException("Invalid url protocol, should be http or https");
        }
        try {
            return ByteArray.fromInputStream(ins);
        } finally {
            IOUtils.closeQuietly(ins);
        }
    }

    /**
     * 根据输入流初始化
     *
     * @param ins
     * @return
     */
    public static ByteArray fromInputStream(InputStream ins) throws IOException {
        return new ByteArray(IOUtils.toByteArray(ins));
    }

    /**
     * 根据文件初始化
     *
     * @param file
     * @return
     */
    public static ByteArray fromFile(File file) throws IOException {
        return new ByteArray(IOUtils.toByteArray(file));
    }

    /**
     * 根据byte[]初始化
     *
     * @param value
     * @return
     */
    public static ByteArray fromArray(byte[] value) {
        return new ByteArray(value);
    }

    /**
     * 根据字符串初始化，默认使用UTF8
     *
     * @param value
     * @return
     */
    public static ByteArray fromString(String value) {
        return new ByteArray(value != null ? value.getBytes(StandardCharsets.UTF_8) : null);
    }

    /**
     * 根据字符串初始化，使用指定字符集
     *
     * @param value
     * @param charset
     * @return
     */
    public static ByteArray fromString(String value, Charset charset) {
        return new ByteArray(value != null ? value.getBytes(charset) : null);
    }

    /**
     * HexString转byte[]
     *
     * @param value
     * @return
     */
    public static ByteArray fromHexString(String value) {
        return new ByteArray(CodecUtils.fromHexString(value));
    }

    /**
     * Base64转byte[]
     *
     * @param value
     * @return
     */
    public static ByteArray fromBase64(String value) {
        return new ByteArray(CodecUtils.fromBase64(value));
    }

    /**
     * MD5文件摘要并返回byte[]
     *
     * @param file
     * @return
     */
    public static ByteArray md5(File file) {
        return new ByteArray(CodecUtils.md5(file));
    }

    /**
     * MD5字符串摘要并返回byte[]
     *
     * @param value
     * @return
     */
    public static ByteArray md5(String value) {
        return new ByteArray(CodecUtils.md5(value));
    }

    /**
     * MD5二进制摘要并返回byte[]
     *
     * @param value
     * @return
     */
    public static ByteArray md5(byte[] value) {
        return new ByteArray(CodecUtils.md5(value));
    }

    /**
     * SHA1字符串摘要并返回byte[]
     *
     * @param value
     * @return
     */
    public static ByteArray sha1(String value) {
        return new ByteArray(CodecUtils.sha1(value));
    }

    /**
     * SHA256字符串摘要并返回byte[]
     *
     * @param value
     * @return
     */
    public static ByteArray sha256(String value) {
        return new ByteArray(CodecUtils.sha256(value));
    }

    /**
     * HMAC-SHA256加密摘要并返回byte[]
     *
     * @param key   密钥字符串
     * @param value 原始字符串
     * @return
     */
    public static ByteArray hmacsha256(String key, String value) {
        return new ByteArray(
                CodecUtils.hmacsha256(key.getBytes(StandardCharsets.UTF_8), value.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * byte[]转String，使用UTF8
     *
     * @return
     */
    public String toString() {
        return value != null ? new String(value, StandardCharsets.UTF_8) : null;
    }

    /**
     * byte[]转String，使用指定字符集
     *
     * @param charset
     * @return
     */
    public String toString(Charset charset) {
        return value != null ? new String(value, charset) : null;
    }

    /**
     * byte[]转HexString
     *
     * @return
     */
    public String toHexString() {
        return CodecUtils.toHexString(value);
    }

    /**
     * byte[]转Base64
     *
     * @return
     */
    public String toBase64() {
        return CodecUtils.toBase64(value);
    }

    /**
     * 根据byte[]构造输入流
     *
     * @return
     */
    public ByteArrayInputStream toInputStream() {
        if (value != null) {
            return new ByteArrayInputStream(value);
        } else {
            return null;
        }
    }

    /**
     * byte[]到输出流
     *
     * @param ous
     * @throws IOException
     */
    public void toOutputStream(OutputStream ous) throws IOException {
        if (value != null) {
            ous.write(value);
        }
    }

    /**
     * byte[]写入到文件
     *
     * @param file
     * @throws IOException
     */
    public void toFile(File file) throws IOException {
        if (value != null) {
            InputStream ins = new ByteArrayInputStream(value);
            FileOutputStream ous = new FileOutputStream(file);
            IOUtils.copy(ins, ous);
            IOUtils.closeQuietly(ins);
            IOUtils.closeQuietly(ous);
        }
    }

}

