using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using ConeCalorimeter.Services;
using ConeCalorimeter.ViewModels;
using ConeCalorimeter.Views;
using Serilog;

namespace ConeCalorimeter
{
    public partial class MainWindow : Window
    {
        private const double HiddenHeatFluxEntrySize = 80;
        private static readonly TimeSpan HiddenHeatFluxLongPressDuration = TimeSpan.FromSeconds(2);

        private readonly ITcpDeviceConnectionService _tcpDeviceConnectionService;
        private readonly IScaleService _scaleService;
        private readonly DispatcherTimer _hiddenHeatFluxLongPressTimer;
        private bool _hiddenHeatFluxLongPressActive;
        private bool _hiddenHeatFluxDialogOpenedByLongPress;
        private bool _isHeatFluxCoefficientDialogOpen;
        private int? _hiddenHeatFluxTouchDeviceId;

        public MainWindow()
        {
            InitializeComponent();

            _hiddenHeatFluxLongPressTimer = new DispatcherTimer
            {
                Interval = HiddenHeatFluxLongPressDuration
            };
            _hiddenHeatFluxLongPressTimer.Tick += HiddenHeatFluxLongPressTimer_Tick;
            PreviewMouseLeftButtonDown += MainWindow_PreviewMouseLeftButtonDown;
            PreviewMouseLeftButtonUp += MainWindow_PreviewMouseLeftButtonUp;
            PreviewMouseMove += MainWindow_PreviewMouseMove;
            PreviewTouchDown += MainWindow_PreviewTouchDown;
            PreviewTouchUp += MainWindow_PreviewTouchUp;
            PreviewTouchMove += MainWindow_PreviewTouchMove;

            var tcpOptions = TcpDeviceConnectionOptions.FromEnvironment();
            var scaleOptions = SerialScaleOptions.FromEnvironment();

            _tcpDeviceConnectionService = new TcpDeviceConnectionService(tcpOptions);
            _scaleService = new ModbusRtuScaleService(scaleOptions);
            Log.Information(
                "Main window initialized. TCP endpoint={Endpoint}, scale port={ScalePort}.",
                _tcpDeviceConnectionService.Endpoint,
                scaleOptions.PortName);
            _ = _tcpDeviceConnectionService.StartAsync();

            var experimentDataService = new ExperimentDataService(
                new ModbusRealtimeDataService(_tcpDeviceConnectionService, _scaleService),
                new HeatReleaseCorrectionService(new JsonHeatReleaseResponseCalibrationStore()));
            DataContext = new MainViewModel(
                experimentDataService,
                _tcpDeviceConnectionService,
                new NpoiReportExportService(),
                new JsonReportInputPersistenceService(),
                new NpoiRealtimeDataExportService(),
                new HelpDialogService());
        }

        protected override async void OnClosed(EventArgs e)
        {
            Log.Information("Main window closing; disposing device services.");
            _scaleService.Dispose();
            await _tcpDeviceConnectionService.DisposeAsync();
            base.OnClosed(e);
        }

        private void MainWindow_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (IsInHiddenHeatFluxEntry(e.GetPosition(this)))
            {
                BeginHiddenHeatFluxLongPress();
            }
        }

        private void MainWindow_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            CancelHiddenHeatFluxLongPress();

            if (_hiddenHeatFluxDialogOpenedByLongPress)
            {
                _hiddenHeatFluxDialogOpenedByLongPress = false;
                e.Handled = true;
            }
        }

        private void MainWindow_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (!_hiddenHeatFluxLongPressActive)
            {
                return;
            }

            if (e.LeftButton != MouseButtonState.Pressed
                || !IsInHiddenHeatFluxEntry(e.GetPosition(this)))
            {
                CancelHiddenHeatFluxLongPress();
            }
        }

        private void MainWindow_PreviewTouchDown(object? sender, TouchEventArgs e)
        {
            if (IsInHiddenHeatFluxEntry(e.GetTouchPoint(this).Position))
            {
                _hiddenHeatFluxTouchDeviceId = e.TouchDevice.Id;
                BeginHiddenHeatFluxLongPress();
            }
        }

        private void MainWindow_PreviewTouchUp(object? sender, TouchEventArgs e)
        {
            if (_hiddenHeatFluxTouchDeviceId == e.TouchDevice.Id)
            {
                _hiddenHeatFluxTouchDeviceId = null;
                CancelHiddenHeatFluxLongPress();
            }
        }

        private void MainWindow_PreviewTouchMove(object? sender, TouchEventArgs e)
        {
            if (!_hiddenHeatFluxLongPressActive
                || _hiddenHeatFluxTouchDeviceId != e.TouchDevice.Id)
            {
                return;
            }

            if (!IsInHiddenHeatFluxEntry(e.GetTouchPoint(this).Position))
            {
                _hiddenHeatFluxTouchDeviceId = null;
                CancelHiddenHeatFluxLongPress();
            }
        }

        private void HiddenHeatFluxLongPressTimer_Tick(object? sender, EventArgs e)
        {
            _hiddenHeatFluxLongPressTimer.Stop();
            if (!_hiddenHeatFluxLongPressActive)
            {
                return;
            }

            _hiddenHeatFluxLongPressActive = false;
            _hiddenHeatFluxTouchDeviceId = null;
            _hiddenHeatFluxDialogOpenedByLongPress = true;
            ShowHeatFluxCoefficientDialog();
        }

        private void BeginHiddenHeatFluxLongPress()
        {
            if (_isHeatFluxCoefficientDialogOpen)
            {
                return;
            }

            _hiddenHeatFluxDialogOpenedByLongPress = false;
            _hiddenHeatFluxLongPressActive = true;
            _hiddenHeatFluxLongPressTimer.Stop();
            _hiddenHeatFluxLongPressTimer.Start();
        }

        private void CancelHiddenHeatFluxLongPress()
        {
            _hiddenHeatFluxLongPressTimer.Stop();
            _hiddenHeatFluxLongPressActive = false;
        }

        private static bool IsInHiddenHeatFluxEntry(Point position)
        {
            return position.X >= 0
                && position.Y >= 0
                && position.X <= HiddenHeatFluxEntrySize
                && position.Y <= HiddenHeatFluxEntrySize;
        }

        private void ShowHeatFluxCoefficientDialog()
        {
            if (_isHeatFluxCoefficientDialogOpen)
            {
                return;
            }

            _isHeatFluxCoefficientDialogOpen = true;
            try
            {
                var dialog = new HeatFluxCoefficientWindow(_tcpDeviceConnectionService)
                {
                    Owner = this
                };
                dialog.ShowDialog();
            }
            finally
            {
                _isHeatFluxCoefficientDialogOpen = false;
            }
        }
    }
}
