using System.Text;
using skyscraper5.Skyscraper.IO;
using skyscraper8.Ieee802_1AB.Model;

namespace skyscraper8.Ieee802_1AB;

public class LldpFrame
{
    public LldpFrame(byte[] contents)
    {
        MemoryStream ms = new MemoryStream(contents, false);
        while (ms.Position < ms.Length)
        {
            ushort tlv = ms.ReadUInt16BE();
            int type = (tlv & 0xfe00) >> 9;
            int length = (tlv & 0x01ff);
            byte[] value = ms.ReadBytes(length);
            switch (type)
            {
                case 1: //Chassis
                    this.Chassis = new LldpChassis(value);
                    break;
                case 2: //Port
                    this.Port = new LldpPort(value);
                    break;
                case 3: //Time to Live
                    this.TimeToLive = (value[0] << 8) | value[1];
                    this.TimeToLive--;
                    break;
                case 4:
                    this.PortDescription = Encoding.UTF8.GetString(value);
                    break;
                case 5:
                    this.SystemName = Encoding.UTF8.GetString(value);
                    break;
                case 6:
                    this.SystemDescription = Encoding.UTF8.GetString(value);
                    break;
                case 7:
                    this.Capabilities = new LldpCapabilities(value);
                    break;
                case 8:
                    this.ManagementAddress = new LldpManagementAddress(value);
                    break;
                case 127:
                    string OrganizationUniqueCode = BitConverter.ToString(value, 0, 3);
                    byte OrganizationallyDefinedSubtype = value[3];
                    MemoryStream payload = new MemoryStream(value, 4, value.Length - 4);
                    switch (OrganizationUniqueCode)
                    {
                        case "00-80-C2":    //IEEE 802.1 Chair
                            switch (OrganizationallyDefinedSubtype)
                            {
                                case 6: //VLAN ID
                                    this.PortVlanIdentifier = ms.ReadUInt16BE();
                                    break;
                                default:
                                    throw new NotImplementedException(String.Format("Unknown IEEE 802.1 Subtype {0}! Please share a sample of this stream.",OrganizationallyDefinedSubtype));
                            }
                            break;
                        case "00-12-0F":    //IEEE 802.3 Chair
                            switch (OrganizationallyDefinedSubtype)
                            {
                                //see 802.1AB-2009.pdf, Annex F
                                case 1: //MAC/PHY configuration/status TLV format
	                                this.MacPhyConfigurationStatus = new LldpMacPhyConfigurationStatus(payload);
	                                break;
                                default:
                                    throw new NotImplementedException(String.Format("Unknown IEEE 802.3 Subtype {0}! Please share a sample of this stream.", OrganizationallyDefinedSubtype));
                            }
                            break;
                        case "00-12-BB":
	                        switch (OrganizationallyDefinedSubtype) //TR-41. Don't know which document yet.
	                        {
                                case 1: //Media Capabilities
	                                this.MediaCapabilities = new LldpMediaCapabilities(payload);
	                                break;
                                default:
	                                throw new NotImplementedException(String.Format("Unknown TR-41 Subtype {0}! Please share a sample of this stream.", OrganizationallyDefinedSubtype));
}
	                        break;
						default:
                            throw new NotImplementedException(String.Format("Unknown Organization Unique Code {0}. Please share a sample of this stream.",OrganizationUniqueCode));
                    }

                    break;
                case 0: //End of LLDP PDU
                    return;
                default:
                    throw new NotImplementedException( String.Format("LLDP Option {0} not yet supported. Please share a sample of this stream.",type));
            }
        }
    }

    public LldpMediaCapabilities MediaCapabilities { get; set; }

    public LldpMacPhyConfigurationStatus MacPhyConfigurationStatus { get; set; }

    public ushort? PortVlanIdentifier { get; set; }

    public LldpManagementAddress ManagementAddress { get; private set; }

    public LldpCapabilities Capabilities { get; set; }

    public string SystemDescription { get; set; }

    public string SystemName { get; set; }

    public string PortDescription { get; set; }

    public int? TimeToLive { get; set; }

    public LldpPort Port { get; set; }

    public LldpChassis Chassis { get; private set; }

    protected bool Equals(LldpFrame other)
    {
	    return Port.Equals(other.Port) && Chassis.Equals(other.Chassis);
    }

    public override bool Equals(object? obj)
    {
	    if (ReferenceEquals(null, obj)) 
		    return false;
	    if (ReferenceEquals(this, obj)) 
		    return true;
	    if (obj.GetType() != this.GetType()) 
		    return false;
	    return Equals((LldpFrame)obj);
    }

    public override int GetHashCode()
    {
	    return HashCode.Combine(Port, Chassis);
    }

    public override string ToString()
    {
	    StringBuilder sb = new StringBuilder();
	    if (!string.IsNullOrEmpty(SystemName))
	    {
		    sb.Append(SystemName);
	    }
	    else
	    {
		    sb.Append(Chassis.ToString());
	    }

	    sb.Append(", ");
	    sb.Append(Port.ToString());
	    return sb.ToString();
    }
}