"""
Hypertext Transfer Protocol.
"""
import re
import logging

from packetracer import packetracer, triggerlist

logger = logging.getLogger("packetracer")


class HTTPHeader(triggerlist.TriggerList):
	def _pack(self, tuple_entry):
		# logger.debug("packing HTTP-header")
		# no header = no CRNL
		if len(self) == 0:
			# logger.debug("empty buf 2")
			return b""
		#return b"\r\n".join([b": ".join(keyval) for keyval in self]) + b"\r\n\r\n"
		#logger.debug("adding: %r" % (tuple_entry[0] +b": "+ tuple_entry[1] + b"\r\n"))
		return tuple_entry[0] + b": " + tuple_entry[1] + b"\r\n"

# [Method] [Path] HTTP...\r\n
# key: value\r\n
# \r\n
# [body]
PROG_STARTLINE		= re.compile(rb"[\w\./]{3,10} +[\w\./]{1,400} +[\w\./]{1,20}.+")
PROG_SPLIT_HEADBODY	= re.compile(b"\r\n\r\n")
split_headbody		= PROG_SPLIT_HEADBODY.split
PROG_SPLIT_HEADER	= re.compile(b"\r\n")
split_header		= PROG_SPLIT_HEADER.split
PROG_SPLIT_KEYVAL	= re.compile(b": ")
split_keyval		= PROG_SPLIT_KEYVAL.split


class HTTP(packetracer.Packet):
	__hdr__ = (
		# content: b"startline"
		("startline", None, None),
		# content: [("name", "value"), ...]
		("hdr", None, HTTPHeader),
		("sep", "2s", b"\r\n")
	)

	def _dissect(self, buf):
		# requestline: [method] [uri] [version] eg GET / HTTP/1.1
		# responseline: [version] [status] [reason] eg HTTP/1.1 200 OK
		#logger.debug("Full HTTP: %s", buf)
		# Request/responseline is mendatory to parse header
		if not PROG_STARTLINE.match(buf):
			self.sep = None
			return 0

		try:
			bts_header, bts_body = split_headbody(buf, maxsplit=1)
			#logger.debug("Header: %s\nBody: %s", bts_header, bts_body)
		except ValueError:
			#logger.debug("no startline/header present")
			# deactivate separator
			self.sep = None
			# assume this is part of a bigger (splittet) HTTP-message: no header/only body
			return 0

		try:
			startline, bts_header = split_header(bts_header, maxsplit=1)
		except ValueError:
			# logger.debug("just startline: %r, hdr length=%d" % (bts_header, len(bts_header) + 4))
			# bts_header was something like "HTTP/1.1 123 status" (\r\n\r\n previously removed)
			self.startline = bts_header + b"\r\n"
			#self._init_triggerlist("hdr", b"", lambda _: [])
			return len(bts_header) + 4  # startline + 2 (CR NL) + 0 (header) + 2 (sep: CR NL) + 0 (body)

		self.startline = startline + b"\r\n"
		# bts_header = hdr1\r\nhdr2 -> hdr1\r\nhdr2\r\n
		self._init_triggerlist("hdr", bts_header + b"\r\n", self.__parse_header)

		#logger.debug("startline: %s" % self.startline)
		#logger.debug("hdr: %s" % self.hdr)
		#logger.debug("bts_header: %s" % (bts_header+b"\r\n"))
		#logger.debug("sep: %s" % self.sep)

		# logger.debug(len(startline+b"\r\n") + len(bts_header+b"\r\n\r\n"))
		# logger.debug("lengths head/body: %d %d" % (len(buf), len(bts_body)))
		# logger.debug(buf[:len(buf) - len(bts_body)])
		# HEADER + "\r\n" + BODY -> newline is part of the header
		return len(buf) - len(bts_body)

	@staticmethod
	def __parse_header(buf):
		#logger.debug("parsing header: %s", buf)
		header = []
		lines = split_header(buf)

		for line in lines:
			#logger.debug("checking line: %s", line)
			if len(line) == 0:
				break
			try:
				key, val = split_keyval(line, 1)
				header.append((key, val))
			except:
				# Not a "key: value" line
				logger.warning("Invalid HTTP line: %s", line)
				header.append(line)

		return header
