using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using log4net;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using RevitToIfcConverter.Core.Tools;

namespace RevitToIfcConverter.Core
{
    class ConverterImplementation : IPipeServerCommunicateStrategy
    {
        private readonly ILog _logger;
        private readonly ExportOptionsConverter _optionsConverter;
        private ExternalEvent _exEvent;
        private readonly ManualResetEvent _exportComplete = new ManualResetEvent(false);
        private string _inputRvtFilePath;
        private string _outputIfcFilePath;
        private Dictionary<string, string> _settings;


        public ConverterImplementation(ILog logger)
        {
            _logger = logger;
            _optionsConverter = new ExportOptionsConverter();
        }

        public void SetExEvent(ExternalEvent exEvent)
        {
            _exEvent = exEvent;
        }

        public void StartConverting(UIApplication app)
        {
            _outputIfcFilePath = ConvertRvtToIfc(app.Application, _inputRvtFilePath);
            _inputRvtFilePath = null;
            _exportComplete.Set();
        }

        private string ConvertRvtToIfc(Application revitApp, string rvtFilePath)
        {
            try
            {
                _logger.Info($"{nameof(ExternalApplication)}: converting {rvtFilePath}");
                string ifcFilePath;

                var modelPath = ModelPathUtils.ConvertUserVisiblePathToModelPath(rvtFilePath);
                var options = new OpenOptions();

                using (var doc = revitApp.OpenDocumentFile(modelPath, options))
                {
                    var ifcOption = _optionsConverter.ComposeOptions(doc, _settings);

                    using (var transaction = new Transaction(doc, "Export"))
                    {
                        transaction.Start();

                        doc.Export(
                            Path.GetDirectoryName(doc.PathName),
                            Path.GetFileNameWithoutExtension(doc.PathName),
                            ifcOption);

                        transaction.Commit();
                    }

                    ifcFilePath = Path.ChangeExtension(doc.PathName, "ifc");
                    doc.Close();
                }

                return ifcFilePath;
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
                return $"Error: {ex.Message}";
            }
        }

        public void Communicate(PipeStream stream)
        {
            try
            {
                if (!string.IsNullOrEmpty(_inputRvtFilePath))
                    throw new Exception("Previous convertation has not been completed yet.");

                var request = stream.ReadRequest();
                _inputRvtFilePath = request.Path;
                _settings = request.Settings;

                _exEvent.Raise();

                _exportComplete.WaitOne();
                _exportComplete.Reset();
                stream.WriteString(_outputIfcFilePath);
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
                throw;
            }
        }
    }
}
