using System.Reflection;
using GraphicsManager;
using GraphicsManager.Enums;
using GraphicsManager.Interfaces;
using GraphicsManager.Objects;
using Luski.GUI.MainScreen.UI.LuskiControls;
using Luski.net.Structures.Public;
using Luski.Shared.PublicServers.V1.Enums;
using OpenTK.Graphics.ES11;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Common.Input;
using OpenTK.Windowing.GraphicsLibraryFramework;
using MatrixMode = OpenTK.Graphics.OpenGL.MatrixMode;

namespace Luski.GUI.MainScreen.UI.PublicServers;

public class PublicChat : UserControl
{

    public FlowLayout MessageFlow;
    private Label title, desc;
    private TextBox tb;
    private SocketChannel? Channel;
    UserControl titlecon;
    private Rectangle? UserCon;
    private SocketMessage? first;
    private FlowLayout? memberflow = null;
    
    public PublicChat()
    {
        
        base.Size = new(980.ScaleInt(), 866.ScaleInt());
        base.BackgroundColor = new(40,40,40,255);
        Anchor = ObjectAnchor.All;
        Controls.Add(MessageFlow = new()
        {
            Size = new(base.Size.X, 785.ScaleInt()),
            Location = new(0, 52.ScaleInt(), 0),
            BackgroundColor = new(40,40,40,255),
            Anchor = ObjectAnchor.All,
            HScrollPixels = Globals.Settings.PerScrollPixels,
        });
        MessageFlow.FlowUpdate += MessageFlowOnFlowUpdate;
        if (LuskiExperiments.GUI.MessageLiveSize.IsEnabled()) MessageFlow.SizeChanged += OnSizeChanged;
        LuskiExperiments.GUI.MessageLiveSize.EventToggled += MessageLiveSizeOnEventToggled;

        Task MessageLiveSizeOnEventToggled(bool arg)
        {
            if (arg) MessageFlow.SizeChanged += OnSizeChanged;
            else MessageFlow.SizeChanged -= OnSizeChanged;
            return Task.CompletedTask;
        }
        Controls.Add(titlecon = new UserControl()
        {
            Anchor = ObjectAnchor.Left | ObjectAnchor.Top | ObjectAnchor.Right,
            Size = new(980.ScaleInt(), 48.ScaleInt()),
            BackgroundColor = new(50,50,50,255),
        });
        UserCon =
            new(Globals.ms.TextureManager.GetTextureResource("person.png"))
            {
                Size = new(24.ScaleInt()),
                Location = new(944.ScaleInt(), 12.ScaleInt(),0),
                Anchor = ObjectAnchor.Right | ObjectAnchor.Top,
                Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context],
                BackgroundColor = Color4.LightGray
            };
        UserCon.MouseEnter += o => { UserCon.BackgroundColor = Color4.White; return Task.CompletedTask; };
        UserCon.MouseLeave += o => { UserCon.BackgroundColor = Color4.LightGray; return Task.CompletedTask; };
        UserCon.Clicked += UserConOnClicked;
        titlecon.Controls.Add(UserCon);
        
        
        titlecon.ForceDistanceUpdate(this);
        titlecon.Controls.Add(title = new Label(Globals.DefaultFont)
        {
            //Location = new(
        });
        titlecon.Controls.Add(desc = new Label(Globals.DefaultFont)
        {
            Color = new(161,161,161,255), 
            Location = new(title.Location.X + title.Size.X + 5, title.Location.Y, 0)
        });
        
        Controls.Add(tb = new()
        {
           // InsideColor = new(28, 28, 28, 255),
            //BorderColor = Color4.DarkCyan,
            Location = new(10.ScaleInt(), 824.ScaleInt(), 0),
            Size = new(960.ScaleInt(), 34.ScaleInt()),
            Anchor = ObjectAnchor.Bottom | ObjectAnchor.Left | ObjectAnchor.Right,
            HoverMouse = MouseCursor.IBeam,
            //Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context],
            //Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context],
            BackgroundColor = Color4.Red,
            TextLocation = TextLocation.LineCenter
        });
        tb.KeyPress += TbOnKeyPress;
        tb.OnRemoveLine += TbOnOnRemoveLine;
        tb.OnNewLine += TbOnOnNewLine;
        tb.ForceDistanceUpdate(this);
        //tb.KeyPress += TbOnKeyPress;
        //Globals.Luski.MainServer.MessageReceived += LuskiOnMessageReceived;
    }

    private bool ssss = false;
    
    private Task OnSizeChanged(IRenderObject arg)
    {
        if (ssss) return Task.CompletedTask;
        ssss = true;
        BlockDraw = true;
        bool off = false;
        for (int j = 0; j < MessageFlow.Controls.Length; j++)
        {
            if (MessageFlow.Controls[j] is ChatMessage cm && cm.Controls.Length != 0 && cm.Location.Y + cm.Size.Y >= 0 && !off)
            {
                if (cm.MessageObjs[0] is Label First)
                {
                    First.MaxSize = new(MessageFlow.Size.X - First.Location.X - 10.ScaleInt(), Int32.MaxValue);
                    First.ForceDistanceUpdate(cm);
                }
                for (int i = 1; i < cm.MessageObjs.Count; i++)
                {
                    cm.MessageObjs[i].Location = new(cm.MessageObjs[i].Location.X,
                        cm.MessageObjs[i - 1].Location.Y + cm.MessageObjs[i - 1].Size.Y,
                        cm.MessageObjs[i].Location.Z);
                    cm.MessageObjs[i].ForceDistanceUpdate(cm);
                    if (cm.MessageObjs[i] is Label l)
                    {
                        l.MaxSize = new(MessageFlow.Size.X - l.Location.X - 10.ScaleInt(), Int32.MaxValue);
                    }
                }

                int ny = cm.MessageObjs.Last().Size.Y + cm.MessageObjs.Last().Location.Y + (int)cm.VerticalPadding;
                if (cm.Location.Y + ny > MessageFlow.Size.Y) off = true;
                
                if (cm.Size.Y != ny)
                {
                    cm.Size = new(Size.X, ny);
                    MessageFlow.ReportSizeUpdate(cm);
                }
                ssss = false;
            }
        }
        BlockDraw = false;
        return Task.CompletedTask;
    }

    private async Task MessageFlowOnFlowUpdate(bool arg1, uint arg2, uint arg3)
    {
        if (!loadingm && arg1 && arg3 == 0 && (arg2 != 0 || MessageFlow.MaxScrollValue == 0))
        {
            loadingm = true;
            var messages = await Channel!.GetMessages(CancellationToken.None, first!, Globals.Settings.LoadPerChannel);
            if (messages.Count == 0) return;
            index = 0;
            fakeIndex = false;
            lastmIndex = null;
            lastUserIndex = null;
            LastChatMessageIndex = null;
            await AddMessagesIndex(messages);
            MessageFlow.ForceScrollUpdate();
            first = messages.Last();
            if (Globals.Settings.LoadPerChannel == messages.Count)loadingm = false;
        }
    }

    private async Task TbOnKeyPress(KeyboardKeyEventArgs arg)
    {
        //var t = Matrix4.Identity * Matrix4.CreateScale(2 / (float)Size.X, 2 / (float)Size.Y, 1) * Matrix4.CreateTranslation(-1.0f, -1.0f, 0.0f);
        //var tt = Matrix4.CreateOrthographicOffCenter(0.0f, Size.X, 0.0f, Size.Y, 1, -1);
        if (arg.Key == Keys.Enter && !arg.Shift)
        {
            
            await Channel!.SendMessage(tb.Text);
            tb.Text = string.Empty;
            tb.CursorLocation = 0;
        }
    }

    private Task TbOnOnRemoveLine()
    {
        BlockDraw = true;
        tb.Location = new(tb.Location.X, tb.Location.Y + (int)tb.Font.PixelHeight, 0);
        MessageFlow.Size = new(MessageFlow.Size.X, MessageFlow.Size.Y + (int)tb.Font.PixelHeight);
        BlockDraw = false;
        return Task.CompletedTask;
    }

    private Task TbOnOnNewLine()
    {
        BlockDraw = true;
        tb.Location = new(tb.Location.X, tb.Location.Y - (int)tb.Font.PixelHeight, 0);
        MessageFlow.Size = new(MessageFlow.Size.X, MessageFlow.Size.Y - (int)tb.Font.PixelHeight);
        BlockDraw = false;
        return Task.CompletedTask;
    }
    
    private bool um_open = false;
    private bool SeperateOffline = true;
    

    private async Task UserConOnClicked(IRenderObject arg)
    {
        um_open = !um_open;
        if (um_open)
        {
            if (memberflow is null)
            {
                int x = 232.ScaleInt();
                memberflow = new()
                {
                    BackgroundColor = new(34, 34, 34, 255),
                    Size = new(x, Size.Y - titlecon.Size.Y),
                    Location = new(Size.X - x, titlecon.Size.Y, 0),
                    Anchor = ObjectAnchor.Top | ObjectAnchor.Right | ObjectAnchor.Bottom
                };
                memberflow.ForceDistanceUpdate(this);
                Controls.Add(memberflow);
            }
            
            if (memberflow.Controls.Length == 0)
            {
                Dictionary<long, RoleView> Roles = new();
                RoleView? Offline = null;
                Dictionary<long, RoleView> Users = new();
            
                foreach (var m in (await Channel!.GetMembers()))
                {
                    Role top_role = (await m.GetRoles())[0];

                    if (m.Status == UserStatus.Offline && SeperateOffline)
                    {
                        if (Offline is null)
                        {
                            Offline = new();
                            memberflow.Controls.Add(Offline);
                        }
                        await Offline.AddUser(m, top_role);
                        Users.Add(m.Id, Offline);
                        memberflow.ReportSizeUpdate(Offline);
                    }
                    else
                    {
                        if (!Roles.ContainsKey(top_role.ID))
                        {
                            Roles.Add(top_role.ID, new(top_role));
                            int _index_ = 0;
                            for (int index_ = memberflow.Controls.Length-1; index_ >= 0; index_--)
                            {
                                RoleView r = ((RoleView)memberflow.Controls[index_]);
                                if (r.r is null) continue;
                                if (r.r.Index > top_role.Index)
                                {
                                    _index_ = index_ + 1;
                                    break;
                                }
                            }
                            memberflow.Controls.Insert(_index_, Roles[top_role.ID]);
                        }
                        await Roles[top_role.ID].AddUser(m, top_role);
                        Users.Add(m.Id, Roles[top_role.ID]);
                        memberflow.ReportSizeUpdate(Roles[top_role.ID]);
                    }
                }

                async Task OnServerOnStatusUpdate(UserStatus status, SocketUser user)
                {
                    if (!SeperateOffline && !Users.ContainsKey(user.Id)) return;
                    Role top_role2 = (await user.GetRoles())[0];
                    Globals.ms.Invoke(async () =>
                    {
                        BlockDraw = true;
                        if (status == UserStatus.Offline && user.Status != UserStatus.Offline)
                        {
                            Offline!.RemoveUser(user.Id);
                            if (Offline.i == 0)
                            {
                                memberflow.Controls.Remove(Offline);
                            }

                            if (!Roles.ContainsKey(top_role2.ID))
                            {
                                Roles.Add(top_role2.ID, new(top_role2));
                                int _index_ = 0;
                                for (int index_ = memberflow.Controls.Length - 1; index_ >= 0; index_--)
                                {
                                    RoleView r = ((RoleView)memberflow.Controls[index_]);
                                    if (r.r is null) continue;
                                    if (r.r.Index > top_role2.Index)
                                    {
                                        _index_ = index_ + 1;
                                        break;
                                    }
                                }

                                memberflow.Controls.Insert(_index_, Roles[top_role2.ID]);
                            }

                            await Roles[top_role2.ID].AddUser(user, top_role2);
                            memberflow.ReportSizeUpdate(Roles[top_role2.ID]);
                            if (Offline is not null) memberflow.ReportSizeUpdate(Offline);
                            Users[user.Id] = Roles[top_role2.ID];
                        }

                        if (status != UserStatus.Offline && user.Status == UserStatus.Offline)
                        {
                            Users[user.Id].RemoveUser(user.Id);
                            bool g = true;
                            if (Users[user.Id].i == 0)
                            {
                                if (Roles.ContainsKey(top_role2.ID)) Roles.Remove(top_role2.ID);
                                g = false;
                                memberflow.Controls.Remove(Users[user.Id]);
                            }

                            if (Offline is null)
                            {
                                Offline = new();
                                memberflow.Controls.Add(Offline);
                            }

                            await Offline.AddUser(user, top_role2);
                            memberflow.ReportSizeUpdate(Offline);
                            if (g) memberflow.ReportSizeUpdate(Users[user.Id]);
                            Users[user.Id] = Offline;
                        }

                        BlockDraw = false;
                        Globals.ms.DrawFrame();
                    });
                }

                Channel!.Server.StatusUpdate += OnServerOnStatusUpdate;
            }
            TryDraw();
            memberflow.Visible = true;

            MessageFlow.Size = new(MessageFlow.Size.X - memberflow.Size.X, MessageFlow.Size.Y);
            MessageFlow.ForceDistanceUpdate(this);
            tb.Size = new(tb.Size.X - memberflow.Size.X, tb.Size.Y);
            tb.ForceDistanceUpdate();
        }
        else
        {
            memberflow!.Visible = false;
            MessageFlow.Size = new(MessageFlow.Size.X + memberflow.Size.X, MessageFlow.Size.Y);
            MessageFlow.ForceDistanceUpdate(this);
            tb.Size = new(tb.Size.X + memberflow.Size.X, tb.Size.Y);
            tb.ForceDistanceUpdate();
        }
    }

    private SocketMessage? lastm;
    private long? lastUser;
    public ChatMessage? LastChatMessage;
    
    private SocketMessage? lastmIndex;
    private long? lastUserIndex;
    private bool fakeIndex = false;
    public ChatMessage? LastChatMessageIndex;
    private bool loadingm = false;

    public async Task LoadChannel(SocketChannel channel)
    {
        Channel = channel;
        BlockDraw = true;
        Globals.ms.BlockDraw = true;
        if (memberflow is not null)
        {
            await UserConOnClicked(UserCon!);
            memberflow.Controls.Clear();
            await UserConOnClicked(UserCon!);
        }
        title.Text = channel.Name;
        var five = 5.ScaleInt();
        title.Location = new(five + five,
            (titlecon.Size.Y - ((int)(title.Font.PixelHeight * ((float)title.Font.CurrentFonts[0].Face.Height /
                                                                title.Font.CurrentFonts[0].Face.UnitsPerEM)))) / 2,
            title.Location.Z);
        desc.Text = channel.Description;
        desc.Location = new((int)(title.Location.X + title.Size.X + five),
            (titlecon.Size.Y - ((int)(desc.Font.PixelHeight * ((float)desc.Font.CurrentFonts[0].Face.Height /
                                                               desc.Font.CurrentFonts[0].Face.UnitsPerEM)))) / 2,
            desc.Location.Z);
        if (Window is not null)
        {
            Window.Title = $"{channel.Name} | {channel.Server.Name} - Luski";
        }
        tb.WatermarkText = $"Message {channel.Name}";
        tb.Text = "";
        tb.CursorLocation = 0;
        tb.Focus();
        Globals.ms.BlockDraw = false;
        BlockDraw = false;
        TryDraw();
    }

    public void ClearChat()
    {
        MessageFlow.Controls.Clear();
        MessageFlow.ScrollValue = 0;
        MessageFlow.ScrollUpdatesInterval = 33;
        lastm = null;
        lastUser = null;
        LastChatMessage = null;
        first = null;
    }

    public async Task AddMessages(IEnumerable<SocketMessage> messages, bool reverse = true)
    {
        MessageFlow.BlockDraw = true;
        if (reverse)
        {
            foreach (SocketMessage message in messages.Reverse())
            {
                await AddMessage(message);
            }
        }
        else
        {
            foreach (SocketMessage message in messages)
            {
                await AddMessage(message);
            }
        }
        MessageFlow.BlockDraw = false;
    }
    
    public async Task AddMessagesIndex(IEnumerable<SocketMessage> messages)
    {
        MessageFlow.BlockDraw = true;
        index = 0;
        foreach (SocketMessage message in messages.Reverse())
        {
            await AddMessageIndex(message);
        }
        MessageFlow.BlockDraw = false;
    }
    
    public async Task AddMessage(SocketMessage Message, bool scrool = false)
    {
        if (first is null) first = Message;
        bool hasbeentenmin = false;
        if (lastm is not null)
        {
            DateTime chan = Channel!.Epoch.AddMilliseconds(lastm.ID >> 22).ToLocalTime();
            hasbeentenmin =
                Channel!.Epoch.AddMilliseconds(lastm.ID >> 22).ToLocalTime().AddMinutes(10) <
                Channel!.Epoch.AddMilliseconds(Message.ID >> 22).ToLocalTime();
        }
            
        lastm = Message;
        if (lastUser is null || lastUser != Message.ProfileID || hasbeentenmin)
        {
            if (Window is null || !Globals.ms.InvokeRequired)
            {
                MessageFlow.Controls.Add(LastChatMessage = await ChatMessage.MakeChatMessage(this, Message));
                if (scrool) MessageFlow.ScrollToBottom();
            }
            else
            {
                Globals.ms.Invoke(async () =>
                {
                    LastChatMessage = await ChatMessage.MakeChatMessage(this, Message);
                    MessageFlow.Controls.Add(LastChatMessage);
                    if (scrool) MessageFlow.ScrollToBottom();
                    Window.TryDraw();
                });
            }
        }
        else
        {
            if (Window is null || !Globals.ms.InvokeRequired)
            {
                await LastChatMessage!.AddMessage(Message);
                if (scrool) MessageFlow.ScrollToBottom();
            }
            else
            {
                Globals.ms.Invoke(() =>
                {
                    LastChatMessage!.AddMessage(Message);
                    if (scrool) MessageFlow.ScrollToBottom();
                    Window!.TryDraw();
                });
            }
        }
        lastUser = Message.ProfileID;
    }

    private int index = 0;
    
    public async Task AddMessageIndex(SocketMessage Message)
    {
        bool hasbeentenmin = false;
        if (lastmIndex is not null)
            hasbeentenmin =
                Channel!.Epoch.AddMilliseconds(lastmIndex.ID >> 22).ToLocalTime().AddMinutes(10) <
                Channel!.Epoch.AddMilliseconds(Message.ID >> 22).ToLocalTime();
        lastmIndex = Message;
        if (lastUserIndex is null || lastUserIndex != Message.ProfileID || hasbeentenmin)
        {
            if (Window is null || !Globals.ms.InvokeRequired)
            {
                MessageFlow.Controls.Insert(index, LastChatMessageIndex = await ChatMessage.MakeChatMessage(this, Message));
                LastChatMessageIndex.LoadToParent(MessageFlow, Globals.ms);
                index++;
            }
            else
            {
                LastChatMessageIndex = await ChatMessage.MakeChatMessage(this, Message);
                Globals.ms.Invoke(() =>
                {
                    MessageFlow.Controls.Insert(index, LastChatMessageIndex);
                    LastChatMessageIndex.LoadToParent(MessageFlow, Globals.ms);
                    index++;
                    Window.TryDraw();
                });
            }
        }
        else
        {
            if (Window is null || !Globals.ms.InvokeRequired)
            {
                await LastChatMessageIndex!.AddMessage(Message);
            }
            else
            {
                Globals.ms.Invoke(() =>
                {
                    LastChatMessageIndex!.AddMessage(Message);
                    Window!.TryDraw();
                });
            }
        }
        lastUserIndex = Message.ProfileID;
    }
}