#!/usr/bin/env python
#
# Copyright (c) 2013
# Contributors to the Freedoom project.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#  * Neither the name of the freedoom project nor the names of its
#    contributors may be used to endorse or promote products derived from
#    this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# ----------------------------------------------------------------------
#
# Script to generate menu and intermission screen 'text' graphics for
# Freedoom, by compositing individual font character graphics together.
#

from glob import glob
from sys import stdout
import subprocess
import re

from config import *

# ImageMagick commands used by this script:
CONVERT_COMMAND = 'convert'
IDENTIFY_COMMAND = 'identify'

# ImageMagick -colorize parameters for colorizing text:
COLOR_BLUE = (100, 100, 0)
COLOR_RED = (0, 100, 100)
COLOR_WHITE = (0, 0, 0)

# Background color for output files.
BACKGROUND_COLOR = '#00ffff'

# Height of font in pixels.
FONT_HEIGHT = 16

# Width of a space character in pixels.
SPACE_WIDTH = 10

# Output from 'identify' looks like this:
#  fontchars/font033.gif GIF 9x16 9x16+0+0 8-bit sRGB 32c 194B 0.000u 0:00.000
IDENTIFY_OUTPUT_RE = re.compile(r'(\S+)\s(\S+)\s(\d+)x(\d+)\s')

def get_image_dimensions(filename):
	proc = subprocess.Popen([IDENTIFY_COMMAND, filename],
	                        stdout=subprocess.PIPE)
	proc.wait()

	line = proc.stdout.readline().decode('utf-8')
	match = IDENTIFY_OUTPUT_RE.match(line)
	assert match is not None
	return (int(match.group(3)), int(match.group(4)))

def invoke_command(command):
	"""Invoke a command, printing the command to stdout.

	Args:
	  command: Command and arguments as a list.
	"""
	for arg in command:
		if arg.startswith('-'):
			stdout.write("\\\n    ")

		if ' ' in arg or '#' in arg:
			stdout.write(repr(arg))
		else:
			stdout.write(arg)

		stdout.write(' ')

	stdout.write('\n')
	return subprocess.call(command)

class Font(object):
	def __init__(self, fontdir, kerning_table={}):
		self.fontdir = fontdir
		self.kerning_table = self.compile_kerning_table(kerning_table)
		self.get_font_widths()

	def compile_kerning_table(self, kerning_table):
		"""Given a dictionary of kerning patterns, compile Regexps."""

		result = {}
		for pattern, adjust in kerning_table.items():
			result[re.compile(pattern)] = adjust
		return result

	def get_font_widths(self):
		charfiles = glob('%s/font*.gif' % self.fontdir)
		self.char_widths = {}
		for c in range(128):
			filename = self.char_filename(chr(c))
			if filename not in charfiles:
				continue
			w, _ = get_image_dimensions(filename)
			self.char_widths[chr(c)] = w

	def __contains__(self, c):
		return c in self.char_widths

	def char_width(self, c):
		return self.char_widths[c]

	def char_filename(self, c):
		return '%s/font%03d.gif' % (self.fontdir, ord(c))

	def kerning_adjust(self, char_1, char_2):
		"""Get kerning adjustment for pair of characters.

		Zero means no adjustment. A negative value adjusts to the
		left and a positive value adjusts to the right.
		"""
		for pattern, adjust in self.kerning_table.items():
			if pattern.match(char_1 + char_2):
				return adjust
		else:
			return 0

	def iterate_char_positions(self, text):
		"""Iterate over characters in string, yielding character with
		   position it should be placed at in the output file.
		"""
		x = 0
		last_c = ' '
		for c in text:
			if c == ' ':
				x += SPACE_WIDTH

			if c in self:
				x += self.kerning_adjust(last_c, c)

				yield c, x

				# Characters overlap by one pixel.
				x += self.char_width(c) - 1

			last_c = c

		# We need to add back the missing pixel from the right side
		# of the last char.
		x += 1
		yield None, x

	def text_width(self, text):
		"""Given a string of text, get text width in pixels."""
		for c, x in self.iterate_char_positions(text):
			if c is None:
				return x

	def _make_command_line(self, text, color):
		"""Command line construction helper, used in render functions"""
		width = self.text_width(text)

		command_line = [
			CONVERT_COMMAND,
			'-size', '%ix%i' % (width, FONT_HEIGHT),
			'xc:none',
		]

		for c, x in self.iterate_char_positions(text):
			if c is None:
				break

			filename = self.char_filename(c)
			command_line.extend([
				'-draw',
				'image over %i,0 0,0 %s' % (x, filename)
			])

		command_line.extend([
			'-colorize', '%i,%i,%i,0' % color,
		])

		return command_line

	def render_transparent_text(self, text, output_filename, color):
		"""Render the given text to the given output file.

		The rendered text is generated with a transparent background.
		"""

		command_line = self._make_command_line(text, color)

		command_line.append(output_filename)

		invoke_command(command_line)

	def render_text(self, text, output_filename,
	                color=COLOR_WHITE, bgcolor=BACKGROUND_COLOR):
		"""Render text to a file with the given background color."""

		command_line = self._make_command_line(text, color)

		command_line.extend([
			'-background', bgcolor, '-flatten',
			output_filename])

		invoke_command(command_line)

def generate_graphics(graphics, color=COLOR_WHITE):
	for name, text in sorted(graphics.items()):
		print("# %s.gif: '%s'" % (name, text))
		font.render_text(text, '%s.gif' % name, color=color)

font = Font('fontchars', kerning_table=FONT_KERNING_RULES)

generate_graphics(red_graphics, COLOR_RED)
generate_graphics(blue_graphics, COLOR_BLUE)
generate_graphics(white_graphics, COLOR_WHITE)

