using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.InformationProtection;
using Microsoft.InformationProtection.File;
using Microsoft.InformationProtection.Policy;
using Microsoft.InformationProtection.Protection;
using LogLevel = Microsoft.InformationProtection.LogLevel;

namespace AipGateway.AIP
{
    public sealed class AipFileManager
    {
        public int LastErrNo { get; internal set; }
        public string LastErrMsg { get; internal set; }

        private readonly AipConfig _aipConfig;

        private readonly ApplicationInfo _appInfo;
        private AuthDelegateImplementation _authDelegate;

        private MipContext _mipContext;

        private readonly FileManager _fileManager = null;
        private readonly PolicyManager _policyManager = null;
        private readonly ProtectionManager _protectionManager = null;

        public AipFileManager(AipConfig aipConfig)
        {
            _aipConfig = aipConfig;
            _appInfo = new ApplicationInfo
            {
                ApplicationId = aipConfig.ClientId,
                ApplicationName = aipConfig.AppName,
                ApplicationVersion = aipConfig.AppVersion
            };
            LastErrNo = 0;
            LastErrMsg = string.Empty;

            _fileManager = new FileManager();
            _policyManager = new PolicyManager();
            _protectionManager = new ProtectionManager();
        }
        ~AipFileManager() => this.Dispose(false);
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize((object)this);

        }

        private void Dispose(bool disposing)
        {
            lock (this)
            {
                if (_fileManager != null)
                {
                    _fileManager.Dispose();
                }
                if (_policyManager != null)
                {
                    _policyManager.Dispose();
                }
                if (_protectionManager != null)
                {
                    _protectionManager.Dispose();
                }

                _authDelegate = null;
            }
        }

        private void SetError(int errNo, string errMsg1, string errMsg2 = "No Exception Message.")
        {
            LastErrNo = errNo;
            LastErrMsg = errMsg1 + "\r\n" + errMsg2;
            Console.WriteLine("AipFileManager::SetError ==> {0}, {1}, {2}", errNo, errMsg1, errMsg2);
        }

        public bool Initialize()
        {
            _authDelegate = new AuthDelegateImplementation(_aipConfig);
            try
            {
                // Initialize SDK DLLs. If DLLs are missing or wrong type, this will throw an exception
                MIP.Initialize(MipComponent.File);
                //MIP.Initialize(MipComponent.Policy);
                //MIP.Initialize(MipComponent.Protection);    // Protection
            }
            catch (Exception ex) {
                SetError(1, "MIP.Initialize Failed.", ex.Message);
                return false;
            }

            // Create MipConfiguration Object
            var mipConfiguration = new MipConfiguration(_appInfo, _aipConfig.MipData, LogLevel.Trace, false);
            // mipConfiguration.LoggerDelegateOverride = this;
            
            //Enable DKE
            // Dictionary<FlightingFeature, bool> featureSettings = mipConfiguration.FeatureSettingsOverride ?? new Dictionary<FlightingFeature, bool>();
            // featureSettings[FlightingFeature.DoubleKey] = true;
            // mipConfiguration.FeatureSettingsOverride = featureSettings;

            // Create MipContext using MipConfiguration
            _mipContext = MIP.CreateMipContext(mipConfiguration);

            return true;
        }

        public bool CreateProfile()
        {
            if (_authDelegate == null || _mipContext == null)
            {
                SetError(999, "AipFileManager::CreateProfile Failed.", "Library Is not Initialized.");
                return false;
            }

            if (!_fileManager.CreateProfile(ref _mipContext))
            {
                SetError(11, "AipFileManager::CreateProfile ==> FileManager::CreateProfile Failed.");
                return false;
            }
            if (!_policyManager.CreateProfile(ref _mipContext))
            {
                SetError(12, "AipFileManager::CreateProfile ==> PolicyManager::CreateProfile Failed.");
                return false;
            }
            if (!_protectionManager.CreateProfile(ref _mipContext))
            {
                SetError(13, "AipFileManager::CreateProfile ==> ProtectionManager::CreateProfile Failed.");
                return false;
            }
            return true;
        }

        public bool CreateEngine()
        {
            if (_authDelegate == null || _mipContext == null)
            {
                SetError(999, "AipFileManager::CreateEngine Failed.", "Library Is not Initialized.");
                return false;
            }
            
            Identity identity = new Identity(_aipConfig.EMail);
            if (!_fileManager.CreateEngine(ref identity, ref _authDelegate))
            {
                SetError(21, "AipFileManager::CreateEngine ==> FileManager::CreateEngine Failed.");
                return false;
            }
            if (!_policyManager.CreateEngine(ref identity, ref _authDelegate))
            {
                SetError(22, "AipFileManager::CreateEngine ==> PolicyManager::CreateEngine Failed.");
                return false;
            }
            if (!_protectionManager.CreateEngine(ref identity, ref _authDelegate))
            {
                SetError(23, "AipFileManager::CreateEngine ==> ProtectionManager::CreateEngine Failed.");
                return false;
            }

            return true;
        }

        public bool IsProtected(Stream inputStream, string filePath)
        {
            IFileStatus status = FileHandler.GetFileStatus(inputStream, filePath, _mipContext);
            bool result = status.IsProtected();
            return result;
        }
        public bool IsLabeledOrProtected(Stream inputStream, string filePath)
        {
            IFileStatus status = FileHandler.GetFileStatus(inputStream, filePath, _mipContext);
            bool isLabeled = status.IsLabeled();
            bool isProtected = status.IsProtected();

            return (isLabeled || isProtected);
        }

        public AipFileInfo GetFileInfo(string fileName)
        {
            if (_authDelegate == null || _mipContext == null)
            {
                SetError(999, "AipFileManager::GetFileInfo Failed.", "Library is not Initialized.");
                return new AipFileInfo
                {
                    errorCode = 10,
                    errorMessage = "AIP 라이브러리가 초기화 되지 않았습니다.",
                };
            }

            AipFileInfo fileInfo = _fileManager.GetFileInfo(fileName);
            if (fileInfo == null)
            {
                SetError(_fileManager.LastErrNo, "AipFileManager::GetFileInfo Failed.", _fileManager.LastErrMsg);
                return new AipFileInfo
                {
                    errorCode = _fileManager.LastErrNo == 0 ? 11 : _fileManager.LastErrNo,
                    errorMessage = _fileManager.LastErrMsg,
                };
            }
            return fileInfo;
        }
        public AipFileInfo GetFileInfo(Stream fileStream, string outputFileName)
        {
            if (_authDelegate == null || _mipContext == null)
            {
                SetError(999, "AipFileManager::GetFileInfo Failed.", "Library Is not Initialized.");
                return new AipFileInfo
                {
                    errorCode = 10,
                    errorMessage = "AIP 라이브러리가 초기화 되지 않았습니다.",
                };
            }
            AipFileInfo fileInfo = _fileManager.GetFileInfo(fileStream, outputFileName);
            if (fileInfo == null)
            {
                SetError(_fileManager.LastErrNo, "AipFileManager::GetFileInfo Failed.", _fileManager.LastErrMsg);
                return new AipFileInfo
                {
                    errorCode = _fileManager.LastErrNo == 0 ? 12 : _fileManager.LastErrNo,
                    errorMessage = _fileManager.LastErrMsg,
                };
            }
            return fileInfo;
        }
        public List<AipLabel> SensitivityLabels()
        {
            if (_authDelegate == null || _mipContext == null)
            {
                SetError(999, "AipFileManager::SensitivityLabels Failed.", "Library Is not Initialized.");
                return null;
            }

            var result = new List<AipLabel>();
            try
            {
                var labels = _fileManager.SensitivityLabels();
                foreach (var label in labels)
                {
                    var aipLabel = Utilities.LabelToAip(label);
                    if (aipLabel != null)
                    {
                        if (label.Children.Count > 0)
                        {
                            foreach (var child in label.Children)
                            {
                                var aipChildLabel = Utilities.LabelToAip(child);
                                if (aipChildLabel != null) aipLabel.Children.Add(aipChildLabel);
                            }
                        }
                        result.Add(aipLabel);
                    }
                }
            }
            catch (Exception ex)
            {
                SetError(31, "AipFileManager::SensitivityLabels Failed.", ex.Message);
                result = new List<AipLabel>();
            }
            return result;
        }

        public List<AipLabel> ListSensitivityLabels()
        {
            if (_authDelegate == null || _mipContext == null)
            {
                SetError(999, "AipFileManager::ListSensitivityLabels Failed.", "Library Is not Initialized.");
                return null;
            }

            var result = new List<AipLabel>();
            try
            {
                var labels = _policyManager.ListSensitivityLabels();
                foreach (var label in labels)
                {
                    var aipLabel = Utilities.LabelToAip(label);
                    if (aipLabel != null)
                    {
                        if (label.Children.Count > 0)
                        {
                            foreach (var child in label.Children)
                            {
                                var aipChildLabel = Utilities.LabelToAip(child);
                                if (aipChildLabel != null) aipLabel.Children.Add(aipChildLabel);
                            }
                        }
                        result.Add(aipLabel);
                    }
                }
            }
            catch (Exception ex)
            {
                SetError(32, "AipFileManager::ListSensitivityLabels Failed.", ex.Message);
                result = new List<AipLabel>();
            }
            return result;
        }

        public List<AipTemplate> GetTemplates()
        {
            if (_authDelegate == null || _mipContext == null)
            {
                SetError(999, "AipFileManager::GetTemplates Failed.", "Library Is not Initialized.");
                return null;
            }

            var result = new List<AipTemplate>();
            try
            {
                var templates = _protectionManager.GetTemplates();
                foreach (var template in templates)
                {
                    var aipTemplate = Utilities.TemplateToAip(template);
                    if (aipTemplate != null)
                    {
                        result.Add(aipTemplate);
                    }
                }
            }
            catch (Exception ex)
            {
                SetError(33, "AipFileManager::GetTemplates Failed.", ex.Message);
                result = new List<AipTemplate>();
            }
            return result;
        }

        /// <summary>
        /// ///////////////////////////////////////////////////////
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="actualFileName"></param>
        /// <param name="email"></param>
        /// <param name="labelId"></param>
        /// <param name="templateId"></param>
        /// <param name="comments"></param>
        /// <returns></returns>
        public SetFileInfo SetLabel(string fileName, string actualFileName, string email, string labelId, string templateId, string comments = "")
        {
            // 레이블 및 템플릿 정보 가져오기
            return _fileManager.SetLabel(fileName, actualFileName, email, labelId, templateId, comments);
        }
        public SetFileInfo SetLabel(Stream fileStream, string actualFileName, string email, string labelId, string templateId, string comments = "")
        {
            // 레이블 및 템플릿 정보 가져오기
            return _fileManager.SetLabel(fileStream, actualFileName, email, labelId, templateId, comments);
        }

        /// <summary>
        /// ///////////////////////////////////////////////////////////
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="actualFileName"></param>
        /// <param name="email"></param>
        /// <param name="comments"></param>
        /// <param name="isDelProtection"></param>
        /// <returns></returns>
        public SetFileInfo DeleteLabel(string fileName, string actualFileName, string email, string comments = "", bool isDelProtection = false)
        {
            return _fileManager.DeleteLabel(fileName, actualFileName, email, comments, isDelProtection);
        }
        public SetFileInfo DeleteLabel(Stream fileStream, string actualFileName, string email, string comments = "", bool isDelProtection = false)
        {
            return _fileManager.DeleteLabel(fileStream, actualFileName, email, comments, isDelProtection);
        }
       
        /// <summary>
        /// //////////////////////////////////////////////////////////////
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="actualFileName"></param>
        /// <param name="email"></param>
        /// <param name="templateId"></param>
        /// <param name="comments"></param>
        /// <returns></returns>
        public SetFileInfo SetProtection(string fileName, string actualFileName, string email, string templateId, string comments = "")
        {
            return _fileManager.SetProtection(fileName, actualFileName, email, templateId, comments);
        }
        public SetFileInfo SetProtection(Stream fileStream, string actualFileName, string email, string templateId, string comments = "")
        {
            return _fileManager.SetProtection(fileStream, actualFileName, email, templateId, comments);
        }

        /// <summary>
        /// /////////////////////////////////////////////////////////////////////////
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="actualFileName"></param>
        /// <param name="email"></param>
        /// <param name="comments"></param>
        /// <returns></returns>
        public SetFileInfo DeleteProtection(string fileName, string actualFileName, string email, string comments = "")
        {
            return _fileManager.RemoveProtection(fileName, actualFileName, email, comments);
        }
        public SetFileInfo DeleteProtection(Stream fileStream, string actualFileName, string email, string comments = "")
        {
            return _fileManager.RemoveProtection(fileStream, actualFileName, email, comments);
        }

        public AipFileStatus GetAipFileStatus(string fileName)
        {
            try
            {
                var fileStatus = FileHandler.GetFileStatus(fileName, _mipContext);
                AipFileStatus result = new AipFileStatus
                {
                    IsProtected = fileStatus.IsProtected(),
                    IsLabeled = fileStatus.IsLabeled(),
                    ContainsProtectedObjects = fileStatus.ContainsProtectedObjects()
                };
                return result;
            }
            catch (Exception ex)
            {
                SetError(81, "AipFileManager::GetAipFileStatus Failed.", ex.Message);
            }

            return null;
        }
    }
}
