using InnovEnergy.Lib.SysTools.Utils;

namespace InnovEnergy.Lib.SysTools;

public readonly struct SysCommand
{
    public SysPath Path { get; }
    public String  Args { get; }

    public SysCommand(SysPath path, String args = "")
    {
        Path = path;
        Args = args;
    }

    public static SysCommand FromPath(SysPath path) => new SysCommand(path);
    public static SysCommand FromString(String cmd) => new SysCommand(cmd);

    public SysCommand Opt1(String option)
    {
        return new SysCommand(Path, $"{Args} -{option}");
    }

    public SysCommand Opt1(String option, Object value, String separator = " ")
    {
        return new SysCommand(Path, $"{Args} -{option}{separator}{QuoteArg(value)}");
    }

    public SysCommand Opt2(String option)
    {
        return new SysCommand(Path, $"{Args} --{option}");
    }

    public SysCommand Opt2(String option, Object value, String separator = "=")
    {
        return new SysCommand(Path, $"{Args} --{option}{separator}{QuoteArg(value)}");
    }

    public SysCommand Opt(String option)
    {
        return option.Length == 1
            ? Opt1(option)
            : Opt2(option);
    }

    public SysCommand Opt(String option, Object value)
    {
        return option.Length == 1
            ? Opt1(option, value)
            : Opt2(option, value);
    }

    public SysCommand Arg(Object argument)
    {
        return new SysCommand(Path, $"{Args} {QuoteArg(argument)}");
    }

    private static String QuoteArg(Object argument)
    {
        var arg = argument.ToString();
        if (arg.Contains(" "))
            arg = arg.Quote(); // TODO: single quote?

        return arg;
    }

    public static implicit operator SysCommand(String cmd)   => FromString(cmd);
    public static implicit operator SysCommand(SysPath path) => FromPath(path);

    public static SysCommand operator %(SysCommand cmd, String argument) => cmd.Arg(argument);
    public static SysCommand operator -(SysCommand cmd, String option  ) => cmd.Opt(option);
    public static SysCommand operator -(SysCommand cmd, (String opt, Object value) option ) => cmd.Opt(option.opt, option.value);

    #region equality

    public Boolean Equals(SysCommand other)
    {
        return Path.Equals(other.Path) && Args == other.Args;
    }

    public override Boolean Equals(Object obj)
    {
        return obj is SysCommand other && Equals(other);
    }

    public override Int32 GetHashCode()
    {
        unchecked
        {
            return (Path.GetHashCode() * 397) ^ (Args != null ? Args.GetHashCode() : 0);
        }
    }

    #endregion equality

    public void Deconstruct(out SysPath path, out String args)
    {
        path = Path;
        args = Args;
    }

    public override String ToString() => $"{Path}{Args}";
}