#!/usr/bin/env python

#    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/>.

#  PicUploadScreenlet (c) Igor Vatavuk 2008 <igor@generationstars.com>
#
# INFO:
# - Drag and drop pictures to upload them to imageshack.
#   This screenlet is based on ShackShag script <http://http://code.google.com/p/shackshag/>

import screenlets
from screenlets.options import StringOption , BoolOption , IntOption , FileOption , DirectoryOption , ListOption , AccountOption , TimeOption , FontOption, ColorOption , ImageOption
from screenlets.options import create_option_from_node
from screenlets import DefaultMenuItem
import pango
import gobject
import gtk
import sys, os, getopt, httplib, mimetypes
import urllib
import threading


ALLOWED_EXTENSIONS = ('jpeg', 'jpg', 'png', 'gif', 'bmp', 'tif', 'tiff', 'swf')
MAX_FILE_SIZE = 1536 # size in kB
HOST = 'imageshack.us'
SELECTOR = '/'
USER_AGENT = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.12) \
Gecko/20050922 Firefox/1.0.7 (Debian package 1.0.7-1)"
REFERRER = 'http://imageshack.us/'
FORM_MAX_FILE_SIZE = '3145728'
FORM_SUBMIT = 'host it!'
REMOVE_THUMB_BAR_FIELD = ('rembar', '1')
HINT = 'SHELL UPLOADER:'
EMAIL = ''

class Uploader (threading.Thread):
	def __init__(self, filename):
		self.image=filename
		self.finished=False
		self.stopped=False
		self.succesful=True
		self.result = ['','','']
		threading.Thread.__init__(self)

	def is_finished(self):
		return self.finished

	def get_result(self):
		return self.result

	def get_file_string(self, response):
		if self.stopped==False:
			it = iter(response.read().split('\n'))
			try:
				while True:
					line = it.next()
					if line.find(HINT) != -1:
						return line.split(':')[1].strip()
			except StopIteration:
				self.finished='True'
				return None

	def get_file_contents(self, file_name):
		if self.stopped==False:
			f = open(file_name);
			data = f.read()
			f.close()
			return data

	def send_file(self, file_name, additional_fields):
		if self.stopped==False:
			fields = [
			    ('MAX_FILE_SIZE', FORM_MAX_FILE_SIZE),
			    ('refer', ''),
			    ('brand', ''),
			    ('email', EMAIL),
			    ('submit', FORM_SUBMIT)
			    ]

			fields.extend(additional_fields)
			file_data = ('fileupload', file_name, self.get_file_contents(file_name))
			content_type, body = self.encode_multipart_formdata(fields, file_data)
			h = httplib.HTTPConnection(HOST)
			h.putrequest(_('POST'), SELECTOR)
			h.putheader(_('Content-Type'), content_type)
			h.putheader(_('Content-Length'), str(len(body)))
			h.putheader(_('Referer'), REFERRER)
			h.putheader(_('User-Agent'), USER_AGENT)
			h.endheaders()
			h.send(body)
			return self.get_file_string(h.getresponse())

	def encode_multipart_formdata(self, fields, file_data):
		if self.stopped==False:
			BOUNDARY = '----------HappyHappy__bouNdaRY_$'
			CRLF = '\r\n'
			L = []
			 # add additional form fields
			for (key, value) in fields:
				L.append('--' + BOUNDARY)
				L.append('Content-Disposition: form-data; name="%s"' % key)
				L.append('')
				L.append(value)
			# add file
			L.append('--' + BOUNDARY)
			L.append('Content-Disposition: form-data; name="%s"; filename="%s"' \
			    % (file_data[0], file_data[1]))
			L.append('Content-Type: %s' % self.get_content_type(file_data[1]))
			L.append('')
			L.append(file_data[2])
			L.append('--' + BOUNDARY + '--')
			L.append('')
			body = CRLF.join(L)
			content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
			return content_type, body

	def get_content_type(self, filename):
		if self.stopped==False:
			return mimetypes.guess_type(filename)[0] or 'application/octet-stream'

	def image_details(self, id_string):
		if self.stopped==False:
			details = {'host': HOST}
			details['node'], details['dir'], details['file'] = id_string.split('/')
			details['file_prefix'], details['file_extension'] = details['file'].split('.')
			return details

	def get_direct_url(self, id_string):
		return 'http://%s.%s/%s' % (id_string.split('/')[0], HOST, id_string)

	def stop(self):
		self.stopped=True
		return

	def get_thumbnail(self, id_string):
		return '<a href="http://%(node)s.%(host)s/my.php?image=%(file)s" \
		target="_blank"><img src="\
		http://%(node)s.%(host)s/%(node)s/%(dir)s/%(file_prefix)s.th.%(file_extension)s" \
		border="0" alt="Free Image Hosting at www.ImageShack.us" /></a>' \
		  % self.image_details(id_string)

	def get_forum_link(self, id_string):
		return '[URL=http://%(host)s]\
[IMG]http://%(node)s.%(host)s/%(node)s/%(dir)s/%(file)s[/IMG][/URL]' \
  % self.image_details(id_string)

	def run(self):
		"""Main function, calls others to upload image."""
		try:
			additional_fields = []
			additional_fields.append((REMOVE_THUMB_BAR_FIELD));
			if self.stopped==False:
				if self.image.split('.')[-1].lower() not in ALLOWED_EXTENSIONS:
					print >> sys.stderr, '%s: illegal file extension, skipping' % self.image
				if os.stat(self.image).st_size < MAX_FILE_SIZE * 1024:
					id_string = self.send_file(self.image, additional_fields)
					if id_string != None:
						self.result[0] = self.get_direct_url(id_string)
						print self.result[0]
						self.result[1] = self.get_thumbnail(id_string)
						self.result[2] = self.get_forum_link(id_string)
						self.finished=True
						return self.result
					else:
						print >> sys.stderr, 'Failed to upload file: %s' % image
				else:
					print >> sys.stderr, '%s: file to large, max. size is %s kilobytes.' \
% (image, MAX_FILE_SIZE)
		except:
			self.succesful=False
			self.finished=True

#use gettext for translation
import gettext

_ = screenlets.utils.get_translator(__file__)

def tdoc(obj):
	obj.__doc__ = _(obj.__doc__)
	return obj

@tdoc

class PicUploadScreenlet (screenlets.Screenlet):
	"""Drag and drop pictures to upload them to imageshack"""
	
	# default meta-info for Screenlets (should be removed and put into metainfo)
	__name__	= 'PicUploadScreenlet'
	__version__	= '1.1'
	__author__	= 'Igor Vatavuk'
	__desc__	= __doc__	# set description to docstring of class
	
	#vars
	result=[]
	filename=''
	yy=0
	# editable options (options that are editable through the UI)
	drag_text = _('Drag a picture here')
	state = _('Waiting')
	bool_tooltip=True
	bool_sound = True
	color_example =(1, 1, 1, 1)
	font_example = "Sans Medium 5"
	hover = False
	number = 1
	# constructor
	def __init__ (self, **keyword_args):
		#call super (width/height MUST match the size of graphics in the theme)
		screenlets.Screenlet.__init__(self, width=160, height=160, 
			uses_theme=True,drag_drop=True, **keyword_args)
		# set theme
		self.theme_name = "default"
		# add option group
		self.add_options_group(_('PicUpload'), _('Options'))
		# add editable option to the group
		self.add_option(StringOption(_('PicUpload'),'drag_text',			# attribute-name
			self.drag_text,						# default-value
			_('Waiting state text:'), 						# widget-label
			_('Text shown during waiting state, default: Drag a picture here')	# description
			))

		self.add_option(BoolOption(_('PicUpload'),'bool_sound', 
			self.bool_sound, _('Sound'), 
			_('Enable/Disable sounds (requires mplayer)')))

		self.add_option(BoolOption(_('PicUpload'),'bool_tooltip', 
			self.bool_tooltip, _('Show tooltip'), 
			_('Enable/Disable tooltip after completed upload')))

		self.add_option(ColorOption(_('PicUpload'),'color_example', 
			self.color_example, _('Font color'), 
			_('Choose font color')))

	def update (self):
		if self.number < 12:
			self.number = self.number+1
		else:
			self.number = 1

		if self.thread.is_finished():
			self.thread.join()
			self.timer = gobject.source_remove(self.timer)
			self.result=self.thread.get_result()
			if self.result[0]!='':
				print self.result[0]
				print self.result[1]
				print self.result[2]
				os.popen('mplayer -quiet -slave ' + chr(34) + 'file://' + self.get_screenlet_dir() + '/themes/' + self.theme_name + '/complete.ogg' + chr(34) , 'w')
				self.state=_('Completed')
			else:
				self.state=_('Failed')
			
		self.redraw_canvas()
		return True # keep running this event	


	def on_drop (self, x, y, sel_data, timestamp):
		if self.state==_('Waiting'):
			self.filename = ''
			# get text-elements in selection data
			try:
				txt = unicode.encode(sel_data.get_text(), 'utf-8')

			except:
				txt = sel_data.get_text()

			txt = urllib.unquote(txt)
			txt = txt[:-1]
			if txt and txt != '':
				# if it is a filename, use it
				if txt.startswith('file://'):
					self.filename = txt[7:]
				else:
					screenlets.show_error(self, _('Invalid string: %s.') % txt)
			else:
				# else get uri-part of selection
				uris = sel_data.get_uris()
				if uris and len(uris)>0:
					self.filename = uris[0][7:]
			self.do_upload()


	def do_upload (self):
			if self.filename != '':
				self.timer = gobject.timeout_add( 100, self.update)
				self.thread = Uploader(self.filename)
				self.thread.start()
				self.state=_('Uploading')
		
	def on_init (self):
		"""Called when the Screenlet's options have been applied and the 
		screenlet finished its initialization. If you want to have your
		Screenlet do things on startup you should use this handler."""
		self.add_default_menuitems()
	
	def on_mouse_down (self, event):
		"""Called when a buttonpress-event occured in Screenlet's window. 
		Returning True causes the event to be not further propagated."""
		x = event.x / self.scale
		y = event.y / self.scale
		self.hover=False
		
		if event.button == 1 and x >= 20 and x <= 140:
			if self.state==_('Uploading') and y>=110 and y<=130:
				self.thread.stop()
				try:
					self.timer = gobject.source_remove(self.timer)
				except:
					print _('Timer stopped')
				self.state=_('Waiting')
			elif self.state==_('Failed') and y>=82 and y<=102:
				self.do_upload()
			elif self.state==_('Failed') and y>=110 and y<=130:
				self.state=_('Waiting')
			elif self.state==_('Completed') and y>=37 and y<=57:
				# get the clipboard
				clipboard = gtk.clipboard_get()
				# set the clipboard text data
				clipboard.set_text(self.result[0])
				# make our data available to other applications
				clipboard.store()
			elif self.state==_('Completed') and y>=65 and y<=85:
				# get the clipboard
				clipboard = gtk.clipboard_get()
				# set the clipboard text data
				clipboard.set_text(self.result[2])
				# make our data available to other applications
				clipboard.store()
			elif self.state==_('Completed') and y>=93 and y<=113:
				# get the clipboard
				clipboard = gtk.clipboard_get()
				# set the clipboard text data
				clipboard.set_text(self.result[1])
				# make our data available to other applications
				clipboard.store()
			elif self.state==_('Completed') and y>=121 and y<=141:
				self.state=_('Waiting')
				
			self.redraw_canvas()
		return False
	
	def on_mouse_enter (self, event):
		"""Called when the mouse enters the Screenlet's window."""

	        if self.state==_('Completed') and self.bool_tooltip:
			self.show_tooltip(_("Click on a button to copy the link to clipboard"),self.x+self.mousex,self.y+self.mousey)
		else:
			self.hide_tooltip()
		
	def on_mouse_leave (self, event):
		"""Called when the mouse leaves the Screenlet's window."""
		self.hide_tooltip()
		self.hover = False

	def on_mouse_move(self, event):
		"""Called when the mouse moves in the Screenlet's window."""
		x = event.x / self.scale
		y = event.y / self.scale
		
		if x >= 20 and x <= 140:
			if self.state==_('Uploading') and y>=110 and y<=130:
				self.yy=110
				self.hover=True
			elif self.state==_('Failed') and y>=82 and y<=102:
				self.yy=82
				self.hover=True
			elif self.state==_('Failed') and y>=110 and y<=130:
				self.yy=110
				self.hover=True
			elif self.state==_('Completed') and y>=37 and y<=57:
				self.yy=37
				self.hover=True
			elif self.state==_('Completed') and y>=65 and y<=85:
				self.yy=65
				self.hover=True
			elif self.state==_('Completed') and y>=93 and y<=113:
				self.yy=93
				self.hover=True
			elif self.state==_('Completed') and y>=121 and y<=141:
				self.yy=121
				self.hover=True
			else:
				self.hover=False
		else:
			self.hover=False
				
			self.redraw_canvas()
		self.redraw_canvas()
		pass

	def on_draw (self, ctx):
		# if theme is loaded
		if self.theme:
			# set scale rel. to scale-attribute
			ctx.scale(self.scale, self.scale)
			ctx.set_source_rgba( self.color_example[0], self.color_example[1], self.color_example[2],self.color_example[3])
			self.theme.render(ctx, 'bg')
			if self.state==_('Waiting'):
				ctx.save()
				ctx.translate(35,45)
				self.theme.render(ctx, 'drop')
				ctx.restore()
				ctx.set_source_rgba( self.color_example[0], self.color_example[1], self.color_example[2],self.color_example[3])
				self.draw_text(ctx, self.drag_text, 0, 15, self.font_example , 10, self.width,pango.ALIGN_CENTER)
			elif self.state==_('Uploading'):
				ctx.save()
				ctx.translate(65,50)
				self.theme.render(ctx, 'prog'+str(self.number))
				ctx.restore()
				ctx.set_source_surface(self.theme['button.png'], 20, 110)
				ctx.paint_with_alpha(1)
				ctx.set_source_rgba( self.color_example[0], self.color_example[1], self.color_example[2],self.color_example[3])
				self.draw_text(ctx, _('Cancel'), 0, 112, self.font_example , 10, self.width,pango.ALIGN_CENTER)
				self.draw_text(ctx, _('Uploading'), 0, 15, self.font_example , 10, self.width,pango.ALIGN_CENTER)
			elif self.state==_('Completed'):
				ctx.set_source_surface(self.theme['button.png'], 20, 37)
				ctx.paint_with_alpha(1)
				ctx.set_source_surface(self.theme['button.png'], 20, 65)
				ctx.paint_with_alpha(1)
				ctx.set_source_surface(self.theme['button.png'], 20, 93)
				ctx.paint_with_alpha(1)
				ctx.set_source_surface(self.theme['button.png'], 20, 121)
				ctx.paint_with_alpha(1)
				ctx.set_source_rgba( self.color_example[0], self.color_example[1], self.color_example[2],self.color_example[3])
				self.draw_text(ctx, _('Direct link'), 0, 39, self.font_example , 10, self.width,pango.ALIGN_CENTER)
				self.draw_text(ctx, _('Forum code'), 0, 67, self.font_example , 10, self.width,pango.ALIGN_CENTER)
				self.draw_text(ctx, _('HTML code'), 0, 95, self.font_example , 10, self.width,pango.ALIGN_CENTER)
				self.draw_text(ctx, _('Back'), 0, 123, self.font_example , 10, self.width,pango.ALIGN_CENTER)
				self.draw_text(ctx, _('Completed'), 0, 15, self.font_example , 10, self.width,pango.ALIGN_CENTER)
			elif self.state==_('Failed'):
				ctx.save()
				ctx.translate(55,30)
				self.theme.render(ctx, 'failed')
				ctx.restore()
				ctx.set_source_surface(self.theme['button.png'], 20, 82)
				ctx.paint_with_alpha(1)
				ctx.set_source_surface(self.theme['button.png'], 20, 110)
				ctx.paint_with_alpha(1)
				ctx.set_source_rgba( self.color_example[0], self.color_example[1], self.color_example[2],self.color_example[3])
				self.draw_text(ctx, _('Try Again'), 0, 85, self.font_example , 10, self.width,pango.ALIGN_CENTER)
				self.draw_text(ctx, _('Back'), 0, 112, self.font_example , 10, self.width,pango.ALIGN_CENTER)
				self.draw_text(ctx, _('Failed'), 0, 15, self.font_example , 10, self.width,pango.ALIGN_CENTER)
			ctx.set_source_rgba(1,1,1,0.2)
			if self.hover:
				self.draw_rectangle(ctx,20,self.yy,120,20)
			self.theme.render(ctx, 'glass')
	
	def on_draw_shape (self, ctx):
		self.on_draw(ctx)
	
# If the program is run directly or passed as an argument to the python
# interpreter then create a Screenlet instance and show it
if __name__ == "__main__":
	# create new session
	import screenlets.session
	screenlets.session.create_session(PicUploadScreenlet)

