import gtk
import cairo
import pango
from gtk.gdk import CairoContext
from ConfigParser import SafeConfigParser


class Color:
	def __init__(self, color="(0.1,0.1,0.1,0.7)"):
		if color and color != "None":
			if color.__class__.__name__ == "tuple": 
				self.color = color

			elif color.__class__.__name__ == "str": 
				s = color
				self.color = None
				try:
					if s[0] + s[-1] != "()":
						raise ValueError("Badly Formatted Color value (missing brackets).")
					items = s[1:-1] # removes the leading and trailing brackets
					items = items.split(',')
					L = [float(x.strip()) for x in items]
					self.color=tuple(L)
				except:
					raise ValueError("Badly Formatted Color value")
		else:
			self.color = None

	def get(self):
		return self.color


class EnumParser:
	def __init__(self, value, enums, classname="int"):
		if value:
			if (value.__class__.__name__ == classname) and (value in enums.values()):
				self.value=value
			elif value.__class__.__name__ == "str":
				for key in enums:
					if key.lower()==value:
						self.value = enums[key]
		else:
			self.value = None
	
	def get(self):
		return self.value


class Alignment:
	LEFT, MIDDLE, RIGHT = 0,1,2

	_enums = {"left":LEFT, "middle":MIDDLE, "right":RIGHT}

	def __init__(self, alignment=LEFT):
		self.alignment = EnumParser(alignment, Alignment._enums).get()
	
	def get(self):
		return self.alignment


class Gradient:
	HORIZONTAL, VERTICAL = 0,1

	_enums = {"horizontal":HORIZONTAL, "vertical":VERTICAL}

	def __init__(self, gradient_type=VERTICAL):
		self.gradient_type = EnumParser(gradient_type, Gradient._enums).get()

	def get(self):
		return self.gradient_type

	def createGradient (self, gradient_type, col1, col2, width, height):
		gradient = None
		if gradient_type and col1 and col2:
			if gradient_type == Gradient.VERTICAL:
				gradient = cairo.LinearGradient(0,0,0,height)
			elif gradient_type == Gradient.HORIZONTAL:
				gradient = cairo.LinearGradient(0,0,width,0)
			if gradient:
				gradient.add_color_stop_rgba(0,col1[0],col1[1],col1[2],col1[3])
				gradient.add_color_stop_rgba(1,col2[0],col2[1],col2[2],col2[3])
			return gradient
	
	createGradient = classmethod(createGradient)


class RowItems:
	BUDDY_ICON, STATUS_ICON, PROTOCOL_ICON = 5,6,7
	BUDDY_PROTOCOL_ICON, BUDDY_INFO = 8,9

	ICONS = {"buddy_icon":BUDDY_ICON, "status_icon":STATUS_ICON,
				"protocol_icon":PROTOCOL_ICON, "buddy_info":BUDDY_INFO,
				"buddy_protocol_icon":BUDDY_PROTOCOL_ICON}

	def __init__(self, row_items):
		if row_items:
			if row_items.__class__.__name__ == "list": 
				self.row_items = row_items
			elif row_items.__class__.__name__ == "str": 
				s = row_items
				self.row_items = []
				str_row_items = []
				try:
					if s[0] + s[-1] != "[]":
						raise ValueError("Badly Formatted 'row_items' (missing square brackets).")
					items = s[1:-1] # removes the leading and trailing brackets
					items = items.split(',')
					str_row_items = [str(x.strip()) for x in items]
				except:
					raise ValueError("Badly Formatted 'row_items'")

				for item in str_row_items:
					if item in RowItems.ICONS:
						val = EnumParser(item, RowItems.ICONS).get()
						if val: self.row_items.append(val)
					else:
						raise ValueError("Unknown entry '"+item+"' in row_items")
		else:
			self.row_items = []

	def get(self):
		return self.row_items


def parseBoolean(value):
	if value.__class__.__name__ == "str":
		value = (value.lower()=="true")
	return value


class WindowOptions:
	def __init__(self, width=180, round_style=True, radius=5, 
			shadowed=True, shadow_size=16, shadow_color=(0.1,0.1,0.1,0.9)):

		self.width, self.round_style, self.radius = width, round_style, radius
		self.shadowed, self.shadow_size = shadowed, shadow_size
		self.shadow_color = shadow_color

	def __setattr__(self, name, value):
		if name == 'shadow_color':
			self.__dict__[name] = Color(value).get()
		elif name in ('width', 'shadow_size', 'radius'):
			self.__dict__[name] = int(value)
		elif name in ('round_style', 'shadowed'):
			self.__dict__[name] = parseBoolean(value)
		else:
			self.__dict__[name] = value
	

class Layout:
	def __init__ (self, row_items=None, use_alternating_style=False):
		self.row_items = row_items
		self.use_alternating_style=use_alternating_style

	def __setattr__(self, name, value):
		if name == 'row_items':
			self.__dict__[name] = RowItems(value).get()
		elif name in ('use_alternating_style'):
			self.__dict__[name] = parseBoolean(value)
		else:
			self.__dict__[name] = value


class GroupInfo:
	def __init__ (self, collapsible=True, show_buddy_count=False, 
			show_border=False, border_color=(0,0,0,0), 
			border_width=1):
		self.collapsible = collapsible
		self.show_buddy_count = show_buddy_count
		self.show_border = show_border
		self.border_width = border_width
		self.border_color = Color(border_color).get()

	def __setattr__(self, name, value):
		if name == 'border_color':
			self.__dict__[name] = Color(value).get()
		elif name in ('border_width'):
			self.__dict__[name] = int(value)
		elif name in ('collapsible', 'show_buddy_count', 'show_border'):
			self.__dict__[name] = parseBoolean(value)
		else:
			self.__dict__[name] = value

class StatusText:
	def __init (self, enabled=False, font="Sans 7", ypadding=2, color = None):
		self.enabled = enabled
		self.font = font
		self.ypadding = ypadding
		self.color = Color(color).get()

	def __setattr__(self, name, value):
		if name == 'color':
			self.__dict__[name] = Color(value).get()
		elif name in ('ypadding'):
			self.__dict__[name] = int(value)
		elif name in ('enabled'):
			self.__dict__[name] = parseBoolean(value)
		else:
			self.__dict__[name] = value

class Icon:
	def __init (self, size=0, lpadding=2, rpadding=2, rounding_radius=0.0):
		self.size = size
		self.lpadding = lpadding
		self.rpadding = rpadding
		self.rounding_radius = rounding_radius

	def __setattr__(self, name, value):
		if name in ('lpadding', 'rpadding', 'size'):
			self.__dict__[name] = int(value)
		elif name in ('rounding_radius'):
			self.__dict__[name] = float(value)
		elif name in ('alignment'):
			self.__dict__[name] = Alignment(value).get()
		else:
			self.__dict__[name] = value

class Style:
	def __init__ (self, font="Sans 8", color=(0,0,0,1), use_gradient=True,
			background=(0.6,0.6,0.6,0.8), background_gradient=(0.35,0.35,0.35,0.8),
			gradient_type=Gradient.VERTICAL, lpadding=5, rpadding=5, 
			top_spacing=2, bottom_spacing=2, text_alignment=Alignment.LEFT,
			height=16, icon_transparency=0.8, capital_letters=False, enabled=True, text_shadow=False):
		self.font = font
		self.color = color
		self.use_gradient = use_gradient
		self.background = background
		self.background_gradient = background_gradient
		self.gradient_type = gradient_type
		self.lpadding = lpadding
		self.rpadding = rpadding
		self.top_spacing = top_spacing
		self.bottom_spacing = bottom_spacing
		self.text_alignment = Alignment(text_alignment).get()
		self.height = height
		self.icon_transparency = icon_transparency
		self.capital_letters = capital_letters
		self.enabled = enabled
		self.text_shadow = False
	
	def __setattr__(self, name, value):
		if name in ('color', 'background', 'background_gradient'):
			self.__dict__[name] = Color(value).get()
		elif name in ('text_alignment'):
			self.__dict__[name] = Alignment(value).get()
		elif name in ('gradient_type'):
			self.__dict__[name] = Gradient(value).get()
		elif name in ('lpadding', 'rpadding', 'height', 'top_spacing', 'bottom_spacing',
				'notify_time'):
			if value: self.__dict__[name] = int(value)
			else: self.__dict__[name] = 0
		elif name in ('icon_transparency'):
			self.__dict__[name] = float(value)
		elif name in ('use_gradient', 'enabled', 'capital_letters', 'text_shadow'):
			self.__dict__[name] = parseBoolean(value)
		else:
			self.__dict__[name] = value


class ThemeParser:
	# Define some constants
	IMPORT = "import_from"

	STYLE_SECTIONS = ["group", "buddy_online", "buddy_online_alt",
			"buddy_idle", "buddy_idle_alt", "buddy_away", "buddy_away_alt",
			"buddy_busy", "buddy_busy_alt", "buddy_offline", "buddy_offline_alt",
			"buddy_signon", "buddy_signoff", "buddy_typing", "buddy_content",
			"buddy_hover", "buddy_selected", "notification"]

	ICON_SECTIONS = ["protocol_icon", "status_icon", "buddy_icon", "group_collapse_icon"]

	_test_ctx = gtk.gdk.CairoContext(
			cairo.Context(cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100)))

	def getFontHeight(self, font):
		p_layout = self._test_ctx.create_layout()
		p_fdesc = pango.FontDescription(font)
		p_layout.set_font_description(p_fdesc)
		p_layout.set_text("X")
		(text_width,text_height) = p_layout.get_pixel_size()
		return text_height

	def __init__(self, filename):
		c = SafeConfigParser()
		c.read(filename)
		read_sections = {}
		unread_sections = list(c.sections())
		import_count = 0

		while len(unread_sections):
			s = unread_sections.pop(0)
			#print "Parsing "+s
			deps = None
			if ThemeParser.IMPORT in c.options(s):
				deps = c.get(s, ThemeParser.IMPORT)
				if deps not in read_sections:
					#print "Depends on "+deps+".. deferring read"
					unread_sections.append(s)
					import_count += 1
					if import_count > 100:
						raise "Circular Imports ! "+str(unread_sections)
					continue

			# See section type and initialize appropriate data structure
			obj = None
			if s in ThemeParser.STYLE_SECTIONS: obj = Style()
			elif s in RowItems.ICONS or s=="group_collapse_icon": obj = Icon()
			elif s=="window": obj = WindowOptions()
			elif s=="group_info": obj = GroupInfo()
			elif s=="layout": obj = Layout()
			elif s=="status_text": obj = StatusText()

			if obj:
				self.__dict__[s] = obj
				# Do the import if need be 
				if deps:
					depobj = read_sections[deps]
					if depobj.__class__ != obj.__class__:
						raise "Invalid Import (Different class): "+str(s)+" -> "+deps
					for i in depobj.__dict__:
						obj.__dict__[i] = depobj.__dict__[i]

				# Read values in this section and set values
				if obj:
					for o in c.options(s):
						obj.__setattr__(o, c.get(s, o))
			else:
				raise "Unknown Section: "+s

			# Mark this section as read
			read_sections[s] = obj

		self.setCellSizes()
		if not self.window.round_style:
			self.window.radius=0
		if not self.window.shadowed:
			self.window.shadow_size=0
		if not self.group_info.show_border:
			self.group_info.border_width=0


	def calculateHeight(self, styleobj):
		ht = self.getFontHeight(styleobj.font)
		if self.status_text.enabled:
			self.status_text.height = self.getFontHeight(self.status_text.font)
			ht += self.status_text.height + self.status_text.ypadding

		if ht < self.buddy_icon.size :
			if (RowItems.BUDDY_ICON in self.layout.row_items or
					RowItems.BUDDY_PROTOCOL_ICON in self.layout.row_items): 
				ht = self.buddy_icon.size 

		ht += styleobj.top_spacing + styleobj.bottom_spacing
		return ht


	def setCellSizes (self):
		# Increase Buddy list height if required
		# Set the height and width of styles
		# - height changed for only "buddy" styles that are smaller
		for i in self.__dict__:
			obj = self.__dict__[i]
			if obj.__class__.__name__ == "Style":
				obj.__dict__["width"] = self.window.width
				if i.find("buddy") == 0:
					obj.height = self.calculateHeight(obj)

if __name__ == "__main__":
	t = ThemeParser('/home/varun/.screenlets/Pidgin/themes/blue_lagoon/options.conf')
	print t.window.width
	print t.buddy_online.font
	print t.buddy_away.font
	print t.buddy_online.background[0]
	print t.buddy_online.use_gradient
	print t.buddy_online.gradient_type
	print t.buddy_online.text_alignment
	print t.buddy_online.height
	print t.buddy_online.width
	print t.buddy_away.height
