﻿using Newtonsoft.Json;
using System.IO;
using System.Text;
using UnityEngine;

namespace ModestTree
{
    // https://github.com/modesttree/wiki/wiki/How-does-the-Asset-Bundle-extension-work
    public class AbSceneExtensionHelper
    {
        public static AssetBundleMetadata GetAssetBundleExportMetadata(string assetBundleFilePath)
        {
            using (var fileStream = new FileStream(assetBundleFilePath, FileMode.Open, FileAccess.Read))
            {
                return AbSceneExtensionHelper.GetAssetBundleExportMetadataFromStream(fileStream);
            }
        }

        public static bool WriteAssetBundleExportMetadataToFile(
            string outputPath,
            string renderAssetTypeName,
            AbScenePlatforms buildPlatform,
            bool compressed)
        {
            var metadata = new ExportMetadata()
            {
                RenderPipelineAssetType = renderAssetTypeName,
                BuildTarget = buildPlatform.ToString(),
                Compressed = compressed,
            };

            var jsonData = JsonConvert.SerializeObject(metadata);
            var byteData = Encoding.UTF8.GetBytes(jsonData);

            if (!File.Exists(outputPath))
            {
                Debug.LogError($"File expected at {outputPath} does not exists");
                return false;
            }

            using (var fileStream = new FileStream(outputPath, FileMode.Append, FileAccess.Write))
            using (var binaryWriter = new BinaryWriter(fileStream))
            {
                binaryWriter.Write(byteData);
                binaryWriter.Write(ExportMetadata.MagicNumber);
                binaryWriter.Write((long)(byteData.Length + sizeof(long) + sizeof(int)));
            }

            return true;
        }

        public static AssetBundleMetadata GetAssetBundleExportMetadataFromStream(Stream stream)
        {
            // We append some additional metadata to the end of the asset bundle files such that 
            // it doesn't interfere with Unity's asset bundle loaders while still allowing for
            // us to add additional information without adding a separate file.
            using (var streamReader = new BinaryReader(stream))
            {
                const int sizeOfHeader = sizeof(int) + sizeof(long);

                stream.Seek(-sizeOfHeader, SeekOrigin.End);
                var bytes = streamReader.ReadInt32();
                var equal = bytes == ExportMetadata.MagicNumber;
                if (!equal)
                {
                    return new AssetBundleMetadata()
                    {
                        Result = AssetBundleMetadataResult.NoMagic
                    };
                }

                var backIndex = streamReader.ReadInt64();

                if (backIndex < 0)
                {
                    return new AssetBundleMetadata()
                    {
                        Result = AssetBundleMetadataResult.InvalidOffset
                    };
                }

                stream.Seek(-backIndex, SeekOrigin.End);

                var readData = streamReader.ReadBytes((int)(backIndex - sizeOfHeader));
                var stringData = Encoding.UTF8.GetString(readData);

                var exportMetadataObject = JsonConvert.DeserializeObject<ExportMetadata>(stringData);
                return new AssetBundleMetadata()
                {
                    Result = AssetBundleMetadataResult.Success,
                    Data = exportMetadataObject
                };
            }
        }
    }

    public class AssetBundleMetadata
    {
        public AssetBundleMetadataResult Result;
        public ExportMetadata Data;
        public AssetBundle Bundle;
    }

    public enum AssetBundleMetadataResult
    {
        Success,
        NoMagic,
        InvalidOffset
    }
}
