# main_window.py
#
# Copyright (C) 2011 Tim Marston <edam@waxworlds.org>
#
# This file is part of prep-images (hereafter referred to as "this program").
# See http://www.waxworlds.org/edam/software/gtk/prep-images for more
# information.
#
# This program 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.
#
# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.


import sys
import os
import gobject
import pygtk
pygtk.require( '2.0' );
import gtk
from subprocess import call
from .preferences import *
from .prefs_dialog import *


class MainWindow:

	DRAWING_AREA_INIT_WIDTH = 550
	DRAWING_AREA_INIT_HEIGHT = 500
	IMAGEMAGICK_CONVERT_BIN = '/usr/bin/convert'

	def __init__( self, filename ):
		self.filename = filename

		# load preferences
		self.prefs = Preferences()

		# load main image and reset copyright image state to unloaded
		self.load_main_image()
		self.copyright_pic = None

		# init pos
		self.pos = "bottom-left";

		# work out the initial size of the drawing area so that the greatest of
		# the two dimensions is 450px
		scale = min( \
			float( self.DRAWING_AREA_INIT_WIDTH ) / self.pic.get_width(), \
			float( self.DRAWING_AREA_INIT_HEIGHT ) / self.pic.get_height() )
		init_width = int( round( scale * self.pic.get_width() ) )
		init_height = int( round( scale * self.pic.get_height() ) )

		# create window
		self.window = gtk.Window( gtk.WINDOW_TOPLEVEL )
		self.window.connect( "delete_event", self.delete_event )
		self.window.connect( "destroy", self.destroy )
		self.window.connect( "key-press-event", self.key_press_event )
		self.window.set_title( "Add Copyright to Images" );
		self.window.set_border_width( 10 )
		self.window.set_position( gtk.WIN_POS_CENTER )
		vbox = gtk.VBox( False, 10 )
		self.window.add( vbox )

		# create drawing area
		self.drawing_area = gtk.DrawingArea()
		self.drawing_area.set_size_request( init_width, init_height )
		self.drawing_area.add_events( gtk.gdk.POINTER_MOTION_MASK | \
			gtk.gdk.POINTER_MOTION_HINT_MASK | \
			gtk.gdk.BUTTON_PRESS_MASK )
		self.drawing_area.connect( "configure_event", \
			self.drawing_area_configure_event )
		self.drawing_area.connect( "expose_event", \
			self.drawing_area_expose_event )
		self.drawing_area.connect( "motion_notify_event", \
			self.motion_notify_event )
		self.drawing_area.connect( "button_press_event", \
			self.drawing_area_button_press_event )
		vbox.pack_start( self.drawing_area, True, True, 0 )

		# create buttons
		hbox = gtk.HBox( False, 10 )
		vbox.pack_end( hbox, False, False, 0 )
		left_button_box = gtk.HButtonBox()
		left_button_box.set_layout( gtk.BUTTONBOX_START )
		hbox.pack_start( left_button_box, True, True, 0 )
		button_box = gtk.HButtonBox()
		button_box.set_layout( gtk.BUTTONBOX_END )
		button_box.set_spacing( 10 )
		hbox.pack_end( button_box, True, True, 0 )
		prefs_button = gtk.Button( stock = gtk.STOCK_PREFERENCES )
		prefs_button.connect( "clicked", self.prefs_button_pressed )
		left_button_box.pack_start( prefs_button, True, True, 0 )
		cancel_button = gtk.Button( stock = gtk.STOCK_CANCEL )
		cancel_button.connect_object( "clicked", gtk.Widget.destroy, \
			self.window )
		button_box.pack_end( cancel_button, True, True, 0 )
		self.ok_button = gtk.Button( stock = gtk.STOCK_APPLY )
		self.ok_button.connect( "clicked", self.ok_button_pressed )
		button_box.pack_end( self.ok_button, True, True, 0 )
		self.ok_button.set_flags( gtk.CAN_DEFAULT )
		self.ok_button.grab_default()
		self.ok_button.grab_focus()

		# show it all
		self.window.show_all()

		# load copyright image
		self.load_copyright_image()

	def load_main_image( self ):

		# load image file
		try:
			self.pic = gtk.gdk.pixbuf_new_from_file( self.filename )
		except gobject.GError as e:
			dialog = gtk.MessageDialog( None, gtk.DIALOG_MODAL, \
				gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, e.message )
			dialog.run()
			dialog.destroy()
			exit( 1 )

		# scale it
		if self.prefs.resize:
			scale = min( \
				float( self.prefs.resize_width ) / self.pic.get_width(), \
				float( self.prefs.resize_height ) / self.pic.get_height() )
			self.pic = self.pic.scale_simple( \
				int( round( scale * self.pic.get_width() ) ), \
				int( round( scale * self.pic.get_height() ) ), \
				gtk.gdk.INTERP_BILINEAR )

	def load_copyright_image( self ):

		# reset state
		self.copyright_pic = None
		self.ok_button.set_sensitive( False )

		# check if we've got a copyright filename set
		if self.prefs.copyright_filename == '':
			dialog = gtk.MessageDialog( self.window, gtk.DIALOG_MODAL, \
				gtk.MESSAGE_INFO, gtk.BUTTONS_OK, \
				"No copyright image has been set. " + \
				"Please set one in the Preferences." )
			dialog.format_secondary_text( "While this issue persists, you " + \
				"can not proceed with this operation" )
			dialog.run()
			dialog.destroy()
			return

		# load copyright overlay image file
		try:
			self.copyright_pic = gtk.gdk.pixbuf_new_from_file( \
				os.path.expanduser( self.prefs.copyright_filename ) )
		except gobject.GError as e:
			dialog = gtk.MessageDialog( None, gtk.DIALOG_MODAL, \
				gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, \
				"Unable to load copyright overlay image: " + \
				e.message )
			dialog.format_secondary_text( "While this issue persists, you " + \
				"can not proceed with this operation" )
			dialog.run()
			dialog.destroy()
			return

		# check the copyright overlay pic is smaller than the main pic
		if self.copyright_pic.get_width() > self.pic.get_width() or \
				self.copyright_pic.get_height() > self.pic.get_height():
			dialog = gtk.MessageDialog( None, gtk.DIALOG_MODAL, \
				gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, \
				"The copyright overlay image is larger than the photo!" )
			dialog.format_secondary_text( "While this issue persists, you " + \
					"can not proceed with this operation" )
			dialog.run()
			dialog.destroy()
			self.copyright_pic = None
			return

		# enable the "ok" button and reconfigure the drawing area to generate
		# scaled puixbufs
		self.ok_button.set_sensitive( True )
		self.drawing_area.queue_resize()

	def main( self ):
		gtk.main()

	def ok_button_pressed( self, widget ):

		# calculate output filename
		pos = self.filename.rindex( '.' )
		if self.prefs.export_inplace:
			append = ".tmp"
		else:
			append = self.prefs.export_append
		out_filename = self.filename[ : self.filename.rindex( '.' ) ] + \
			append + self.filename[ self.filename.rindex( '.' ) : ]

		# calculate geometry
		top = 0 if self.pos.find( 'top' ) != -1 else \
			self.pic.get_height() - self.copyright_pic.get_height()
		left = 0 if self.pos.find( 'left' ) != -1 else \
			self.pic.get_width() - self.copyright_pic.get_width()

		infile = self.filename

		# calculate program args
		resize_geometry = str( self.prefs.resize_width ) + "x" + \
			str( self.prefs.resize_height )
		copyright_geometry = str( self.copyright_pic.get_width() ) + \
			"x" + str( self.copyright_pic.get_height() ) + "+" + \
			str( left ) + "+" + str( top )
		args = [ self.IMAGEMAGICK_CONVERT_BIN, \
			os.path.expanduser( self.prefs.copyright_filename ), \
			"-geometry", copyright_geometry, \
			"-compose", "src-over", "-composite", \
			out_filename ]
		if( self.prefs.resize ):
			args[ 1 : 1 ] = [ "(", "-resize", resize_geometry, \
				self.filename, "-unsharp", "1x3+.6+.09", ")" ]
		else:
			args[ 1 : 1 ] = [ self.filename ]

		# call ImageMagick to do our dirtywork
		try:
			print "calling: " + ' '.join( args )
			ret = call( args )
			if ret != 0:
				print >>sys.stderr, "ImageMagick failed, returned ", ret
				return
		except OSError as e:
			print >>sys.stderr, "Failed to execute ImageMagick: ", e
			return

		if self.prefs.export_inplace:
			# move original file out of the way
			try:
				os.rename( self.filename, self.filename + ".old" )
			except OSError as e:
				print >>sys.stderr, "Could not rename original image file: ", \
					e.strerror
				exit( 1 )

			# move new file in-place
			try:
				os.rename( out_filename, self.filename )
			except OSError as e:
				# try and put the original file back again
				try:
					os.rename( self.filename + ".old", self.filename )
				except OSError:
					pass
				print >>sys.stderr, e.strerror
				exit( 1 )

			# delete original
			try:
				os.unlink( self.filename + ".old" )
			except OSError as e:
				print >>sys.stderr, "Unable to remove original image file (it ", \
					"now has '.old' appended to it's name): ", e.strerror
				exit( 1 )

		# close this window
		self.window.destroy()

	def prefs_button_pressed( self, widget ):
		PreferencesDialog( self.window ).main()
		self.prefs = Preferences()
		self.load_main_image()
		self.load_copyright_image()
		self.drawing_area.queue_resize()
		self.drawing_area.queue_draw()

	def key_press_event( self, widget, event ):
		if event.keyval == gtk.keysyms.Escape:
			self.window.destroy()
		elif event.keyval == gtk.keysyms.Up:
			self.move_pos( "bottom", "top" )
		elif event.keyval == gtk.keysyms.Down:
			self.move_pos( "top", "bottom" )
		elif event.keyval == gtk.keysyms.Left:
			self.move_pos( "right", "left" )
		elif event.keyval == gtk.keysyms.Right:
			self.move_pos( "left", "right" )
		else:
			return False

		return True

	def delete_event( self, widget, event, data = None ):
		return False

	def destroy( self, widget, data = None ):
		gtk.main_quit()

	def motion_notify_event( self, widget, event ):
		x, y, width, height = widget.get_allocation()

		# get mouse position
		if event.is_hint:
			x, y, state = event.window.get_pointer()
		else:
			x = event.x
			y = event.y
			state = event.state

		# calculate copyright overlay pos
		pos = ( "top" if y <= height / 2 else "bottom" ) + "-" + \
			( "left" if x <= width / 2 else "right" )

		# redraw if necessary
		if getattr( self, "pos", None ) != pos:
			self.pos = pos
			self.generate_pixmap()
			widget.queue_draw()

		return True

	def drawing_area_button_press_event( self, widget, event ):
		if self.ok_button.get_property( "sensitive" ):
			self.ok_button.clicked()

	def drawing_area_configure_event( self, widget, event ):
		x, y, width, height = widget.get_allocation()

		# work out minimum scale required to fit pic_orig in to the widget area
		scale = min( float( width ) / self.pic.get_width(),
			float( height ) / self.pic.get_height() )

		# scale the images
		self.pic_scale = self.pic.scale_simple( \
			int( round( scale * self.pic.get_width() ) ), \
			int( round( scale * self.pic.get_height() ) ), \
			gtk.gdk.INTERP_BILINEAR )
		if self.copyright_pic != None:
			self.copyright_pic_scale = self.copyright_pic.scale_simple( \
				int( round( scale * self.copyright_pic.get_width() ) ), \
				int( round( scale * self.copyright_pic.get_height() ) ), \
				gtk.gdk.INTERP_BILINEAR )

		# recreate the pixmap to render to the screen
		self.generate_pixmap()

	def generate_pixmap( self ):
		widget = self.drawing_area
		x, y, width, height = widget.get_allocation()

		# calculate offsets to centre pic in drawing area
		pre_width = ( width - self.pic_scale.get_width() ) / 2
		pre_height = ( height - self.pic_scale.get_height() ) / 2
		post_width = width - self.pic_scale.get_width() - pre_width
		post_height = height - self.pic_scale.get_height() - pre_height

		# create pixmap
		self.pixmap = gtk.gdk.Pixmap( widget.window, width, height )

		# create composite pixbuf containing the main image
		pixbuf = self.pic_scale.copy()

		if self.copyright_pic != None and \
			getattr( self, "copyright_pic_scale", None ) != None:

			# work out position of copyright overlay
			if self.pos == "top-left" or self.pos == "bottom-left":
				x = 0
			if self.pos == "top-right" or self.pos == "bottom-right":
				x = self.pic_scale.get_width() - \
					self.copyright_pic_scale.get_width()
			if self.pos == "top-left" or self.pos == "top-right":
				y = 0
			if self.pos == "bottom-left" or self.pos == "bottom-right":
				y = self.pic_scale.get_height() - \
					self.copyright_pic_scale.get_height()

			# draw copyright
			self.copyright_pic_scale.composite( pixbuf, x, y, \
				self.copyright_pic_scale.get_width(), \
				self.copyright_pic_scale.get_height(), \
				x, y, 1, 1, gtk.gdk.INTERP_NEAREST, 255 )

		# draw composite pixbuf to the off-screen pixmap
		self.pixmap.draw_pixbuf( widget.get_style().white_gc, \
			pixbuf, 0, 0, pre_width, pre_height, \
			pixbuf.get_width(), pixbuf.get_height() )

		# draw black rectangles if the pic is being centred
		if pre_width > 0 or pre_height > 0:
			self.pixmap.draw_rectangle( widget.get_style().black_gc, True, \
				0, 0, \
				pre_width if pre_width > 0 else width, \
				pre_height if pre_height > 0 else height )
		if post_width > 0 or post_height > 0:
			self.pixmap.draw_rectangle( widget.get_style().black_gc, True, \
				width - post_width if post_width > 0 else 0, \
				height - post_height if post_height > 0 else 0, \
				post_width if post_width > 0 else width, \
				post_height if post_height > 0 else height )

	def move_pos( self, tok_from, tok_to ):

		# calculate copyright overlay pos
		pos = self.pos.replace( tok_from, tok_to, 1 )

		# redraw if necessary
		if self.pos != pos:
			self.pos = pos
			self.generate_pixmap()
			self.drawing_area.queue_draw()

	def drawing_area_expose_event( self, widget, event ):
		x, y, width, height = event.area
		widget.window.draw_drawable( \
			widget.get_style().fg_gc[ gtk.STATE_NORMAL ], \
			self.pixmap, x, y, x, y, width, height )
		return False
