import hashlib, subprocess, uuid
from tags import TagSet

def generate_checksum(something):
	checksum = hashlib.md5()

	if type(something) is bytes:
		checksum.update(something)
	elif type(something) is str:
		with open(something, "rb") as f:
			checksum.update(f.read())
	else:
		checksum.update(something.read())

	return checksum.hexdigest()

class Asset:
	def __init__(self, id=None, tags=None, source=None, checksum=None):
		if id:
			self._id = format(id)
		else:
			self._id = format(uuid.uuid4())

		self._tags = TagSet(tags)
		try:
			self._tags.subscribe(self)
		except AttributeError:
			pass

		if source:
			self._source = source
		else:
			self._source = None

		if checksum:
			self._checksum = checksum

		self.subscribers = []

	def __str__(self):
		return format(self.id)[:8] + " " + format(self.tags)

	@property
	def id(self):
		return self._id

	@id.setter
	def id(self, value):
		self._id = value
		self.broadcast("changed")

	@property
	def checksum(self):
		return self._checksum

	@checksum.setter
	def checksum(self, value):
		self._checksum = value
		self.broadcast("changed")

	@property
	def source(self):
		return self._source

	@source.setter
	def source(self, value):
		self._source = value
		self.broadcast("changed")

	@property
	def tags(self):
		return self._tags

	@tags.setter
	def tags(self, value):
		tag_set = TagSet(value)
		self._tags = tag_set
		tag_set.subscribe(self)
		self.broadcast("changed")

	def subscribe(self, obj):
		self.subscribers.append(obj)

	def broadcast(self, message):
		for sub in self.subscribers:
			sub.receive(self, message)

	def receive(self, obj, message):
		# Just defer the message.
		self.broadcast(message)

	def serialize(self):
		return {
			"id": format(self.id),
			"tags": self.tags.serialize(),
			"checksum": self.checksum,
			"path": self.path,
			"source": self.source,
			"label": format(self),
		}

	def replace(self, path=None, raw=None, require_match=False):
		"""Replaces asset's file with new file while maintaining all metadata."""
		if path:
			with open(path, "rb") as new:
				return self.replace(raw=new.read(), require_match=require_match)
		elif raw:
			if require_match:
				checksum = generate_checksum(raw)
				if checksum != self.checksum:
					return False
			with open(self.path, "wb") as f:
				f.write(raw)
		if not require_match:
			self.checksum = generate_checksum(self.path)
		return True

	def open(self):
		try:
			path = self.path
		except AttributeError:
			path = self.temp_path
		return subprocess.Popen(["xdg-open", path], stderr=subprocess.PIPE, bufsize=-1)
