using System;
using System.IO;
using System.Linq;
using SharpCompress.IO;

namespace SharpCompress.Common.SevenZip
{
    internal class SevenZipFilePart : FilePart
    {
        private CompressionType? _type;
        private readonly Stream _stream;
        private readonly ArchiveDatabase _database;

        internal SevenZipFilePart(Stream stream, ArchiveDatabase database, int index, CFileItem fileEntry, ArchiveEncoding archiveEncoding)
           : base(archiveEncoding)
        {
            _stream = stream;
            _database = database;
            Index = index;
            Header = fileEntry;
            if (Header.HasStream)
            {
                Folder = database._folders[database._fileIndexToFolderIndexMap[index]];
            }
        }

        internal CFileItem Header { get; }
        internal CFolder? Folder { get; }
        internal int Index { get; }

        internal override string FilePartName => Header.Name;

        internal override Stream? GetRawStream()
        {
            return null;
        }

        internal override Stream GetCompressedStream()
        {
            if (!Header.HasStream)
            {
                return null!;
            }
            var folderStream = _database.GetFolderStream(_stream, Folder!, _database.PasswordProvider);

            int firstFileIndex = _database._folderStartFileIndex[_database._folders.IndexOf(Folder!)];
            int skipCount = Index - firstFileIndex;
            long skipSize = 0;
            for (int i = 0; i < skipCount; i++)
            {
                skipSize += _database._files[firstFileIndex + i].Size;
            }
            if (skipSize > 0)
            {
                folderStream.Skip(skipSize);
            }
            return new ReadOnlySubStream(folderStream, Header.Size);
        }

        public CompressionType CompressionType
        {
            get
            {
                if (_type is null)
                {
                    _type = GetCompression();
                }
                return _type.Value;
            }
        }

        //copied from DecoderRegistry
        private const uint K_COPY = 0x0;
        private const uint K_DELTA = 3;
        private const uint K_LZMA2 = 0x21;
        private const uint K_LZMA = 0x030101;
        private const uint K_PPMD = 0x030401;
        private const uint K_BCJ = 0x03030103;
        private const uint K_BCJ2 = 0x0303011B;
        private const uint K_DEFLATE = 0x040108;
        private const uint K_B_ZIP2 = 0x040202;

        internal CompressionType GetCompression()
        {
            var coder = Folder!._coders.First();
            switch (coder._methodId._id)
            {
                case K_LZMA:
                case K_LZMA2:
                    {
                        return CompressionType.LZMA;
                    }
                case K_PPMD:
                    {
                        return CompressionType.PPMd;
                    }
                case K_B_ZIP2:
                    {
                        return CompressionType.BZip2;
                    }
                default:
                    throw new NotImplementedException();
            }
        }

        internal bool IsEncrypted => Folder!._coders.FindIndex(c => c._methodId._id == CMethodId.K_AES_ID) != -1;
    }
}