using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using Aitex.Core.RT.Log;
using BlinkDataType = System.Collections.Generic.KeyValuePair<MECF.Framework.Common.Device.Bases.SignalTowerActions, uint>;

namespace MECF.Framework.Common.Device.Bases;

/// <summary>
/// 信号灯塔元件闪烁模式。
/// </summary>
[Serializable]
[DataContract]
public class STBlinkPattern : IComparable, ICloneable
{
    #region Variables

    /// <summary>
    /// 以-或.配置闪烁模式的字串正则表达式。
    /// </summary>
    public const string REG_PATT_BLINK_PATTERN = @"^([-\.])+$";

    /// <summary>
    /// 模式字符串中每个字符对应的延时时长，单位毫秒。
    /// </summary>
    private const uint DELAY_MS_PER_CHAR = 100;


    private const string PATTERN_FAST = "-----..";
    private const string PATTERN_SLOW = "-----.......";
    private const string PATTERN_JOB_DONE = "-----.......";

    #endregion

    #region Constructors

    /// <summary>
    /// 构造信号灯元件闪烁模式。
    /// <para>如果未指定闪烁模式，则默认开一秒，关一秒，无限循环。</para>
    /// </summary>
    public STBlinkPattern()
    {
        TotalCycles = 0;
        Pattern = PATTERN_SLOW;
    }

    /// <summary>
    /// 创建信号灯塔元件闪烁模式对象的实例。
    /// </summary>
    /// /// <param name="pattern">
    /// 闪烁模式。
    /// 请参考属性<see cref="Pattern"/>以或许模式的设置方法。
    /// </param>
    /// <param name="priority">当前工作模式优先级。</param>
    /// <param name="cycle">总循环次数，0或负值表示无限循环。</param>
    public STBlinkPattern(string pattern, int priority, int cycle = int.MaxValue)
    {
        Debug.Assert(!string.IsNullOrEmpty(pattern), "pattern can not be null");
        
        TotalCycles = cycle;
        Pattern = pattern;
        Priority = priority;
        IsPlcMode = false;
    }

    /// <summary>
    /// 创建信号灯塔元件闪烁模式对象的实例。
    /// </summary>
    /// <param name="priority">当前工作模式优先级。</param>
    /// <param name="cycle">总循环次数，0或负值表示无限循环。</param>
    /// <param name="frequencyHz">
    /// PLC控制模式下的闪烁频率。
    /// </param>
    /// <param name="fallbackPattern">
    /// 如果PLC不支持设置闪烁频率，则回落到此模式。
    /// </param>
    public STBlinkPattern(float frequencyHz, int priority, int cycle = int.MaxValue, string fallbackPattern = PATTERN_SLOW)
    {
        TotalCycles = cycle;
        Pattern = fallbackPattern;
        FrequencyHz = frequencyHz;
        Priority = priority;
        IsPlcMode = true;
    }


    #endregion

    #region Properties
    
    /// <summary>
    /// 返回是否为PLC控制模式。
    /// <br/>
    /// <remarks>PLC模式指闪烁效果有PLC控制，以提供更精确的控制效果。</remarks>
    /// </summary>
    public bool IsPlcMode { get; }
    

    /// <summary>
    /// 设置或返回总循环次数。
    /// </summary>
    [DataMember]
    public int TotalCycles { get; set; }
    
    /// <summary>
    /// 设置或返回闪烁模式。
    /// <remarks>
    /// 闪烁模式以字符串形式表达，表达式中仅允许包含字符’-‘（英文减号）和’.‘（英文句号）。
    /// 其中<value>-</value>表示开100ms，<value>.</value>表示关100ms。
    /// <br/>
    /// 通过'-'和‘.‘的组合，可描述不同的闪烁模式。
    /// </remarks>
    /// </summary>
    [DataMember]
    public string Pattern { get; set; }
    
    /// <summary>
    /// 闪烁频率。
    /// </summary>
    public float FrequencyHz { get; set; }

    /// <summary>
    /// 当前工作模式的优先级。
    /// </summary>
    public int Priority { get; set; }
    
    #endregion

    #region Static Methods

    /// <summary>
    /// 解析闪烁模式字串，生成闪烁数据供M<see cref="SignalTowerLightBase"/>产生闪烁效果。
    /// </summary>
    /// <param name="pattern"></param>
    /// <param name="blinkData">闪烁效果数据。</param>
    /// <param name="reason">解析失败原因。</param>
    /// <returns>
    /// <value>True: 解析成功</value>
    /// <value>False: 解析失败</value>
    /// </returns>
    public static bool GetBlinkData(STBlinkPattern pattern, out List<BlinkDataType> blinkData, out string reason)
    {
        // var regex = new Regex(@"(([-\.])(\2*))");

        reason = "";
        blinkData = new List<BlinkDataType>();
        
        // 校验Pattern字串的正则，Pattern仅允许有字符’-‘和’.’组成。
        var regPatternFormat = new Regex(REG_PATT_BLINK_PATTERN);
        
        // Pattern分组正则
        var regGroup = new Regex(@"([\-\.])\1*");

        if (!regPatternFormat.IsMatch(pattern.Pattern))
        {
            // 如果存在除-和.以外的字符，则为非法Pattern字串。
            reason = "pattern contains illegal characters";
            LOG.Error(reason);
            return false;
        }
        
        var matches = regGroup.Matches(pattern.Pattern);

        foreach (Match match in matches)
        {
            if (match.Success && match.Groups.Count == 2)
            {
                var leadChar = match.Groups[1].Value; // 编组字符
                var length = (uint)match.Length;
                
                // 根据每组的字符判断动作。
                SignalTowerActions action;
                switch (leadChar)
                {
                    case "-":
                        action = SignalTowerActions.On;
                        break;
                    case ".":
                        action = SignalTowerActions.Off;
                        break;
                    
                    default:
                        LOG.Error($"Undefined character {leadChar} in pattern");
                        continue;
                }

                blinkData.Add(new(action, DELAY_MS_PER_CHAR * length));
            }

        }

        return true;
    }

    /// <summary>
    /// <inheritdoc cref="Object.ToString()"/>
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return $"IsPlcMode={IsPlcMode}, Freq={FrequencyHz}Hz, Pattern={Pattern}";
    }

    /// <inheritdoc />
    public object Clone()
    {
        return MemberwiseClone();
    }

    /// <summary>
    /// 对比当前STBlinkPattern是否和指定的STBlinkPattern相同。
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public int CompareTo(object obj)
    {
        if (obj is not STBlinkPattern target)
            return -1;

        return (target.IsPlcMode == IsPlcMode 
                && target.TotalCycles == TotalCycles
                && Math.Abs(target.FrequencyHz - FrequencyHz) < 0.01 
                && target.Pattern == Pattern
                && target.Priority == Priority)
            ? 0
            : -1;
    }

    /// <summary>
    /// 获取预设的快速闪烁模式。
    /// </summary>
    /// <remarks>
    /// 开200ms，关200ms，无限循环。
    /// </remarks>
    /// <returns></returns>
    public static STBlinkPattern GetDefaultPattern()
    {
        return new STBlinkPattern(PATTERN_SLOW, (int)SignalTowerActions.Customized);
    }

    
    /// <summary>
    /// 获取预设的工艺完成闪烁模式。
    /// </summary>
    /// <remarks>
    /// 以开200ms、关200ms方式连续闪烁3次，然后关1s，循环3次。
    /// </remarks>
    /// <returns></returns>
    public static STBlinkPattern GetJobDonePattern()
    {
        return new STBlinkPattern(PATTERN_JOB_DONE, priority:-1, cycle:3);
    }

    #endregion
}