using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;
using UnityEngine.SceneManagement;
//using X2Interface;

namespace Giant.Resource
{
    public class AssetBundleStream : Stream
    {
        Stream m_fileStream;
        bool m_isEncrypt = false;
        long m_realkey;
        public AssetBundleStream(string path)
        {
            m_fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
            m_isEncrypt = isABEncrypt(m_fileStream);
            if (m_isEncrypt)
            {
                m_realkey = secretKey;// ^ m_fileStream.Length;
            }
        }

        static bool isABEncrypt(Stream abstream)
        {
            var flagbuf = new byte[unityfsFlag.Length];
            if (abstream.Read(flagbuf, 0, unityfsFlag.Length) == unityfsFlag.Length)
            {
                //m_isEncrypt = true;
                for (int i = 0; i < unityfsFlag.Length; i++)
                {
                    if (flagbuf[i] != unityfsFlag[i])
                    {

                        abstream.Seek(0, SeekOrigin.Begin);
                        return true;
                    }
                }
            }
            abstream.Seek(0, SeekOrigin.Begin);
            return false;
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (m_fileStream != null)
                m_fileStream.Dispose();
        }
        public override bool CanRead => m_fileStream.CanRead;

        public override bool CanSeek => m_fileStream.CanSeek;

        public override bool CanWrite => m_fileStream.CanWrite;

        public override long Length => m_fileStream.Length;

        public override long Position
        {
            get => m_fileStream.Position;
            set
            {
                m_fileStream.Position = value;
            }
        }

        public override void Flush()
        {
            throw new NotImplementedException();
        }

        int ReadEncryptData(byte[] buffer, int offset, int count)
        {
            var pos = Position;
            int readsize = m_fileStream.Read(buffer, offset, count);

            if (pos >= encryptheadsize)
                return readsize;


            long realkey = m_realkey;

            unsafe
            {
                fixed (byte* pbuff = buffer)
                //fixed ()
                {
                    byte* pkey = (byte*)&realkey;
                    byte* pdest = pbuff;
                    //ulong key = *(ulong*)pkey;
                    int i = (int)(pos % 8);
                    //int left = 8 - i;

                    int leftsize = System.Math.Min(readsize, encryptheadsize - (int)pos);

                    if (i > 0)
                    {
                        for (; i < 8; i++)
                        {
                            *pdest ^= pkey[i];
                            pdest++;
                            leftsize--;
                        }
                    }
                    while (leftsize >= 8)
                    {
                        *((long*)pdest) ^= realkey;
                        pdest += 8;
                        leftsize -= 8;
                    }
                    for (i = 0; i < leftsize; i++)
                    {
                        pdest[i] ^= pkey[i];
                    }


                }
            }

            return readsize;
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (null == m_fileStream)
                return 0;

            //Debug.LogError("Read fs");
            if (!m_isEncrypt)
                return m_fileStream.Read(buffer, offset, count);
            else
                return ReadEncryptData(buffer, offset, count);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            if (null == m_fileStream) {
                return 0;
            }
            return m_fileStream.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }

        //static byte[] secretKey = new byte[] { 0x5A,0x7A,0x27,0xC2,0x47,0x61,0x43,0xCB };
        const long secretKey = 0x5A7A27C2476143CB;
        static byte[] unityfsFlag = System.Text.Encoding.UTF8.GetBytes("UnityFS");
        const int encryptheadsize = 32 * 1024;

        //const int encryptblocksize = 32 * 1024;
        public static bool EncryptFile(string srcpath, string destpath)
        {
            var srcfile = new FileStream(srcpath, FileMode.Open, FileAccess.Read);
            if (isABEncrypt(srcfile))
            {
                srcfile.Dispose();
                return false;
            }

            var destfile = new FileStream(destpath, FileMode.OpenOrCreate, FileAccess.Write);
            var buf = new byte[32 * 1024];
            long realkey = secretKey;// ^ srcfile.Length;
            int curencryptsize = 0;
            //destfile.Write(encryptFalg, 0, encryptFalg.Length);
            while (true)
            {
                int readsize = srcfile.Read(buf, 0, buf.Length);
                if (readsize == 0)
                    break;
                if (curencryptsize < encryptheadsize)
                {
                    unsafe
                    {
                        fixed (byte* pbuff = buf)
                        {
                            long* pdest = (long*)pbuff;

                            while (curencryptsize < encryptheadsize)
                            {
                                *pdest ^= realkey;

                                curencryptsize += 8;
                                pdest++;
                            }

                        }
                    }
                }


                destfile.Write(buf, 0, readsize);
            }
            destfile.Dispose();
            srcfile.Dispose();
            return true;

        }
    }
}