#-*- coding:utf-8 -*-

"""
This file is part of OpenSesame.

OpenSesame is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

OpenSesame is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with OpenSesame.  If not, see <http://www.gnu.org/licenses/>.
"""

from libopensesame.py3compat import *
import os
import pygame
import platform
from collections import OrderedDict
from openexp.backend import configurable
from openexp._canvas.canvas import Canvas
from openexp._coordinates.legacy import Legacy as LegacyCoordinates
from libopensesame.oslogging import oslogger

# PyGame 1.9.2 suffers from character encoding issues. To get around those, we
# pass file objects directly to PyGame, instead of paths. These file objects
# need to remain open in the case of fonts, otherwise the associated font
# object won't work anymore. So here we keep a list of all open file objects,
# which are closed when the display is closed. In addition, we keep track of
# font objects so that we don't repeatedly initialize the same font.
#
# A bug report regarding character encoding is here:
# - https://bitbucket.org/pygame/pygame/issues/196/\
#   font-file-name-containing-unicode-error
fileobjects = []
fonts = {}


class Legacy(Canvas, LegacyCoordinates):

	"""
	desc:
		This is a canvas backend built on top of PyGame.
		For function specifications and docstrings, see
		`openexp._canvas.canvas`.
	"""

	# The settings variable is used by the GUI to provide a list of back-end
	# settings
	settings = {
		u"pygame_hwsurface": {
			u"name": u"Hardware surface",
			u"description": u"Create a hardware surface",
			u"default": u"yes"
		},
		u"pygame_doublebuf": {
			u"name": u"Double buffering",
			u"description": u"Use double buffering",
			u"default": u"yes"
		},
		u"pygame_window_frame": {
			u"name": u"Draw window frame",
			u"description": u"Draw a frame in window mode",
			u"default": u"yes",
		},
		u"pygame_window_pos": {
			u"name": u"Window position",
			u"description": u"Window position in window mode (format: 'x,y' or 'auto')",
			u"default": u"auto",
		}
	}

	def __init__(self, experiment, auto_prepare=True, **style_args):

		Canvas.__init__(
			self, experiment,
			auto_prepare=auto_prepare,
			**style_args
		)
		LegacyCoordinates.__init__(self)
		self.antialias = True
		self.surface = self.experiment.surface.copy()
		self.clear()
		if platform.system() == u'Darwin':
			self.show = self._show_macos

	def show(self):

		t0 = pygame.time.get_ticks()
		self.experiment.surface.blit(self.surface, (0, 0))
		self.experiment.last_shown_canvas = self.surface
		pygame.display.flip()
		t1 = pygame.time.get_ticks()
		if t1 - t0 > self.MAX_SHOW_DT:
			oslogger.warning('Canvas.show() took {0} ms'.format(t1 - t0))
		return t1

	def _show_macos(self):

		"""
		visible: False

		desc:
			On Mac OS, the display is sometimes not refreshed unless there is
			some interaction with the event loop. Therefor we implement this
			hack which is only used on Mac OS.
		"""

		self.experiment.surface.blit(self.surface, (0, 0))
		self.experiment.last_shown_canvas = self.surface
		pygame.display.flip()
		pygame.event.pump()
		return pygame.time.get_ticks()

	def prepare(self):

		"""
		desc:
			Finishes pending canvas operations (if any), so that a subsequent
			call to [canvas.show] is extra fast. It's only necessary to call
			this function if you have disabled `auto_prepare` in
			[canvas.__init__].
		"""

		self.surface.fill(self.background_color.backend_color)
		Canvas.prepare(self)

	def lower_to_bottom(self, element):

		Canvas.lower_to_bottom(self, element)
		self.redraw()

	def raise_to_top(self, element):

		Canvas.raise_to_top(self, element)
		self.redraw()

	def redraw(self):

		if not self.auto_prepare:
			return
		self.prepare()

	def set_config(self, **cfg):

		Canvas.set_config(self, **cfg)
		if hasattr(self, u'surface'):
			self.redraw()

	def copy(self, canvas):

		self.surface = canvas.surface.copy()
		Canvas.copy(self, canvas)

	@configurable
	def clear(self):

		self.surface.fill(self.background_color.backend_color)
		self._elements = OrderedDict()

	def _text_size(self, text):

		return self._font.size(text)

	@staticmethod
	def init_display(experiment):

		# Intialize PyGame and set the Window icon
		pygame.init()
		surf = pygame.Surface((32, 32))
		surf.fill((255, 255, 255))
		pygame.draw.circle(surf, (0, 0, 255), (16, 16), 10, 4)
		pygame.display.set_icon(surf)
		# Determine the video mode
		mode = 0
		if experiment.var.get(
			u"pygame_hwsurface",
			u"yes",
			[u"yes", u"no"]
		) == u"yes":
			mode = mode | pygame.HWSURFACE
			oslogger.info(u'enabling hardware surface')
		else:
			oslogger.info(u'not enabling hardware surface')
		if experiment.var.get(
			u"pygame_doublebuf",
			u"yes",
			[u"yes", u"no"]
		) == u"yes":
			mode = mode | pygame.DOUBLEBUF
			oslogger.info(u'enabling double buffering')
		else:
			oslogger.info(u'not enabling double buffering')
		if pygame.display.mode_ok(experiment.resolution(), mode):
			oslogger.info(u'video mode ok')
		else:
			oslogger.warning(u'warning: video mode not ok')
		if experiment.var.fullscreen == u'yes':
			mode = mode | pygame.FULLSCREEN
		if experiment.var.get(
			u'pygame_window_frame',
			u'yes',
			[u'yes', u'no']
		) == u'no':
			mode = mode | pygame.NOFRAME
		if experiment.var.get(u'pygame_window_pos', u'auto') != u'auto':
			os.environ[u'SDL_VIDEO_WINDOW_POS'] = experiment.var.get(
				u'pygame_window_pos'
			)
		# Create the window and the surface
		experiment.window = pygame.display.set_mode(
			experiment.resolution(),
			mode
		)
		pygame.display.set_caption(u'OpenSesame (legacy backend)')
		pygame.mouse.set_visible(False)
		experiment.surface = pygame.display.get_surface()
		# Disable mouse-motion events because they fill up the cue
		pygame.event.set_blocked(pygame.MOUSEMOTION)

	@staticmethod
	def close_display(experiment):

		while fileobjects:
			fileobjects.pop().close()
		while fonts:
			fonts.pop(list(fonts.keys())[0], None)
		pygame.display.quit()


# Non PEP-8 alias for backwards compatibility
legacy = Legacy
