"""Precision Time Protocol v2, IEEE 1588-2008"""
import logging

from packetracer import packetracer
from packetracer.structcbs import pack_Q, unpack_Q

logger = logging.getLogger("packetracer")


PTPv2_TYPE_SYNC				= 0x0  # Flags: PTP_TWO_STEP = True
PTPv2_TYPE_DELAY_REQ			= 0x1
# IEEE802.1AS is the Audio-Visual Bridging (AVB) profile of the IEEE1588 Precision Time Protocol
PTPv2_TYPE_PATH_DELAY_REQ		= 0x2
PTPv2_TYPE_PATH_DELAY_RESP		= 0x3

PTPv2_TYPE_FOLLOW_UP			= 0x8
PTPv2_TYPE_DELAY_RESP			= 0x9
PTPv2_TYPE_PATH_DELAY_RESP_FOLLOWUP	= 0xA
PTPv2_TYPE_ANNOUNCE			= 0xB  # Flags: PTP_TIMESCALE = True
PTPv2_TYPE_SIGNALLING			= 0xC
PTPv2_TYPE_MGMT				= 0xD

CTRL_TYPE_SYNC				= 0x0
CTRL_TYPE_DELAY_REQ			= 0x1
CTRL_TYPE_FOLLOW_UP			= 0x2
CTRL_TYPE_DELAY_RESP			= 0x3
CTRL_TYPE_MGMT				= 0x4
CTRL_TYPE_OTHER				= 0x5


TYPES_TS_ACTIVATE = {PTPv2_TYPE_SYNC, PTPv2_TYPE_FOLLOW_UP, PTPv2_TYPE_ANNOUNCE,
		PTPv2_TYPE_PATH_DELAY_RESP}
TYPES_REQ_PORT_ACTIVATE = {PTPv2_TYPE_PATH_DELAY_RESP, PTPv2_TYPE_PATH_DELAY_RESP_FOLLOWUP}


class PTPv2(packetracer.Packet):
	__hdr__ = (
		("transport_id", "B", PTPv2_TYPE_DELAY_REQ),
		("version", "B", 2),
		("msglen", "H", 0),
		("subdomains", "B", 0),
		("rsv", "B", 0),
		("flags", "H", 0),
		("corr", "Q", 0),
		("rsv2", "I", 0),
		("clockid", "Q", 0),
		("srcport", "H", 0),
		("seqid", "H", 0),
		("ctrl", "B", 0),
		("log", "B", 0),
		("tssec_bts", "6s", None),
		("tsnano", "I", None),
		("reqclockid", "Q", None),
		("reqportid", "H", None)
	)

	def __get_transport(self):
		return (self.transport_id & 0xF0) >> 4

	def __set_transport(self, value):
		self.transport_id = ((value & 0xF) << 4) | (self.transport_id & 0xF)

	transport = property(__get_transport, __set_transport)

	def __get_id(self):
		return self.transport_id & 0xF

	def __set_id(self, value):
		self.transport_id = (self.transport_id & 0xF0) | (value & 0xF)

	id = property(__get_id, __set_id)

	def __get_tssec(self):
		return unpack_Q(b"\x00\x00" + self.tssec_bts)[0]

	def __set_tssec(self, value):
		self.tssec_bts = pack_Q(value)[2:]  # (8-2) bytes

	# allows setting tssec as integer in contrast to tssec_bts
	tssec = property(__get_tssec, __set_tssec)

	class Announce(packetracer.Packet):
		__hdr__ = (
			("utcoff", "H", 0),
			("prio1", "H", 0),
			("clockclass", "B", 0),
			("clockaccuracy", "B", 0),
			("clockvariance", "H", 0),
			("prio2", "B", 0),
			("clockid", "Q", 0),
			("stepsremoved", "H", 0),
			("timesource", "B", 0)
		)

	__handler__ = {
		PTPv2_TYPE_ANNOUNCE: Announce
	}

	def _dissect(self, buf):
		header_len = 34
		ptpv2_type = buf[0] & 0xF

		if ptpv2_type in TYPES_TS_ACTIVATE:
			#logger.debug("activating ts fields")
			self.tssec_bts = b"\x00" * 6
			self.tsnano = 0
			header_len += 10

		if ptpv2_type in TYPES_REQ_PORT_ACTIVATE:
			#logger.debug("activating req fields")
			self.reqclockid = 0
			self.reqportid = 0
			header_len += 10
		elif ptpv2_type == PTPv2_TYPE_ANNOUNCE:
			#logger.debug("got announce")
			self._init_handler(ptpv2_type, buf[header_len:])

		if len(buf) < header_len:
			logger.warning("not enough bytes for header: %d < %d", len(buf), header_len)

		return header_len
