using System.Text;
using InnovEnergy.Lib.Utils;

namespace InnovEnergy.Lib.SysTools;

[Obsolete("Needs rework before use")]
public static class FileIo
{
    public static Boolean Exists         (this SysPath path) => path.FileExists() || path.DirectoryExists();
    public static Boolean DirectoryExists(this SysPath path) => new DirectoryInfo(path).Exists;
    public static Boolean FileExists     (this SysPath path) => new FileInfo(path).Exists;

    public static Boolean DirectoryExists(this SysPath path, String directoryName) => path.Append(directoryName).DirectoryExists();
    public static Boolean FileExists     (this SysPath path, String fileName)      => path.Append(fileName).FileExists();


    public static IEnumerable<SysPath> Directories(this SysPath sysPath) => Directory
                                                                           .GetDirectories(sysPath)
                                                                           .Select(SysPath.FromString);

    public static IEnumerable<SysPath> Files(this SysPath sysPath) => Directory
                                                                     .GetFiles(sysPath)
                                                                     .Select(SysPath.FromString);


    public static SysPath CreateDirectory(this SysPath path)
    {
        Directory.CreateDirectory(path);
        return path;
    }

    public static SysPath MoveFileTo(this SysPath sourcePath, SysPath targetPath)
    {
        File.Move(sourcePath, targetPath);
        return targetPath;
    }

    public static SysPath MoveDirectory(this SysPath sourcePath, SysPath targetPath)
    {
        Directory.Move(sourcePath, targetPath);
        return targetPath;
    }

    public static SysPath CreateDirectory(this SysPath path, String dirName)
    {
        var dir = path / dirName;
        Directory.CreateDirectory(dir);
        return dir;
    }

    public static Boolean DeleteFile(this SysPath path)
    {
        var file = new FileInfo(path);
        if (file.Exists)
            file.Delete();

        return file.Exists;
    }


    public static Boolean DeleteFile(this SysPath path, String fileName)
    {
        return path.Append(fileName).DeleteFile();
    }

    public static Boolean DeleteDirectory(this SysPath path)
    {
        var dir = new DirectoryInfo(path);
        if (dir.Exists)
            dir.Delete(recursive: true);

        return dir.Exists;
    }

    public static Boolean DeleteDirectory(this SysPath path, String directoryName)
    {
        return path.Append(directoryName).DeleteDirectory();
    }

    public static SysPath CopyDirectoryTo(this SysPath sourceDir, SysPath targetDir)
    {
        if (sourceDir.FileExists())
            throw new ArgumentException($"{nameof(sourceDir)} is a file, expected a directory", nameof(sourceDir));

        if (targetDir.FileExists())
            throw new ArgumentException($"{nameof(targetDir)} is a file, expected a directory", nameof(targetDir));

        var source = new DirectoryInfo(sourceDir);

        if (!source.Exists)
            throw new ArgumentException("source directory does not exist", nameof(sourceDir));

        SysPath Target(SysPath path) => targetDir.Append(path.RelativeTo(sourceDir));

        sourceDir.Traverse(Directories)
                 .Do(d => Target(d).CreateDirectory())
                 .SelectMany(Files)
                 .ForEach(f => f.CopyFileTo(Target(f)));

        return sourceDir;
    }


    public static SysPath CopyFileTo(this SysPath srcFile, SysPath targetFile)
    {
        File.Copy(srcFile, targetFile);
        return srcFile;
    }

    public static SysPath CopyFileToDirectory(this SysPath srcFile, SysPath targetDir)
    {
        File.Copy(srcFile, targetDir / srcFile.Head);
        return srcFile;
    }


    public static IEnumerable<SysPath> DescendantDirectories(this SysPath path)
    {
        return path.Traverse(Directories);
    }

    public static IEnumerable<SysPath> DescendantFiles(this SysPath path)
    {
        return path
              .DescendantDirectories()
              .SelectMany(Files);
    }

    public static IEnumerable<SysPath> Descendants(this SysPath path)
    {
        foreach (var d in path.DescendantDirectories())
        {
            yield return d;  // yield directory first (depth first pre order)
            foreach (var f in Files(d))
                yield return f;
        }
    }


    public static IEnumerable<Byte> ReadBytes(this SysPath path)
    {
        var buf = new Byte[4096];

        using var fs = File.OpenRead(path);
        var n = fs.Read(buf, 0, buf.Length);

        foreach (var b in buf.Take(Math.Max(0, n)))
            yield return b;
    }


    public static IEnumerable<String> ReadLines(this SysPath path) => path.ReadLines(Encoding.UTF8);

    public static IEnumerable<String> ReadLines(this SysPath path, Encoding encoding)
    {
        using var sr = new StreamReader(path, encoding);
        while (true)
        {
            var str = sr.ReadLine();
            if (str == null)
                yield break;

            yield return str;
        }
    }

    public static String ReadText(this SysPath path) => path.ReadText(Encoding.UTF8);

    public static String ReadText(this SysPath path, Encoding encoding)
    {
        using var sr = new StreamReader(path, encoding);
        return sr.ReadToEnd();
    }

    public static SysPath WriteText(this SysPath filePath, String text)
    {
        using var sw = new StreamWriter(filePath, append: false);
        sw.Write(text);

        return filePath;
    }

    public static SysPath WriteLines(this SysPath filePath, params String[] lines)
    {
        filePath.WriteLines((IEnumerable<String>) lines);
        return filePath;
    }

    public static SysPath WriteLines(this SysPath filePath, IEnumerable<String> lines)
    {
        using var sw = new StreamWriter(filePath, append: false);
        foreach (var line in lines)
            sw.WriteLine(line);

        return filePath;
    }

    public static SysPath AppendText(this SysPath filePath, String text)
    {
        using var sw = new StreamWriter(filePath, append: true);
        sw.Write(text);

        return filePath;
    }

    public static SysPath AppendLines(this SysPath filePath, params String[] lines)
    {
        filePath.AppendLines((IEnumerable<String>) lines);
        return filePath;
    }

    public static SysPath AppendLines(this SysPath filePath, IEnumerable<String> lines)
    {
        using var sw = new StreamWriter(filePath, append: true);
        foreach (var line in lines)
            sw.WriteLine(line);

        return filePath;
    }

    // TODO: binary ops

}