"""
Base Class for DesktopEntry, IconTheme and IconData
"""

import re, os.path, codecs
from Exceptions import *
import xdg.Locale

class IniFile:
	defaultGroup = ''
	fileExtension = ''

	filename = ''

	tainted = False

	def __init__(self, filename=None):
		self.content = dict()
		if filename:
			self.parse(filename)

	def parse(self, filename, headers):
		# for performance reasons
		content = self.content

		if not os.path.isfile(filename):
			raise ParsingError("File not found", filename)

		# parse file
		for line in file(filename,'r'):
			line = line.strip()
			# empty line
			if not line:
				continue
			# comment
			elif line[0] == '#':
				continue
			# new group
			elif line[0] == '[':
				currentGroup = line.lstrip("[").rstrip("]")
				if debug and self.hasGroup(currentGroup):
					raise DuplicateGroupError(currentGroup, filename)
				else:
					content[currentGroup] = {}
			# key
			else:
				index = line.find("=")
				key = line[0:index].strip()
				value = line[index+1:].strip()
				try:
					if debug and self.hasKey(key, currentGroup):
						raise DuplicateKeyError(key, currentGroup, filename)
					else:
						content[currentGroup][key] = value
				except (IndexError, UnboundLocalError):
					raise ParsingError("[%s]-Header missing" % headers[0], filename)

		self.filename = filename
		self.tainted = False

		# check header
		for header in headers:
			if content.has_key(header):
				self.defaultGroup = header
				break
		else:
			raise ParsingError("[%s]-Header missing" % headers[0], filename)

	# start stuff to access the keys
	def get(self, key, group=None, locale=False, type="string", list=False):
		# set default group
		if not group:
			group = self.defaultGroup

		# return key (with locale)
		if self.content.has_key(group) and self.content[group].has_key(key):
			if locale:
				value = self.content[group][self.__addLocale(key, group)]
			else:
				value = self.content[group][key]
		else:
			if debug:
				if not self.content.has_key(group):
					raise NoGroupError(group, self.filename)
				elif not self.content[group].has_key(key):
					raise NoKeyError(key, group, self.filename)
			else:
				value = ""

		if list == True:
			values = self.getList(value)
			result = []
		else:
			values = [value]

		for value in values:
			if type == "string" and locale == True:
				value = value.decode("utf-8", "ignore")
			elif type == "boolean":
				value = self.__getBoolean(value)
			elif type == "integer":
				try:
					value = int(value)
				except ValueError:
					value = 0
			elif type == "numeric":
				try:
					value = float(value)
				except ValueError:
					value = 0.0
			elif type == "regex":
				value = re.compile(value)
			elif type == "point":
				value = value.split(",")

			if list == True:
				result.append(value)
			else:
				result = value

		return result
	# end stuff to access the keys

	# start subget
	def getList(self, string):
		if re.search(r"(?<!\\)\;", string):
			list = re.split(r"(?<!\\);", string)
		elif re.search(r"(?<!\\)\|", string):
			list = re.split(r"(?<!\\)\|", string)
		elif re.search(r"(?<!\\),", string):
			list = re.split(r"(?<!\\),", string)
		else:
			list = [string]
		if list[-1] == "":
			list.pop()
		return list

	def __getBoolean(self, boolean):
		if boolean == 1 or boolean == "true" or boolean == "True":
			return True
		elif boolean == 0 or boolean == "false" or boolean == "False":
			return False
		return False
	# end subget

	def __addLocale(self, key, group=None):
		"add locale to key according the current lc_messages"
		# set default group
		if not group:
			group = self.defaultGroup

		for lang in xdg.Locale.langs:
			if self.content[group].has_key(key+'['+lang+']'):
				return key+'['+lang+']'

		return key

	# start validation stuff
	def validate(self, report="All"):
		"validate ... report = All / Warnings / Errors"

		self.warnings = []
		self.errors = []

		# get file extension
		self.fileExtension = os.path.splitext(self.filename)[1]

		# overwrite this for own checkings
		self.checkExtras()

		# check all keys
		for group in self.content:
			self.checkGroup(group)
			for key in self.content[group]:
				self.checkKey(key, self.content[group][key], group)
				# check if value is empty
				if self.content[group][key] == "":
					self.warnings.append("Value of Key '%s' is empty" % key)

		# raise Warnings / Errors
		msg = ""

		if report == "All" or report == "Warnings":
			for line in self.warnings:
				msg += "\n- " + line

		if report == "All" or report == "Errors":
			for line in self.errors:
				msg += "\n- " + line

		if msg:
			raise ValidationError(msg, self.filename)

	# check if group header is valid
	def checkGroup(self, group):
		pass

	# check if key is valid
	def checkKey(self, key, value, group):
		pass

	# check random stuff
	def checkValue(self, key, value, type="string", list=False):
		if list == True:
			values = self.getList(value)
		else:
			values = [value]

		for value in values:
			if type == "string":
				code = self.checkString(value)
			elif type == "boolean":
				code = self.checkBoolean(value)
			elif type == "number":
				code = self.checkNumber(value)
			elif type == "integer":
				code = self.checkInteger(value)
			elif type == "regex":
				code = self.checkRegex(value)
			elif type == "point":
				code = self.checkPoint(value)
			if code == 1:
				self.errors.append("'%s' is not a valid %s" % (value, type))
			elif code == 2:
				self.warnings.append("Value of key '%s' is deprecated" % key)

	def checkExtras(self):
		pass

	def checkBoolean(self, value):
		# 1 or 0 : deprecated
		if (value == "1" or value == "0"):
			return 2
		# true or false: ok
		elif not (value == "true" or value == "false"):
			return 1

	def checkNumber(self, value):
		# float() ValueError
		try:
			float(value)
		except:
			return 1

	def checkInteger(self, value):
		# int() ValueError
		try:
			int(value)
		except:
			return 1

	def checkPoint(self, value):
		if not re.match("^[0-9]+,[0-9]+$", value):
			return 1

	def checkString(self, value):
		# convert to ascii
		if not value.decode("utf-8", "ignore").encode("ascii", 'ignore') == value:
			return 1

	def checkRegex(self, value):
		try:
			re.compile(value)
		except:
			return 1

	# write support
	def write(self, filename=None):
		if not filename and not self.filename:
			raise ParsingError("File not found", "")

		if filename:
			self.filename = filename
		else:
			filename = self.filename

		if not os.path.isdir(os.path.dirname(filename)):
			os.makedirs(os.path.dirname(filename))

		fp = codecs.open(filename, 'w')
		if self.defaultGroup:
			fp.write("[%s]\n" % self.defaultGroup)
			for (key, value) in self.content[self.defaultGroup].items():
				fp.write("%s=%s\n" % (key, value))
			fp.write("\n")
		for (name, group) in self.content.items():
			if name != self.defaultGroup:
				fp.write("[%s]\n" % name)
				for (key, value) in group.items():
					fp.write("%s=%s\n" % (key, value))
				fp.write("\n")
		self.tainted = False

	def set(self, key, value, group=None, locale=False):
		# set default group
		if not group:
			group = self.defaultGroup

		if locale == True and len(xdg.Locale.langs) > 0:
			key = key + "[" + xdg.Locale.langs[0] + "]"

		try:
			if isinstance(value, unicode):
				self.content[group][key] = value.encode("utf-8", "ignore")
			else:
				self.content[group][key] = value
		except KeyError:
			raise NoGroupError(group, self.filename)
		    
		self.tainted = (value == self.get(key, group))

	def addGroup(self, group):
		if self.hasGroup(group):
			if debug:
				raise DuplicateGroupError(group, self.filename)
			else:
				pass
		else:
			self.content[group] = {}
			self.tainted = True

	def removeGroup(self, group):
		existed = group in self.content
		if existed:
			del self.content[group]
			self.tainted = True
		else:
			if debug:
				raise NoGroupError(group, self.filename)
		return existed

	def removeKey(self, key, group=None, locales=True):
		# set default group
		if not group:
			group = self.defaultGroup

		try:
			if locales:
				for (name, value) in self.content[group].items():
					if re.match("^" + key + xdg.Locale.regex + "$", name) and name != key:
						value = self.content[group][name]
						del self.content[group][name]
			value = self.content[group][key]
			del self.content[group][key]
			self.tainted = True
			return value
		except KeyError, e:
			if debug:
				if e == group:
					raise NoGroupError(group, self.filename)
				else:
					raise NoKeyError(key, group, self.filename)
			else:
				return ""

	# misc
	def groups(self):
		return self.content.keys()

	def hasGroup(self, group):
		if self.content.has_key(group):
			return True
		else:
			return False

	def hasKey(self, key, group=None):
		# set default group
		if not group:
			group = self.defaultGroup

		if self.content[group].has_key(key):
			return True
		else:
			return False

	def getFileName(self):
		return self.filename
