# -*- coding: utf-8 -*-
# plugin.video.alkoflix
# resources/lib/modules/artwork_utils.py

import hashlib
import math
import struct
import zlib

from alko import client
from modules import kodi_utils as ku

PNG_SIGNATURE = b'\x89PNG\r\n\x1a\n'
LOGO_CACHE_DIR = 'special://profile/addon_data/plugin.video.alkoflix/artwork/logos/'


def square_logo(image_url, cache_group='logo'):
	"""Retourne une copie locale carrée et paddée d'un logo PNG distant.

	Les logos TMDb des diffuseurs/providers sont souvent horizontaux. Dans les vues Kodi
	qui imposent une vignette carrée ou arrondie, ils peuvent être étirés ou rognés.
	Cette fonction crée une image PNG transparente plus grande, avec le logo centré,
	afin de préserver les proportions et de limiter le rognage.
	"""
	image_url = str(image_url or '').strip()
	if not image_url or '://' not in image_url:
		return image_url
	if '.png' not in image_url.lower():
		return image_url
	try:
		cache_group = ''.join(c for c in str(cache_group or 'logo') if c.isalnum() or c in ('_', '-')) or 'logo'
		digest = hashlib.sha1(image_url.encode('utf-8')).hexdigest()[:24]
		cache_dir = LOGO_CACHE_DIR + cache_group + '/'
		cache_path = cache_dir + digest + '.png'
		if ku.path_exists(cache_path):
			return cache_path
		try: ku.make_directorys(cache_dir)
		except: pass
		data = client.request(image_url, headers={'Accept': 'image/png,image/*;q=0.8,*/*;q=0.5'}, timeout='20', as_bytes=True)
		if not data or not data.startswith(PNG_SIGNATURE):
			return image_url
		padded = _pad_png_to_square(data)
		if not padded:
			return image_url
		with ku.open_file(cache_path, 'wb') as fh:
			fh.write(padded)
		return cache_path if ku.path_exists(cache_path) else image_url
	except Exception as e:
		try: ku.logger('square logo cache error', '%s | %s' % (image_url, e))
		except: pass
		return image_url


def _pad_png_to_square(data, content_ratio=0.72):
	decoded = _decode_png_rgba(data)
	if not decoded:
		return None
	width, height, pixels = decoded
	if width <= 0 or height <= 0 or not pixels:
		return None
	try:
		content_ratio = float(content_ratio)
	except:
		content_ratio = 0.72
	content_ratio = min(0.95, max(0.50, content_ratio))
	canvas = int(math.ceil(float(max(width, height)) / content_ratio))
	canvas = max(canvas, width, height, 32)
	x_offset = int((canvas - width) / 2)
	y_offset = int((canvas - height) / 2)
	row_len = canvas * 4
	output = bytearray(row_len * canvas)
	src_row_len = width * 4
	for y in range(height):
		src_start = y * src_row_len
		dst_start = (y + y_offset) * row_len + (x_offset * 4)
		output[dst_start:dst_start + src_row_len] = pixels[src_start:src_start + src_row_len]
	return _encode_png_rgba(canvas, canvas, bytes(output))


def _decode_png_rgba(data):
	if not data or not data.startswith(PNG_SIGNATURE):
		return None
	pos = len(PNG_SIGNATURE)
	width = height = bit_depth = color_type = None
	palette = []
	transparency = b''
	idat = bytearray()
	try:
		while pos + 8 <= len(data):
			length = struct.unpack('>I', data[pos:pos + 4])[0]
			chunk_type = data[pos + 4:pos + 8]
			chunk_data = data[pos + 8:pos + 8 + length]
			pos += 12 + length
			if chunk_type == b'IHDR':
				width, height, bit_depth, color_type, compression, filter_method, interlace = struct.unpack('>IIBBBBB', chunk_data)
				if bit_depth != 8 or compression != 0 or filter_method != 0 or interlace != 0:
					return None
			elif chunk_type == b'PLTE':
				palette = [tuple(chunk_data[i:i + 3]) for i in range(0, len(chunk_data), 3)]
			elif chunk_type == b'tRNS':
				transparency = chunk_data
			elif chunk_type == b'IDAT':
				idat.extend(chunk_data)
			elif chunk_type == b'IEND':
				break
		if not width or not height or color_type is None or not idat:
			return None
		channels = {0: 1, 2: 3, 3: 1, 4: 2, 6: 4}.get(color_type)
		if not channels:
			return None
		raw = zlib.decompress(bytes(idat))
		stride = width * channels
		rows = []
		idx = 0
		prev = bytearray(stride)
		for _ in range(height):
			filter_type = raw[idx]
			idx += 1
			scan = bytearray(raw[idx:idx + stride])
			idx += stride
			_unfilter(scan, prev, filter_type, channels)
			rows.append(bytes(scan))
			prev = scan
		rgba = bytearray(width * height * 4)
		dst = 0
		transparent_gray = None
		transparent_rgb = None
		if color_type == 0 and len(transparency) >= 2:
			transparent_gray = struct.unpack('>H', transparency[:2])[0] & 0xff
		elif color_type == 2 and len(transparency) >= 6:
			transparent_rgb = tuple(v & 0xff for v in struct.unpack('>HHH', transparency[:6]))
		palette_alpha = list(transparency) if color_type == 3 and transparency else []
		for row in rows:
			if color_type == 6:
				for x in range(0, len(row), 4):
					rgba[dst:dst + 4] = row[x:x + 4]
					dst += 4
			elif color_type == 2:
				for x in range(0, len(row), 3):
					r, g, b = row[x], row[x + 1], row[x + 2]
					a = 0 if transparent_rgb and (r, g, b) == transparent_rgb else 255
					rgba[dst:dst + 4] = bytes((r, g, b, a))
					dst += 4
			elif color_type == 0:
				for g in row:
					a = 0 if transparent_gray is not None and g == transparent_gray else 255
					rgba[dst:dst + 4] = bytes((g, g, g, a))
					dst += 4
			elif color_type == 4:
				for x in range(0, len(row), 2):
					g, a = row[x], row[x + 1]
					rgba[dst:dst + 4] = bytes((g, g, g, a))
					dst += 4
			elif color_type == 3:
				for index in row:
					try: r, g, b = palette[index]
					except: r, g, b = 0, 0, 0
					a = palette_alpha[index] if index < len(palette_alpha) else 255
					rgba[dst:dst + 4] = bytes((r, g, b, a))
					dst += 4
		return width, height, bytes(rgba)
	except Exception:
		return None


def _unfilter(scan, prev, filter_type, bpp):
	if filter_type == 0:
		return
	for i in range(len(scan)):
		left = scan[i - bpp] if i >= bpp else 0
		up = prev[i] if prev else 0
		up_left = prev[i - bpp] if prev and i >= bpp else 0
		if filter_type == 1:
			scan[i] = (scan[i] + left) & 0xff
		elif filter_type == 2:
			scan[i] = (scan[i] + up) & 0xff
		elif filter_type == 3:
			scan[i] = (scan[i] + ((left + up) >> 1)) & 0xff
		elif filter_type == 4:
			scan[i] = (scan[i] + _paeth(left, up, up_left)) & 0xff


def _paeth(a, b, c):
	p = a + b - c
	pa = abs(p - a)
	pb = abs(p - b)
	pc = abs(p - c)
	if pa <= pb and pa <= pc:
		return a
	if pb <= pc:
		return b
	return c


def _png_chunk(chunk_type, chunk_data):
	return struct.pack('>I', len(chunk_data)) + chunk_type + chunk_data + struct.pack('>I', zlib.crc32(chunk_type + chunk_data) & 0xffffffff)


def _encode_png_rgba(width, height, pixels):
	if len(pixels) != width * height * 4:
		return None
	ihdr = struct.pack('>IIBBBBB', width, height, 8, 6, 0, 0, 0)
	row_len = width * 4
	raw = bytearray()
	for y in range(height):
		raw.append(0)
		start = y * row_len
		raw.extend(pixels[start:start + row_len])
	return PNG_SIGNATURE + _png_chunk(b'IHDR', ihdr) + _png_chunk(b'IDAT', zlib.compress(bytes(raw), 6)) + _png_chunk(b'IEND', b'')
