using System;

namespace MainShell.Hardware.Acs
{
    /// <summary>
    /// Represents one ACS controller address definition.
    /// Used to describe read/write address, data type, length, and note.
    /// </summary>
    public sealed class AcsAddressDefinition
    {
        public AcsAddressDefinition(string name, int address, Type dataType, int length = 1, string description = "")
            : this(name, dataType, length, description, address, null)
        {
        }

        public AcsAddressDefinition(string name, string symbolicAddress, Type dataType, int length = 1, string description = "")
            : this(name, dataType, length, description, null, symbolicAddress)
        {
        }

        private AcsAddressDefinition(string name, Type dataType, int length, string description, int? address, string symbolicAddress)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentException("ACS address name cannot be empty.", nameof(name));
            }

            if (dataType == null)
            {
                throw new ArgumentNullException(nameof(dataType));
            }

            if (length <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(length), length, "ACS address length must be greater than 0.");
            }

            if (address.HasValue && address.Value < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(address), address, "ACS address cannot be less than 0.");
            }

            if (!address.HasValue && string.IsNullOrWhiteSpace(symbolicAddress))
            {
                throw new ArgumentException("ACS address cannot be empty.", nameof(symbolicAddress));
            }

            Name = name;
            Address = address;
            SymbolicAddress = symbolicAddress ?? string.Empty;
            DataType = dataType;
            Length = length;
            Description = description ?? string.Empty;
        }

        /// <summary>
        /// Semantic name, for example ServoOn or ActualPosition.
        /// </summary>
        public string Name { get; }

        /// <summary>
        /// Numeric address in the ACS controller.
        /// </summary>
        public int? Address { get; }

        /// <summary>
        /// Symbolic address in the ACS controller, for example AP_SAPos1.
        /// </summary>
        public string SymbolicAddress { get; }

        /// <summary>
        /// Data type mapped to this address.
        /// </summary>
        public Type DataType { get; }

        /// <summary>
        /// Continuous address length. Default is 1.
        /// </summary>
        public int Length { get; }

        /// <summary>
        /// Address description for maintenance and diagnostics.
        /// </summary>
        public string Description { get; }

        public bool HasNumericAddress
        {
            get { return Address.HasValue; }
        }

        public bool HasSymbolicAddress
        {
            get { return !string.IsNullOrWhiteSpace(SymbolicAddress); }
        }

        public bool IsBoolean
        {
            get { return DataType == typeof(bool); }
        }

        public bool IsInteger
        {
            get { return DataType == typeof(short) || DataType == typeof(ushort) || DataType == typeof(int) || DataType == typeof(uint) || DataType == typeof(long) || DataType == typeof(ulong); }
        }

        public bool IsFloatingPoint
        {
            get { return DataType == typeof(float) || DataType == typeof(double) || DataType == typeof(decimal); }
        }

        public override string ToString()
        {
            if (HasNumericAddress)
            {
                return $"{Name} [Address={Address.Value}, Type={DataType.Name}, Length={Length}]";
            }

            return $"{Name} [Symbol={SymbolicAddress}, Type={DataType.Name}, Length={Length}]";
        }

        public static AcsAddressDefinition CreateBool(string name, int address, string description = "")
        {
            return new AcsAddressDefinition(name, address, typeof(bool), 1, description);
        }

        public static AcsAddressDefinition CreateBool(string name, int address, int length, string description = "")
        {
            return new AcsAddressDefinition(name, address, typeof(bool), length, description);
        }

        public static AcsAddressDefinition CreateInt32(string name, int address, string description = "")
        {
            return new AcsAddressDefinition(name, address, typeof(int), 1, description);
        }

        public static AcsAddressDefinition CreateInt32(string name, int address, int length, string description = "")
        {
            return new AcsAddressDefinition(name, address, typeof(int), length, description);
        }

        public static AcsAddressDefinition CreateDouble(string name, int address, string description = "")
        {
            return new AcsAddressDefinition(name, address, typeof(double), 1, description);
        }

        public static AcsAddressDefinition CreateDouble(string name, int address, int length, string description = "")
        {
            return new AcsAddressDefinition(name, address, typeof(double), length, description);
        }

        public static AcsAddressDefinition CreateSymbol(string name, string symbolicAddress, Type dataType, int length = 1, string description = "")
        {
            return new AcsAddressDefinition(name, symbolicAddress, dataType, length, description);
        }

        public static AcsAddressDefinition CreateSymbol(string name, Type dataType, int length = 1, string description = "")
        {
            return new AcsAddressDefinition(name, name, dataType, length, description);
        }

        public static AcsAddressDefinition CreateBoolSymbol(string name, string symbolicAddress, int length = 1, string description = "")
        {
            return new AcsAddressDefinition(name, symbolicAddress, typeof(bool), length, description);
        }

        public static AcsAddressDefinition CreateBoolSymbol(string name, int length = 1, string description = "")
        {
            return new AcsAddressDefinition(name, name, typeof(bool), length, description);
        }

        public static AcsAddressDefinition CreateInt32Symbol(string name, string symbolicAddress, int length = 1, string description = "")
        {
            return new AcsAddressDefinition(name, symbolicAddress, typeof(int), length, description);
        }

        public static AcsAddressDefinition CreateInt32Symbol(string name, int length = 1, string description = "")
        {
            return new AcsAddressDefinition(name, name, typeof(int), length, description);
        }

        public static AcsAddressDefinition CreateDoubleSymbol(string name, string symbolicAddress, int length = 1, string description = "")
        {
            return new AcsAddressDefinition(name, symbolicAddress, typeof(double), length, description);
        }

        public static AcsAddressDefinition CreateDoubleSymbol(string name, int length = 1, string description = "")
        {
            return new AcsAddressDefinition(name, name, typeof(double), length, description);
        }

        public static AcsAddressDefinition CreateString(string name, int address, int length, string description = "")
        {
            return new AcsAddressDefinition(name, address, typeof(string), length, description);
        }

        public static AcsAddressDefinition CreateStringSymbol(string name, string symbolicAddress, int length, string description = "")
        {
            return new AcsAddressDefinition(name, symbolicAddress, typeof(string), length, description);
        }

        public static AcsAddressDefinition CreateStringSymbol(string name, int length, string description = "")
        {
            return new AcsAddressDefinition(name, name, typeof(string), length, description);
        }
    }
}
