using System;
using System.Text.RegularExpressions;

namespace StandardScene.Signal.Plc
{
    /// <summary>西门子 DB 字节/位地址拼装与缓冲区位操作。</summary>
    internal static class PlcAddress
    {
        private static readonly Regex Addr = new Regex(
            @"^DB(\d+)\.(?:DBB|DBX|DBW)?(\d+)(?:\.(\d+))?$",
            RegexOptions.IgnoreCase | RegexOptions.Compiled);

        public static string Bit(int db, int absByte, int bit) =>
            $"DB{Math.Max(1, db)}.DBX{Math.Max(0, absByte)}.{ClampBit(bit)}";

        public static string Byte(int db, int absByte) =>
            $"DB{Math.Max(1, db)}.DBB{Math.Max(0, absByte)}";

        public static int ClampBit(int bit)
        {
            if (bit < 0) return 0;
            if (bit > 7) return 7;
            return bit;
        }

        public static bool TryParse(string address, out int db, out int absByte, out int bit)
        {
            db = 0;
            absByte = 0;
            bit = 0;
            if (string.IsNullOrWhiteSpace(address))
                return false;
            var m = Addr.Match(address.Trim());
            if (!m.Success)
                return false;
            db = int.Parse(m.Groups[1].Value);
            absByte = int.Parse(m.Groups[2].Value);
            if (m.Groups[3].Success)
                bit = ClampBit(int.Parse(m.Groups[3].Value));
            return true;
        }

        public static bool GetBit(byte[] buffer, int originByte, int absByte, int bit)
        {
            var i = absByte - originByte;
            if (buffer == null || i < 0 || i >= buffer.Length)
                return false;
            return (buffer[i] & (1 << ClampBit(bit))) != 0;
        }

        public static void SetBit(byte[] buffer, int originByte, int absByte, int bit, bool value)
        {
            var i = absByte - originByte;
            if (buffer == null || i < 0 || i >= buffer.Length)
                return;
            var mask = (byte)(1 << ClampBit(bit));
            if (value)
                buffer[i] |= mask;
            else
                buffer[i] = (byte)(buffer[i] & ~mask);
        }
    }
}
