/*
 * 文件: CompressUtil.cs
 * 由SharpDevelop创建。
 * 作者: gwang
 * 
 * 功能: 描述
 * 版本:
 *     1.0.0 Created by gwang - 2016/5/14 16:06 
 */

using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
namespace CSharpUtil.Net
{

    /// <summary>
    /// 压缩工具类- 注意:由于优化能力有限,本工具限制了待压缩数据的大小不超过1MB.
    /// </summary>
    /// <exception cref="DataTooLargeException"></exception>
    public static class CompressUtil
    {
        /// <summary>
        /// 自定义异常
        /// </summary>
        public class DataTooLargeException : Exception
        {
            public DataTooLargeException()
                : base("传入的数据过大(不接受超过1M的数据)!")
            {
            }
        }
        /// <summary>
        /// 数据最大长度不宜超过10M
        /// </summary>
        const int MaxDataLenght = 1024 * 1024 * 10;
        /// <summary>
        /// 利用deflate算法压缩字节数组
        /// </summary>
        /// <param name="data">字节数组(不宜超过1M)</param>
        /// <param name="compressMode">压缩模式(压缩/解压)</param>
        /// <exception cref="OutOfMemoryException">数据不建议超过1M</exception>
        /// <returns></returns>

        public static byte[] Deflate(byte[] data, CompressionMode compressMode = CompressionMode.Compress)
        {
            if (data.Length > MaxDataLenght)
            {
                throw new DataTooLargeException();
            }
            else
            {
                byte[] buffer = null;
                using (var outms = new MemoryStream())
                {
                    using (var df = new DeflateStream(outms, compressMode, true))
                    {
                        df.Write(data, 0, data.Length);
                    }
                    outms.Position = 0;
                    buffer = outms.ToArray();
                }
                return buffer;
            }
        }

        /// <summary>
        /// 解压deflate数据
        /// </summary>
        /// <param name="data">被压缩的内容</param>
        /// <exception cref="OutOfMemoryException">数据不建议超过1M</exception>
        /// <returns></returns>
        public static byte[] InFlate(byte[] data)
        {
            // todo: 这种写法在system.io.compress模式下是支持的.
            return Deflate(data, CompressionMode.Decompress);
        }

        /// <summary>
        /// 解压deflate压缩的字符串数据
        /// </summary>
        /// <param name="data"></param>
        /// <param name="encoder">编码</param>
        /// <returns>还原到字符串</returns>
        public static string InFlate(byte[] data, Encoding encoder)
        {
            using (var inputStream = new MemoryStream(data))
            using (var gzip = new DeflateStream(inputStream, CompressionMode.Decompress))
            using (var reader = new StreamReader(gzip, encoder))
            {
                return reader.ReadToEnd();
            }
        }
        /// <summary>
        /// 解压缩
        /// </summary>
        /// <param name="sm">原始文件流</param>
        /// <returns>还原后的字节数组</returns>
        public static byte[] InFlate(Stream sm)
        {
            using (var outms = new MemoryStream())
            using (var de = new DeflateStream(sm, CompressionMode.Decompress, true))
            {
                var buf = new byte[1024];
                int len;
                while ((len = de.Read(buf, 0, buf.Length)) > 0)
                {
                    outms.Write(buf, 0, len);
                }

                return outms.ToArray();
            }
        }

        /// <summary>
        /// string=>utf8_bytes=>deflate=>base64_encode
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string zb64encode(string data)
        {
            var bytes = Encoding.UTF8.GetBytes(data);
            return Convert.ToBase64String(Deflate(bytes));
        }
        /// <summary>
        /// string=>base64_decode=>inflate=>utf8_stringfrombytes
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string zb64decode(string data)
        {
            return Encoding.UTF8.GetString(InFlate(Convert.FromBase64String(data)));
        }
    }
}
