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

# Copyright (C) 2003-2006 by Igor E. Novikov
# Copyright (C) 1997, 1998, 1999, 2001 by Bernhard Herzog
#
# This library is covered by GNU Library General Public License.
# For more info see COPYRIGHTS file in sK1 root directory.A

from app.events.warn import pdebug

from app import Rect, EmptyRect, IntersectRects, Document, GraphicsDevice,\
		SketchInternalError, QueueingPublisher, StandardColors

from app.conf.const import STATE, VIEW, DOCUMENT, LAYOUT, REDRAW
from app.conf.const import LAYER, LAYER_STATE, LAYER_ORDER, LAYER_COLOR
from app import config

from tkext import PyWidget
from viewport import Viewport



class SketchView(PyWidget, Viewport, QueueingPublisher):

	document = None

	def __init__(self, master=None, toplevel = None, document = None,
					show_visible = 0, show_printable = 1,
					resolution = None, **kw):
		apply(PyWidget.__init__, (self, master), kw)
		Viewport.__init__(self, resolution)
		QueueingPublisher.__init__(self)
		self.toplevel = toplevel

		self.move_window_count = 0
		self.show_page_outline = 1
		self.show_visible = show_visible
		self.show_printable = show_printable
		self.gcs_initialized = 0
		self.gc = GraphicsDevice()
		

		self.init_transactions()
		if document is not None:
			self.SetDocument(document)
		else:
			self.SetDocument(Document(create_layer = 1))
			

	def destroy(self):
		self.unsubscribe_doc()
		PyWidget.destroy(self)
		QueueingPublisher.Destroy(self)


	def MapMethod(self):
		# when being mapped the first time, initialise the gcs. this cannot be
		# done earlier, because the hitgc creates a pixmap which currently
		# only works after the window (id) has been created. In Xt this can be
		# done in the Realize widget method (after calling the superclass'
		# method), but Tk doesn't seem to offer any similar thing.
		if not self.gcs_initialized:
			self.init_gcs()
			self.issue_state()

	def DestroyMethod(self):
		# make sure that gc is deleted. gc may have a shared memory ximage
		# which is not freed if the gc is not destroyed leaving unused shared
		# memory segments in the system even after the process has finished.
		self.gc = None
		PyWidget.DestroyMethod(self)

	def init_gcs(self):
		self.gc.init_gc(self.tkwin, graphics_exposures = 1)
		self.gc.draw_visible = self.show_visible
		self.gc.draw_printable = self.show_printable
		self.gc.allow_outline = 0
		self.gcs_initialized = 1
		self.default_view()
		self.set_gc_transforms()

	def default_view(self):
		self.FitPageToWindow()


	def set_gc_transforms(self):
		self.gc.SetViewportTransform(self.scale, self.doc_to_win,
										self.win_to_doc)

	#
	#	Channels
	#

	def issue_state(self):
		self.queue_message(STATE)

	def issue_view(self):
		self.queue_message(VIEW)

	def issue_document(self):
		self.doc_changed = 1

	def queue_message(self, Publisher):
		if self.transaction:
			QueueingPublisher.queue_message(self, Publisher)
		else:
			self.issue(Publisher)

	def init_transactions(self):
		self.sb_update_pending = 0
		self.doc_changed = 0
		self.transaction = 0

	def begin_transaction(self):
		self.transaction = self.transaction + 1

	def end_transaction(self):
		self.transaction = self.transaction - 1
		if self.transaction == 0:
			if self.doc_changed:
				self.issue(DOCUMENT, self.document)
			self.sb_update_pending = 0
			self.doc_changed = 0
			self.flush_message_queue()
		elif self.transaction < 0:
			raise SketchInternalError('transaction count < 0')

	#
	#	receivers
	#

	def redraw_doc(self, all, rects = None):
		if all:
			self.clear_window()
		else:
			map(self.clear_area_doc, rects)

	def layout_changed(self):
		self.SetPageSize(self.document.Layout().Size())
		self.set_gc_transforms()
		self.update_scrollbars()
		self.update_rulers()
		if self.show_page_outline:
			self.clear_window()

	def layer_changed(self, *args):
		if args:
			redraw = EmptyRect
			if args[0] == LAYER_STATE:
				layer, visible_changed, printable_changed, outlined_changed \
						= args[1]
				rect = layer.bounding_rect
				if rect is not EmptyRect:
					if self.show_printable and printable_changed:
						redraw = rect
					if self.show_visible:
						if visible_changed:
							redraw = rect
						if outlined_changed and layer.Visible():
							redraw = rect
			elif args[0] == LAYER_ORDER:
				layer = args[1]
				if (self.show_printable and layer.Printable()
					or self.show_visible and layer.Visible()):
					redraw = layer.bounding_rect
				if len(args) > 2:
					other = args[2]
					if (self.show_printable and other.Printable()
						or self.show_visible and other.Visible()):
						redraw = IntersectRects(redraw, other.bounding_rect)
					else:
						redraw = EmptyRect
			elif args[0] == LAYER_COLOR:
				layer = args[1]
				rect = layer.bounding_rect
				if self.show_visible and rect is not EmptyRect \
					and layer.Visible():
					redraw = rect
			self.clear_area_doc(redraw)

	#
	#	Widget Methods (Redraw, ... )
	#

	time_redraw = 0
	def RedrawMethod(self, region = None):
		# draw the document
		if __debug__:
			if self.time_redraw:
				import time
				start = time.clock()
		if self.move_window_count >= 2:
			self.clear_window(update = 0)
		self.move_window_count = 0

		region = self.do_clear(region)

		# draw document
		self.gc.InitClip()
		self.gc.ResetFontCache()
		if region:
			self.gc.PushClip()
			self.gc.ClipRegion(region)

		tkwin = self.tkwin
		if region:
			x, y, w, h = region.ClipBox()
			if x < 0:
				w = w - x; x = 0
			if y < 0:
				h = h - y; y = 0
			if w > tkwin.width:
				w = tkwin.width
			if h > tkwin.height:
				h = tkwin.height
		else:
			x = y = 0
			w = tkwin.width
			h = tkwin.height
		p1 = self.WinToDoc(x - 1, y - 1)
		p2 = self.WinToDoc(x + w + 1, y + h + 1)
		rect = Rect(p1, p2)
		
		if not config.preferences.cairo_enabled :
			self.gc.SetFillColor(StandardColors.white)
			self.gc.gc.FillRectangle(x, y, w, h) # XXX ugly to access gc.gc

		#	draw paper
		
		#Drawing tracking
#		print 'start draw'
#		import time
#		_t = time.clock()
		
		self.gc.StartDrawing()
		if self.show_page_outline:
			w, h = self.document.PageSize()
			self.gc.DrawPageOutline(w, h)


		self.document.Draw(self.gc, rect)
		
#		_tm = time.clock() - _t
#		print 'doc draw:', _tm
#		_t = time.clock()
		
		self.gc.FinalizeDrawing()
		
#		_t = time.clock() - _t
#		print 'finalize:', _t
		
		if region:
			self.gc.PopClip()

		if __debug__:
			if self.time_redraw:
				pdebug('timing', 'redraw', time.clock() - start)

		return region

	def ResizedMethod(self, width, height):
		Viewport.ResizedMethod(self, width, height)
		self.gc.WindowResized(width, height)


	#
	#	Viewport- and related methods
	#
	#	extend some Viewport methods to issue VIEW whenever
	#	the displayed area changes
	#

	def ForceRedraw(self):
		# Force a redraw of the whole window
		self.clear_window()
		if __debug__:
			#self.time_redraw = 1
			pass

#     def SetScrollbars(self, hbar, vbar):
# 	Viewport.SetScrollbars(self, hbar, vbar)
# 	hbar.configure(jump = 1)
# 	vbar.configure(jump = 1)

	def set_origin(self, xorg, yorg, move_contents = 1):
		self.begin_transaction()
		try:
			Viewport.set_origin(self, xorg, yorg,
								move_contents = move_contents)
			self.set_gc_transforms()
			self.issue_view()
		finally:
			self.end_transaction()

	def move_window_contents(self, offx, offy):
		# implement the method needed by Viewport.set_origin
		w = self.tkwin
		width = w.width
		height = w.height
		if config.preferences.cairo_enabled :
			self.clear_window()
		else:
			if abs(offx) < width and abs(offy) < height:
				w.CopyArea(w, self.gc.gc, offx, offy, width, height, 0, 0)
				self.move_window_count = self.move_window_count + 1
			else:
				self.clear_window()

	def SetScale(self, scale, do_center = 1):
		# Set current scale
		self.begin_transaction()
		try:
			Viewport.SetScale(self, scale, do_center = do_center)
			self.set_gc_transforms()
		finally:
			self.end_transaction()

	def zoom_fit_rect(self, rect, save_viewport = 0):
		if save_viewport:
			self.save_viewport()
		Viewport.zoom_fit_rect(self, rect)


	#
	#	other view related methods
	#
	def FitToWindow(self, selected_only = 0, save_viewport = 1):
		self.begin_transaction()
		try:
			if selected_only:
				rect = self.document.SelectionBoundingRect()
			else:
				rect = self.document.BoundingRect()
			if rect:
				self.zoom_fit_rect(rect, save_viewport = save_viewport)
		finally:
			self.end_transaction()

	def FitPageToWindow(self, save_viewport = 1):
		self.begin_transaction()
		try:
			w, h = self.document.PageSize()
			self.zoom_fit_rect(Rect(0, 0, w, h).grown(10),
								save_viewport = save_viewport)
		finally:
			self.end_transaction()


	#
	#	Outline Mode
	#
	#	Although not directly related to the viewport methods (the outline
	#	mode doesn't change the displayed area) the outline mode changes the
	#	way the drawing is displayed and thus issues VIEW.

	def SetOutlineMode(self, on = 1):
		self.begin_transaction()
		try:
			if on:
				if self.gc.IsOutlineActive():
					return
				else:
					self.gc.StartOutlineMode()
					self.hitgc.StartOutlineMode()
			else:
				if self.gc.IsOutlineActive():
					self.gc.EndOutlineMode()
					self.hitgc.EndOutlineMode()
				else:
					return
			self.issue_view()
			self.clear_window()
		finally:
			self.end_transaction()

	def ToggleOutlineMode(self):
		self.SetOutlineMode(not self.IsOutlineMode())

	def IsOutlineMode(self):
		return self.gc and self.gc.IsOutlineActive()

	#
	#	Show page outline on/off
	#

	def SetPageOutlineMode(self, on = 1):
		self.begin_transaction()
		try:
			self.show_page_outline = on
			self.issue_view()
			self.clear_window()
		finally:
			self.end_transaction()

	def TogglePageOutlineMode(self):
		self.SetPageOutlineMode(not self.IsPageOutlineMode())

	def IsPageOutlineMode(self):
		return self.show_page_outline

	#
	#
	#

	def unsubscribe_doc(self):
		if self.document is not None:
			self.document.Unsubscribe(REDRAW, self.redraw_doc)
			self.document.Unsubscribe(LAYOUT, self.layout_changed)
			self.document.Unsubscribe(LAYER, self.layer_changed)

	def subscribe_doc(self):
		self.document.Subscribe(REDRAW, self.redraw_doc)
		self.document.Subscribe(LAYOUT, self.layout_changed)
		self.document.Subscribe(LAYER, self.layer_changed)

	def SetDocument(self, doc):
		self.begin_transaction()
		try:
			self.unsubscribe_doc()
			self.document = doc
			self.subscribe_doc()
			self.clear_window()
			self.SetPageSize(self.document.Layout().Size())
			self.FitPageToWindow(save_viewport = 0)
			self.issue_document()
			self.issue_state()
			self.issue_view()
		finally:
			self.end_transaction()

