 
 
using System;
using System.Collections.Generic;
using System.Linq;
using InSimDotNet;
using InSimDotNet.Packets;
namespace MultiLineButton
{
    public class MultiLineButton
    {
        private readonly InSim _inSim;
        private readonly byte _clickId;
        private readonly byte _ucid;
        private readonly byte _width;
        private readonly byte _height;
        private readonly byte _top;
        private readonly byte _left;
        private readonly byte _rowHeight;
        private readonly byte _maxCharsPerRow;
        private readonly string _text;
        private readonly bool _showScrollbar;
        private readonly ButtonStyles _style;
        private readonly List<string> _lines;
        private int _scrollPosition;
        private readonly byte _scrollbarWidth;
        private readonly Dictionary<byte, ButtonInfo> _buttonInfo;
        private struct ButtonInfo
        {
            public ButtonType Type { get; set; }
            public string Text { get; set; }
        }
        private enum ButtonType
        {
            Background,
            TextLine,
            ScrollUp,
            ScrollTrack,
            ScrollDown
        }
        public MultiLineButton(
            InSim inSim,
            byte clickId,
            byte ucid,
            byte width,
            byte height,
            byte top,
            byte left,
            byte rowHeight,
            byte maxCharsPerRow,
            string text,
            ButtonStyles style = ButtonStyles.ISB_DARK)
        {
            _inSim = inSim;
            _clickId = clickId;
            _ucid = ucid;
            _width = width;
            _height = height;
            _top = top;
            _left = left;
            _rowHeight = rowHeight;
            _maxCharsPerRow = maxCharsPerRow;
            _text = text;
            _style = style;
            _scrollPosition = 0;
            _scrollbarWidth = (byte)Math.Max(4, Math.Round(rowHeight * 0.75));
            _buttonInfo = new Dictionary<byte, ButtonInfo>();
            _lines = SplitTextIntoLines(text, maxCharsPerRow);
            _showScrollbar = _lines.Count * rowHeight > height;
            // Setup event handlers
            _inSim.Bind<IS_BTC>((inSim, packet) => HandleButtonClick(packet));
        }
        public void Show()
        {
            DrawButtons();
            LogButtonInfo();
        }
        public void Hide()
        {
            ClearButtons();
        }
        private void DrawButtons()
        {
            ClearButtons();
            var textAreaWidth = (byte)(_showScrollbar ? _width - _scrollbarWidth : _width);
            var maxVisibleRows = _height / _rowHeight;
            // Background button
            SendButton(
                _clickId,
                _top,
                _left,
                _width,
                _height,
                "",
                _style | ButtonStyles.ISB_DARK,
                ButtonType.Background
            );
            // Draw scrollbar if needed
            if (_showScrollbar)
            {
                var canScrollUp = _scrollPosition > 0;
                var canScrollDown = _scrollPosition + maxVisibleRows < _lines.Count;
                var scrollbarLeft = (byte)(_left + textAreaWidth);
                // Up arrow
                if (canScrollUp)
                {
                    SendButton(
                        (byte)(_clickId + 1),
                        _top,
                        scrollbarLeft,
                        _scrollbarWidth,
                        _rowHeight,
                        "▲",
                        _style | ButtonStyles.ISB_CLICK | ButtonStyles.ISB_DARK,
                        ButtonType.ScrollUp
                    );
                }
                // Scrollbar track
                if (maxVisibleRows > 2)
                {
                    var scrollbarHeight = (byte)(_height - (2 * _rowHeight));
                    var scrollbarThumbHeight = (byte)(scrollbarHeight * maxVisibleRows / Math.Max(1, _lines.Count));
                    var scrollbarPosition = (byte)(_top + _rowHeight + 
                        (_scrollPosition * (scrollbarHeight - scrollbarThumbHeight) / 
                        Math.Max(1, _lines.Count - maxVisibleRows)));
                    SendButton(
                        (byte)(_clickId + 2),
                        scrollbarPosition,
                        scrollbarLeft,
                        _scrollbarWidth,
                        scrollbarThumbHeight,
                        "",
                        _style | ButtonStyles.ISB_DARK,
                        ButtonType.ScrollTrack
                    );
                }
                // Down arrow
                if (canScrollDown)
                {
                    SendButton(
                        (byte)(_clickId + 3),
                        (byte)(_top + _height - _rowHeight),
                        scrollbarLeft,
                        _scrollbarWidth,
                        _rowHeight,
                        "▼",
                        _style | ButtonStyles.ISB_CLICK | ButtonStyles.ISB_DARK,
                        ButtonType.ScrollDown
                    );
                }
            }
            // Draw text lines
            byte currentId = (byte)(_clickId + 4);
            for (var i = 0; i < maxVisibleRows && i + _scrollPosition < _lines.Count; i++)
            {
                var text = _lines[i + _scrollPosition];
                // Add ellipsis if there's more text below
                if (i == maxVisibleRows - 1 && i + _scrollPosition < _lines.Count - 1)
                {
                    text += "...";
                }
                SendButton(
                    currentId++,
                    (byte)(_top + i * _rowHeight),
                    _left,
                    textAreaWidth,
                    _rowHeight,
                    text,
                    _style | ButtonStyles.ISB_C4,
                    ButtonType.TextLine
                );
            }
        }
        private void SendButton(
            byte clickId, 
            byte top, 
            byte left, 
            byte width, 
            byte height, 
            string text, 
            ButtonStyles style,
            ButtonType type)
        {
            _buttonInfo[clickId] = new ButtonInfo { Type = type, Text = text };
            var button = new IS_BTN
            {
                ReqI = 1,
                ClickID = clickId,
                UCID = _ucid,
                T = top,
                L = left,
                W = width,
                H = height,
                Text = text,
                BStyle = style
            };
            _inSim.Send(button);
        }
        private void HandleButtonClick(IS_BTC packet)
        {
            if (!_buttonInfo.ContainsKey(packet.ClickID))
                return;
            var buttonInfo = _buttonInfo[packet.ClickID];
            var maxVisibleRows = _height / _rowHeight;
            switch (buttonInfo.Type)
            {
                case ButtonType.ScrollUp when _scrollPosition > 0:
                    _scrollPosition--;
                    DrawButtons();
                    break;
                case ButtonType.ScrollDown when _scrollPosition + maxVisibleRows < _lines.Count:
                    _scrollPosition++;
                    DrawButtons();
                    break;
            }
        }
        private void LogButtonInfo()
        {
            Console.WriteLine("\nButton Information:");
            foreach (KeyValuePair<byte, ButtonInfo> button in _buttonInfo)
            {
                Console.WriteLine($"ClickID {button.Key}: Type={button.Value.Type}, Text={button.Value.Text}");
            }
            Console.WriteLine($"Total Buttons: {_buttonInfo.Count}");
        }
        private void ClearButtons()
        {
            foreach (var clickId in _buttonInfo.Keys.ToList())
            {
                var button = new IS_BFN
                {
                    ReqI = 1,
                    UCID = _ucid,
                    ClickID = clickId,
                    SubT = ButtonFunction.BFN_DEL_BTN
                };
                _inSim.Send(button);
            }
            _buttonInfo.Clear();
        }
        private static List<string> SplitTextIntoLines(string text, int maxCharsPerLine)
        {
            var words = text.Split(' ');
            var lines = new List<string>();
            var currentLine = "";
            foreach (var word in words)
            {
                if (currentLine.Length + word.Length + 1 <= maxCharsPerLine)
                {
                    if (currentLine.Length > 0)
                        currentLine += " ";
                    currentLine += word;
                }
                else
                {
                    if (currentLine.Length > 0)
                    lines.Add(currentLine);
                    currentLine = word;
                }
            }
            if (currentLine.Length > 0)
                lines.Add(currentLine);
            return lines;
        }
    }
    // Example usage
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("=== InSim Multi-line Button Test ===\n");
            Console.WriteLine("This test demonstrates the InSim packet flow for multi-line buttons.\n");
            var inSim = new InSim();
            MultiLineButton multiLineButton = null;
            try
            {
                inSim.Initialize(new InSimSettings
                {
                    Host = "127.0.0.1",
                    Port = 29999,
                    Admin = "",
                    Prefix = '/',
                    Interval = 100,
                    Flags = InSimFlags.ISF_LOCAL
                });
                var testText = @"^7InSim Packets Explained:
1. ^7IS_BTN (Button) Packet:
- ^7ReqI: Request ID (1)
- ^7UCID: Connection ID
- ^7ClickID: Unique button ID
- ^7Inst: 0
- ^7BStyle: Button style flags
- ^7TypeIn: Max chars (0-240)
- ^7L,T: Position
- ^7W,H: Size
- ^7Text: Button text
2. ^7IS_BTC (Button Click):
- ^7ReqI: Copy of button ReqI
- ^7UCID: Connection that clicked
- ^7ClickID: Clicked button ID
- ^7Inst: 0
- ^7CFlags: Click flags
3. ^7IS_BFN (Button Function):
- ^7ReqI: Request ID
- ^7SubT: Delete/clear/etc
- ^7UCID: Target connection
- ^7ClickID: Target button";
                multiLineButton = new MultiLineButton(
                    inSim: inSim,
                    clickId: 1,
                    ucid: 0,
                    width: 40,
                    height: 30,
                    top: 20,
                    left: 20,
                    rowHeight: 5,
                    maxCharsPerRow: 20,
                    text: testText,
                    style: ButtonStyles.ISB_C1
                );
                multiLineButton.Show();
                Console.WriteLine("\nProgram is running. Press 'H' to hide the button, 'S' to show it again, or 'Q' to quit.");
                
                while (true)
                {
                    var key = Console.ReadKey(true);
                    switch (char.ToUpper(key.KeyChar))
                    {
                        case 'H':
                            multiLineButton.Hide();
                            Console.WriteLine("Button hidden");
                            break;
                        case 'S':
                            multiLineButton.Show();
                            Console.WriteLine("Button shown");
                            break;
                        case 'Q':
                            Console.WriteLine("Exiting...");
                            return;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"An error occurred: {ex.Message}");
            }
            finally
            {
                if (multiLineButton != null)
                {
                    multiLineButton.Hide();
                }
                
                if (inSim != null)
                {
                    inSim.Disconnect();
                    inSim.Dispose();
                }
            }
        }
    }
}
<?php 
    openPrivButton( "tt_car_note",$tt_left,$tt_top+142,68,4,3,$tt_time-12,16,"^3This table has to be partially laid out by hand ^8and was last updated: ^019 May 2025"%nl%^1If a driver beats the existing fastest time for a vehicle shown above, then table order may be wrong%nl%^7If a driver sets a time for a vehicle that is *NOT* on this list,"%nl%^2then I will have to manually update the table");
?><?php 
    openPrivButton( "tt_car_note",$tt_left,$tt_top+142,68,4,3,$tt_time-12,16,"^3This table has to be partially laid out by hand ^8and was last updated: ^019 May 2025"
  . "%nl%^1If a driver beats the existing fastest time for a vehicle shown above, then table order may be wrong"          
  . "%nl%^7If a driver sets a time for a vehicle that is *NOT* on this list,"
  . "%nl%^2then I will have to manually update the table");
?> , keep up the wonderful work guys
 , keep up the wonderful work guys 
