using System;
using System.IO;
using System.Runtime.InteropServices;

namespace WinSCP
{
    [Guid("B1DAE3A0-5E56-4001-88D8-786F68557E28")]
    [ComVisible(true)]
    public enum SynchronizationAction
    {
        UploadNew = 1,
        DownloadNew = 2,
        UploadUpdate = 3,
        DownloadUpdate = 4,
        DeleteRemote = 5,
        DeleteLocal = 6,
    };

    [Guid("2D6EFFB5-69BA-47AA-90E8-A92953E8B58A")]
    [ComVisible(true)]
    public sealed class ComparisonFileInfo
    {
        public string FileName { get; internal set; }
        public DateTime LastWriteTime { get; internal set; }
        public long Length { get; internal set; }
        public int Length32 { get { return GetLength32(); } }

        internal ComparisonFileInfo()
        {
        }

        private int GetLength32()
        {
            return Tools.LengthTo32Bit(Length);
        }
    }

    [Guid("97F5222E-9379-4C24-9E50-E93C7334BBD5")]
    [ClassInterface(Constants.ClassInterface)]
    [ComVisible(true)]
    public sealed class ComparisonDifference
    {
        public SynchronizationAction Action { get; internal set; }
        public bool IsDirectory { get; internal set; }
        public ComparisonFileInfo Local { get; internal set; }
        public ComparisonFileInfo Remote { get; internal set; }

        internal ComparisonDifference(string localPath, string remotePath)
        {
            _localPath = localPath;
            _remotePath = remotePath;
        }

        public override string ToString()
        {
            string buf;
            switch (Action)
            {
                case SynchronizationAction.UploadNew:
                    buf = RemotePath.Combine(TranslateLocalPathToRemote(), "*");
                    return $"{GetLocalPathString()} ==> {buf}";

                case SynchronizationAction.DownloadNew:
                    buf = Path.Combine(TranslateRemotePathToLocal(), "*");
                    return $"{buf} <== {GetRemotePathString()}";

                case SynchronizationAction.UploadUpdate:
                    return $"{GetLocalPathString()} ==> {GetRemotePathString()}";

                case SynchronizationAction.DownloadUpdate:
                    return $"{GetLocalPathString()} <== {GetRemotePathString()}";

                case SynchronizationAction.DeleteRemote:
                    return $"× {GetRemotePathString()}";

                case SynchronizationAction.DeleteLocal:
                    return $"× {GetLocalPathString()}";

                default:
                    throw new InvalidOperationException();
            }
        }

        private string TranslateRemotePathToLocal()
        {
            return RemotePath.TranslateRemotePathToLocal(RemotePath.GetDirectoryName(Remote.FileName), _remotePath, _localPath);
        }

        private string TranslateLocalPathToRemote()
        {
            return RemotePath.TranslateLocalPathToRemote(Path.GetDirectoryName(Local.FileName), _localPath, _remotePath);
        }

        private string GetRemotePathString()
        {
            return Remote.FileName + (IsDirectory ? "/" : string.Empty);
        }

        private string GetLocalPathString()
        {
            return Local.FileName + (IsDirectory ? "\\" : string.Empty);
        }

        public FileOperationEventArgs Resolve(Session session, TransferOptions options = null)
        {
            if (session == null)
            {
                throw new ArgumentNullException(nameof(session));
            }

            switch (Action)
            {
                case SynchronizationAction.UploadNew:
                case SynchronizationAction.UploadUpdate:
                    {
                        string remoteDirectory = TranslateLocalPathToRemote();
                        if (!IsDirectory)
                        {
                            return session.PutFileToDirectory(Local.FileName, remoteDirectory, options: options);
                        }
                        else
                        {
                            session.PutEntryToDirectory(Local.FileName, remoteDirectory, options: options);
                            return null;
                        }
                    }

                case SynchronizationAction.DownloadNew:
                case SynchronizationAction.DownloadUpdate:
                    {
                        string localDirectory = TranslateRemotePathToLocal();
                        if (!IsDirectory)
                        {
                            return session.GetFileToDirectory(Remote.FileName, localDirectory, options: options);
                        }
                        else
                        {
                            session.GetEntryToDirectory(Remote.FileName, localDirectory, options: options);
                            return null;
                        }
                    }

                case SynchronizationAction.DeleteRemote:
                    if (!IsDirectory)
                    {
                        return session.RemoveFile(Remote.FileName);
                    }
                    else
                    {
                        session.RemoveEntry(Remote.FileName);
                        return null;
                    }

                case SynchronizationAction.DeleteLocal:
                    if (!IsDirectory)
                    {
                        File.Delete(Local.FileName);
                    }
                    else
                    {
                        Directory.Delete(Local.FileName, true);
                    }
                    return null;

                default:
                    throw session.Logger.WriteException(new InvalidOperationException());
            }
        }

        public void Reverse()
        {
            switch (Action)
            {
                case SynchronizationAction.UploadNew:
                    Action = SynchronizationAction.DeleteLocal;
                    break;

                case SynchronizationAction.DownloadNew:
                    Action = SynchronizationAction.DeleteRemote;
                    break;

                case SynchronizationAction.UploadUpdate:
                    Action = SynchronizationAction.DownloadUpdate;
                    break;

                case SynchronizationAction.DownloadUpdate:
                    Action = SynchronizationAction.UploadUpdate;
                    break;

                case SynchronizationAction.DeleteRemote:
                    Action = SynchronizationAction.DownloadNew;
                    break;

                case SynchronizationAction.DeleteLocal:
                    Action = SynchronizationAction.UploadNew;
                    break;

                default:
                    throw new InvalidOperationException();
            }
        }

        private readonly string _localPath;
        private readonly string _remotePath;
    }
}
