"""
This module provides a class Identicon that can be used to generate identicons
from strings.

It provides a (slighltly modified) version of http://git.jingrow.com/evuez/identicons
which has been released under the MIT license, as described in attributions.md.
"""
from base64 import b64encode
from hashlib import md5
from io import BytesIO

from PIL import Image, ImageDraw

GRID_SIZE = 5
BORDER_SIZE = 20
SQUARE_SIZE = 40


class Identicon:
	def __init__(self, str_, background="#fafbfc"):
		"""
		`str_` is the string used to generate the identicon.
		`background` is the background of the identicon.
		"""
		w = h = BORDER_SIZE * 2 + SQUARE_SIZE * GRID_SIZE
		self.image = Image.new("RGB", (w, h), background)
		self.draw = ImageDraw.Draw(self.image)
		self.hash = self.digest(str_)

	def digest(self, str_):
		"""
		Returns a md5 numeric hash
		"""
		return int(md5(str_.encode("utf-8"), usedforsecurity=False).hexdigest(), 16)

	def calculate(self):
		"""
		Creates the identicon.
		First three bytes are used to generate the color,
		remaining bytes are used to create the drawing
		"""
		color = (self.hash & 0xFF, self.hash >> 8 & 0xFF, self.hash >> 16 & 0xFF)
		self.hash >>= 24  # skip first three bytes
		square_x = square_y = 0  # init square position
		for x in range(GRID_SIZE * (GRID_SIZE + 1) // 2):
			if self.hash & 1:
				x = BORDER_SIZE + square_x * SQUARE_SIZE
				y = BORDER_SIZE + square_y * SQUARE_SIZE
				self.draw.rectangle((x, y, x + SQUARE_SIZE, y + SQUARE_SIZE), fill=color, outline=color)
				# following is just for mirroring
				x = BORDER_SIZE + (GRID_SIZE - 1 - square_x) * SQUARE_SIZE
				self.draw.rectangle((x, y, x + SQUARE_SIZE, y + SQUARE_SIZE), fill=color, outline=color)
			self.hash >>= 1  # shift to right
			square_y += 1
			if square_y == GRID_SIZE:  # done with first column
				square_y = 0
				square_x += 1

	def generate(self):
		"""
		Save and show calculated identicon
		"""
		self.calculate()
		with open("identicon.png", "wb") as out:
			self.image.save(out, "PNG")
		self.image.show()

	def base64(self, format="PNG"):
		"""
		Return the identicon's base64

		Created by: liuzheng712
		Bug report: http://git.jingrow.com/liuzheng712/identicons/issues
		"""
		self.calculate()

		self.image.encoderinfo = {}
		self.image.encoderconfig = ()

		buff = BytesIO()
		self.image.save(buff, format=format.upper())

		return f"data:image/png;base64,{b64encode(buff.getvalue()).decode()}"
