using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using IPCamLib;
using IPCamLib.Concrete;
using Microsoft.Win32;
using Newtonsoft.Json;

namespace IPCamAppBar
{
    public partial class MainForm : Form
    {
        private readonly Config config;
        private readonly AppBar appBar;
        private bool paused = false;


        public MainForm()
        {
            InitializeComponent();

            // ReSharper disable once PossibleNullReferenceException
            // ReSharper disable once AssignNullToNotNullAttribute
            using (var file = File.OpenText(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "config.json")))
            {
                var serializer = new JsonSerializer();
                config = (Config)serializer.Deserialize(file, typeof(Config));
            }

            var monitor = GetMonitor(config.AppBar.Monitor);
            var position = GetAppBarPosition(config.AppBar.Side);

            appBar = new AppBar(Handle);
            appBar.SetPosition(monitor, position, config.AppBar.Size);

            CameraViewContainer.FlowDirection =
                config.AppBar.Side == ConfigSide.Left || config.AppBar.Side == ConfigSide.Right
                    ? FlowDirection.TopDown
                    : FlowDirection.LeftToRight;
            
            config.Cameras?.ForEach(AddCamera);
            
            
            SystemEvents.SessionSwitch += SystemEventsOnSessionSwitch;
        }


        protected override void WndProc(ref Message m)
        {
            appBar?.HandleMessage(m);
            base.WndProc(ref m);
        }


        private static Screen GetMonitor(int number)
        {
            // Since I could not find any way to get the numbers as assigned in the
            // display control panel, we'll just order them left-to-right.
            var orderedScreens = Screen.AllScreens.OrderBy(s => s.Bounds.Left).ToList();

            // 0 means primary. Yeah. Totally not losing geek-creds for using a 1-based
            // index here. No sir. 'tis a feature!
            return number > 0 && number <= orderedScreens.Count
                ? orderedScreens[number - 1]
                : Screen.PrimaryScreen;
        }


        private static AppBarPosition GetAppBarPosition(ConfigSide side)
        {
            return side switch
            {
                ConfigSide.Top => AppBarPosition.Top,
                ConfigSide.Bottom => AppBarPosition.Bottom,
                ConfigSide.Left => AppBarPosition.Left,
                ConfigSide.Right => AppBarPosition.Right,
                _ => throw new ArgumentOutOfRangeException(nameof(side), side, @"Invalid side value")
            };
        }


        private void AddCamera(ConfigCamera configCamera)
        {
            ICamera camera = configCamera.Type switch
            {
                ConfigCameraType.HTTPMJPEG => new HTTPMJPEGStreamCamera(new Uri(configCamera.URL)),
                ConfigCameraType.RTSP => new RTSPStreamCamera(new Uri(configCamera.URL), configCamera.Width, configCamera.Height),
                _ => throw new ArgumentOutOfRangeException($"Camera type in configuration is not valid: {configCamera.Type}")
            };

            var margin = new Padding(0);
            if (CameraViewContainer.FlowDirection == FlowDirection.LeftToRight)
                margin.Right = config.AppBar.Spacing;
            else
                margin.Bottom = config.AppBar.Spacing;

            var view = new CameraView(camera, configCamera.OverlayDateTime)
            {
                Width = configCamera.Width, 
                Height = configCamera.Height,
                Margin = margin
            };

            CameraViewContainer.Controls.Add(view);
        }


        private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            appBar.Dispose();
        }


        private void CloseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        
        private void SystemEventsOnSessionSwitch(object sender, SessionSwitchEventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new Action(() => SystemEventsOnSessionSwitch(sender, e)));
                return;
            }

            var pause = e.Reason == SessionSwitchReason.ConsoleDisconnect ||
                        e.Reason == SessionSwitchReason.RemoteDisconnect ||
                        e.Reason == SessionSwitchReason.SessionLock ||
                        e.Reason == SessionSwitchReason.SessionLogoff;

            if (pause == paused)
                return;

            foreach (var control in CameraViewContainer.Controls)
            {
                if (control is CameraView cameraView)
                    cameraView.SetPaused(pause);
            }
            
            paused = pause;
        }
    }
}
