#!BPY
""" Released under the Blender Artistic Licence (BAL)
Name: 'Browse Repository'
Blender: 244
Group: 'Materials'
Tooltip: 'Browse the Open Material Library'
"""
__author__ = "pat"
__version__ = 9 # 2009-02-19
__email__ = 'Author, pat:psycho3d*de'
__url__ = ["Author's website, http://www.psycho3d.de", "Repository, http://matrep.parastudios.de"]
__bpydoc__ = "No help yet. Just start and click your way through."

from Blender import Draw
try:
	import os
	from urllib import urlopen, urlretrieve, urlencode
	from shutil import copyfile # fallback for rename
except ImportError:
	print 'Error: You need a full python install to use this script.'
	Draw.PupMenu('Error%t|You need a full python install.')
else:
	from Blender import Window, BGL as gl, Image, Library, Get
	
	# globals
	catbuttons = {} #list of buttons to be drawn
	matbuttons = {} # and also kind of a cache
	screen = 0 #screen type to be drawn
	matinfo = None
	preview = 1
	btnSearch = Draw.Create("")
	bFetch = 0
	usr = ""
	pwd = ""
	versioncache = 0
	# constants
	repository = "http://matrep.parastudios.de/"
	debug = 0
	
	
	class MatInfo:
		def __init__(self,id,data):
			self.cacheSphere = 0
			self.cacheSuze = 0
			self.cacheCustom = 0
			self.id = id
			self.title = data[0].strip(" '")
			self.previewSphere = ''.join((repository, "mats/fullsize/", data[1]))
			self.description = data[2]
			self.uploader = data[3]
			if (int(data[5]) == 0):
				self.rating = 0
			else:
				self.rating = float(data[4]) / int(data[5])
			self.rates = int(data[5])
			self.uploadDate = data[6]
			self.previewSuze = ''.join((repository, "mats/fullsize/", data[7]))
			self.previewCustom = ''.join((repository, "mats/fullsize/", data[8]))
			self.downloads = data[9]
			if data[10] == '1':
				self.raytracing = "Yes"
			else:
				self.raytracing = "No"
			if data[11] == '1':
				self.images = "Yes"
			else:
				self.images = "No"
			if data[12] == '1':
				self.particles = "Yes"
			else:
				self.particles = "No"
			if data[13] == '1':
				self.sss = "Yes"
			else:
				self.sss = "No"
			if data[14] == '1':
				self.nodes = "Yes"
			else:
				self.nodes = "No"
			if data[15] == '1':
				self.volumetrics = "Yes"
			else:
				self.volumetrics = "No"
			if data[16] == '1':
				self.glsl = "Yes"
			else:
				self.glsl = "No"
		
		def __del__(self): # cleaning up cached images
			try:
				if self.cacheSphere:
					os.remove(self.previewSphere)
				if self.cacheSuze:
					os.remove(self.previewSuze)
				if self.cacheCustom:
					os.remove(self.previewCustom)
			except:
				pass
	
		def getItems(self):
			return [
			"Title: %s" % self.title,
			"Author: %s" % self.uploader,
			"Rating: %.2f (%i ratings)" % (round(self.rating,2), self.rates),
			"Sent online: %s" % self.uploadDate,
			"Downloads: %s" % self.downloads,
			"Raytracing: %s" % self.raytracing, 
			"Image Textures: %s" % self.images,
			"Particles: %s" % self.particles,
			"SSS: %s" % self.sss,
			"Nodes: %s" % self.nodes,
			"Volumetrics: %s" % self.volumetrics,
			"GLSL: %s" % self.glsl,
			]
	
		def getDescription(self):
			"""
			returns the description for the current material
			"""
			return "Description: %s" % self.description
	
		def getPreview(self, number):
			"""
			gets the preview filename/url
			"""
			if debug: print 'getPreview',
			# replace .preview for some kind of cache
			if number == 1:
				url = self.previewSphere
				if debug: print url
				if url.endswith("/"): return None
				if self.cacheSphere:
					filename = url
				else:
					filename, headers = urlretrieve(url)
					self.previewSphere = filename
					self.cacheSphere = 1
			elif number == 2:
				url = self.previewSuze
				if debug: print url
				if url.endswith("/"): return None
				if self.cacheSuze:
					filename = url
				else:
					filename, headers = urlretrieve(url)
					self.previewSuze = filename
					self.cacheSuze = 1
			else:
				url = self.previewCustom
				if debug: print url
				if url.endswith("/"): return None
				if self.cacheCustom:
					filename = url
				else:
					filename, headers = urlretrieve(url)
					self.previewCustom = filename
					self.cacheCustom = 1
			return filename
	
	
	def readVersion():
		"""
		get the version number of the latest available version, cached
		"""
		global versioncache
		if versioncache == 0:
			version = urlopen(''.join((repository, "communicator.php")))
			versioncache = int(version.read())
		return versioncache
	
	
	def selfupdate():
		"""
		tries to replace the script file with latest version, downloaded from the server
		"""
		if Get('uscriptsdir') != None:
			scriptsdir = Get('uscriptsdir')
		else:
			scriptsdir = Get('scriptsdir')
		
		filename, headers = urlretrieve(''.join((repository, "repobrowser.py")))
		
		currentscript = os.path.join(scriptsdir,'repobrowser.py')
		rename=os.rename
		
		try:
			# backup the old script
			rename(currentscript, currentscript + '_')
		except:
			print 'Error: Failed to backup the current script version. Please update manually or try to change permissions for %s and %s.' % (scriptsdir, currentscript)
			Draw.PupMenu('Failed to update.%t|See console window for details.')
		else:
			# then move the new version to it's place and delete the backup
			try:
				rename(filename, currentscript)
				os.remove(currentscript + '_')
				print 'Success: Update complete. Please restart the script or Blender.'
				Draw.PupMenu('Success%t|Update complete, please restart.')
			except OSError:
				# fallback for cross-device rename
				copyfile(filename, currentscript)
				os.remove(currentscript + '_')
				os.remove(filename)
				print 'Success: Update complete. Please restart the script or Blender.'
				Draw.PupMenu('Success%t|Update complete, please restart.')

		
	
	def help(): # hotkey H
		help = urlopen(''.join((repository, "communicator.php?help")))
		for line in help.readlines():
			print line[:-1] #strip newline
	
	
	
	##########################
	# browsing functionality #
	##########################
	def readCats():
		global catbuttons, screen, bFetch
	
		if debug: print "reading categories"
		bFetch = 1
		Draw.Draw()
		screen = 1
	
		cats = urlopen(''.join((repository, "communicator.php?c=list")))
		catlist = []
		for line in cats.readlines():
			catlist.append(line.split(",",1))
	
		catbuttons = {}
		i = 0
		dc = Draw.Create
		for cat in catlist:
			catbuttons[i] = (int(cat[0]), dc(cat[1][1:-2]))
			i += 1
		bFetch = 0
		Draw.Draw()
	
	
	
	def readMats(id):
		global matbuttons, screen, bFetch
	
		if debug: print "reading materials (id %i)" % id
		bFetch = 1
		Draw.Draw()
		screen = 2
	
		mats = urlopen(''.join((repository, "communicator.php?c=%i" % id)))
		matlist = []
		for line in mats.readlines():
			matlist.append(line.split(","))
	
		matbuttons = {}
		i = 0
		dc = Draw.Create
		for mat in matlist:
			matbuttons[i] = (int(mat[0]), dc(mat[1][1:-1]))
			i += 1
		bFetch = 0
		Draw.Draw()
	
	
	
	def readMatInfo(id):
		global matinfo, screen, preview, bFetch
	
		if debug: print "reading info of material %i" % id
		bFetch = 1
		Draw.Draw()
		screen = 3
		preview = 1
	
		mats = urlopen(''.join((repository, "communicator.php?m=%i" % id)))
		matline = ""
		for line in mats.readlines():
			matline += ' ' + line.rstrip()
		
		del(matinfo)
		matinfo = MatInfo(id, matline.strip().split("','"))
		bFetch = 0
		Draw.Draw()
	
	
	
	def searchMats():
		global screen, matbuttons, bFetch
	
		if debug: print "reading materials (searched)"
		bFetch = 1
		Draw.Draw()
		screen = 2
	
		get = urlencode({"s":btnSearch.val})
	
		mats = urlopen(''.join((repository, "communicator.php?%s" % get)))
		matlist = []
		for line in mats.readlines():
			matlist.append(line.split(","))

		matbuttons = {}
		i = 0
		dc = Draw.Create
		for mat in matlist:
			matbuttons[i] = (int(mat[0]), dc(mat[1][1:-1]))
			i += 1
		bFetch = 0
		Draw.Draw()
	
	
	
	def readFavs(usr, pwd):
		global screen, matbuttons, bFetch
	
		if debug: print "reading favorites"
		bFetch = 1
		Draw.Draw()
		screen = 2
	
		post = urlencode({"user": usr, "pw": pwd})
	
		mats = urlopen(''.join((repository, "communicator.php?fav=")), post)
		matlist = []
		for line in mats.readlines():
			matlist.append(line.split(","))

		matbuttons = {}
		i = 0
		dc = Draw.Create
		for mat in matlist:
			matbuttons[i] = (int(mat[0]), dc(mat[1][1:-1]))
			i += 1
		bFetch = 0
		Draw.Draw()
		
		
	
	def importblendfile(id):
		global bFetch
	
		if debug: print "downloading material %i" % id
		bFetch = 1
		Draw.Draw()
		filename, headers = urlretrieve(''.join((repository, 'download.php?mat=%i' % id)))
		try:
			Library.Open(filename)
			if debug: print filename
			if 'Material' in Library.LinkableGroups():
				mats = Library.Datablocks('Material')
				for mat in mats:
					Library.Load(mat, 'Material',0)
				Library.Update()
			else:
				print 'No materials found.'
				Draw.PupMenu('Error%t|No materials found.')
		except IOError:
			print 'Downloaded file not found. This should never happen?'
			Draw.PupMenu('Error%t|Downloaded file not found.')
		else:
			try:
				os.remove(filename)
			except:
				pass # win seems to lock the file
		bFetch = 0
		Draw.Draw()
	
	
	
	############################
	# give me something to see #
	############################
	def gui():
		if debug:
			from time import time
			print 'Redraw: ', time()
		
		global buttons, btnSearch, screen, preview
		col = Window.Theme.Get()[0].get("buts").back
		gl.glClearColor(col[0]/255.,col[1]/255.,col[2]/255.,col[3]/255.)
		gl.glClear(gl.GL_COLOR_BUFFER_BIT)
		col = Window.Theme.Get()[0].get("buts").text
		gl.glColor3f(col[0]/255.,col[1]/255.,col[2]/255.)
	
		awidth,aheight = Window.GetAreaSize()
		
		# shortcuts
		db = Draw.Button
		dt = Draw.Text
		gl2i = gl.glRasterPos2i
	
		""" ToDo
		description umbrechen wenn text in einer zeile zu lang
		"""
		if bFetch == 1:
			gl2i(awidth / 2 - 41, aheight / 2)
			dt("Downloading...")
		else:
			if screen == 1 and len(catbuttons) == 0:
				screen = 0 #this may occur with searching
			
			if screen == 0: #index
				gl2i(10, 195)
				latestversion = readVersion()
				dt('Visit the repository under matrep.parastudios.de')
				gl2i(10, 180)
				dt('to see our news and recent materials')
				if __version__ < latestversion:
					gl2i(10, 150)
					dt("Press V to update your script.")
					gl2i(10, 130)
					dt("Press Q to quit")
				else:
					gl2i(10, 150)
					dt("Press U to get up one level in hierarchy")
					gl2i(10, 130)
					dt("Press Q to quit")
					btnSearch = Draw.String("Search: ", 0, 10, 90, awidth - 20, 20, btnSearch.val, 50)
					db("Search Repository", 2, 10, 70, awidth - 20, 20)
					db("Browse your Favorites", 3, 10, 40, awidth - 20, 20)
					db("Browse Repository", 1, 10, 10, awidth - 20, 20)
			elif screen == 1: #show cats
				width,height = awidth - 20, 20
				incY = 25
				i = 0
				split = 999
				if len(catbuttons) * 30 > aheight - 10:
					# too much buttons
					rows = len(catbuttons)*30. / (aheight - 10)
					if rows > int(rows):
						rows += 1
					rows = int(rows)
					split = len(catbuttons) * 1. / rows
					if split > int(split):
						split += 1
					split = int(split)
					width = awidth / rows - 20
	
				row = 1
				for key in catbuttons.keys():
					if i == split:
						i = 0
						row += 1
					db(catbuttons[key][1].val, catbuttons[key][0] + 10, 10 + (20 + width) * (row - 1), 10+i*incY, width, height)
					i += 1
			elif screen == 2: # show mats
				width,height = awidth - 20, 20
				incY = 25
				i = 0 # for y positioning
	
				split = 999
				if len(matbuttons) * 30 > aheight - 10:
					# too much buttons
					rows = len(matbuttons)*30. / (aheight - 10)
					if rows > int(rows):
						rows += 1
					rows = int(rows)
					split = len(matbuttons) * 1. / rows
					if split > int(split):
						split += 1
					split = int(split)
					width = awidth / rows - 20
	
				row = 1
				for key in matbuttons.keys():
					if i == split:
						i = 0
						row += 1
					db(matbuttons[key][1].val, matbuttons[key][0] + 10, 10 + (20 + width) * (row - 1), 10+i*incY, width, height)
					i += 1
	
				if len(matbuttons) == 0:
					gl2i(awidth / 2 - 92, aheight / 2)
					dt("Nothing found. Use U to get back")
	
			elif screen == 3: # material details
				# the 4 buttons
				width, height = awidth/4 - 20, 15
				y = aheight - 20
				db("Sphere", 6, 10, y, width, height)
				db("Suzanne", 7, 30 + width, y, width, height)
				db("Custom", 8, 50 + 2*width, y, width, height)
				db("Download", 9, 70 + 3*width, y, width, height)
				
				# preview image
				imgfile = matinfo.getPreview(preview)
				if imgfile != None:
					try:
						imgobj = Image.Load(imgfile)
						Draw.Image(imgobj, 10, 10)
					except IOError:
						gl2i(awidth / 2 - 100, aheight / 2)
						dt("Error opening the image. Please retry.")
				else:
					gl2i(110, 124)
					dt("No image.")
				
				# info text
				minfo = matinfo.getItems()
				minfo.extend(matinfo.getDescription().split("~"))
				i = len(minfo) - 1
				y = i * 15 + 15
				while i >= 0:
					gl2i(270, y - i * 15)
					dt(minfo[i])
					i -= 1
				
		
	
	
	def event(evt, val):
		if not val: return
		if evt == Draw.QKEY: # quit
			global matinfo
			del(matinfo)
			Draw.Exit()
		elif evt == Draw.HKEY: # developer help
			help()
		elif evt == Draw.VKEY: # version update
			selfupdate()
		elif evt == Draw.UKEY: # Up
			global screen
			if screen > 0:
				screen -= 1
			Draw.Redraw()
	
	
	def button(evt):
		global preview, matinfo, bFetch, usr, pwd
		if evt == 0:
			pass
		elif evt == 1: # browse button
			readCats()
		elif evt == 2: # search button
			searchMats()
		elif evt == 3: # fav button
			if usr == "":
				# request username and pass
				_u = Draw.Create("")
				_p = Draw.Create("")
				block = []
				block.append(("Username: ",_u,0,50))
				block.append(("Password: ",_p,0,50))
				if Draw.PupBlock("Login", block):
					usr = _u.val
					pwd = _p.val
				del(block)
				del(_u)
				del(_p)
			if usr != "":
				readFavs(usr, pwd)
		elif evt == 9: # download
			matinfo.downloads = int(matinfo.downloads) + 1
			importblendfile(matinfo.id)
		elif evt < 10: # preview buttons
			bFetch = 1
			Draw.Draw()
			preview = evt - 5
			matinfo.getPreview(preview) # download and cache
			bFetch = 0
			Draw.Draw()
		else:
			if screen == 1:
				readMats(evt - 10)
			elif screen == 2:
				readMatInfo(evt - 10)
	
	Draw.Register(gui,event,button)