using PacketIO;

namespace Archiver.Lib;

public static class ArchiveDecoder
{
    public static async ValueTask Decode(string inputPath, string outputPath)
    {
        if (!File.Exists(inputPath))
        {
            throw new FileNotFoundException($"{inputPath} does not exist", inputPath);
        }

        FileAttributes attributes = File.GetAttributes(inputPath);
        if (attributes.HasFlag(FileAttributes.Directory))
        {
            throw new IOException("Target does not file.");
        }

        using (FileStream fs = new FileStream(inputPath, FileMode.Open))
        {
            byte[] buffer = new byte[ArchiveEncoder.CreateFileHeader().Length];
            await fs.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
            if (!buffer.SequenceEqual(ArchiveEncoder.CreateFileHeader()))
            {
                throw new IOException("Not supported file type.");
            }

            await ReadFile(fs, outputPath).ConfigureAwait(false);
        }
    }

    private static async ValueTask ReadFile(Stream stream, string outputPath)
    {
        IPacketFile? file = await TreePacketFile.ReadTreeFile(stream).ConfigureAwait(false);
        if (file is null)
        {
            return;
        }

        await OutputFile(file, outputPath).ConfigureAwait(false);
        await ReadFile(stream, outputPath).ConfigureAwait(false);
    }

    private static async ValueTask OutputFile(IPacketFile file, string outputPath)
    {
        switch (file.GetFileType())
        {
            case PacketFileType.Directory:
            {
                string dirPath = Path.Combine(outputPath, file.GetFileName());
                if (!Directory.Exists(dirPath))
                {
                    Directory.CreateDirectory(dirPath);
                }

                foreach (IPacketFile inFile in file.EnumerableFiles())
                {
                    await OutputFile(inFile, dirPath).ConfigureAwait(false);
                }

                break;
            }
            case PacketFileType.File:
            {
                string filePath = Path.Combine(outputPath, file.GetFileName());
                using (FileStream outputStream = new FileStream(filePath, FileMode.OpenOrCreate))
                {
                    PacketFile pfile = (PacketFile) file;
                    byte[] buffer = new byte[8192];
                    int c;
                    do
                    {
                        c = await pfile.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
                        await outputStream.WriteAsync(buffer, 0, c).ConfigureAwait(false);
                    } while (c > 0);
                }

                break;
            }
        }

        file.Dispose();
    }
}