##############################################################################
#
# Copyright (c) 2005 TINY SPRL. (http://tiny.be) All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

import tarfile
import re
import urllib2
import os
import shutil
from sets import Set

from osv import fields, osv 
import tools

class module(osv.osv):
	_name = "module.module"
	_description = "Module"
	
	def _costs_get(self, cr, uid, ids, prop=None, unknown_none=None, unknown_dict={}):
		dossiers = self.browse(cr, uid, ids)
		res = {}
		for d in dossiers:
			costs = [l.amount_costs for l in d.lot_id]
			cost_amount = reduce(lambda x, y: x+y, costs, 0.0)
			res[d.id] = cost_amount 
		return res
																			
	_columns = {
		'name': fields.char("Name", size=128, readonly=True),
		'shortdesc': fields.char('Short description', size=256, readonly=True),
		'description': fields.text("Description", readonly=True),
		'author': fields.char("Author", size=128, readonly=True),
		'website': fields.char("Website", size=256, readonly=True),
#		'running_version': fields.function(_get_installed_version, method=True, string='Installed version'),
#		'installed_version': fields.function(_get_installed_version, method=True, string='Installed version'),
		'latest_version': fields.many2one('module.module.version', 'Latest version'),
		'versions': fields.one2many('module.module.version', 'module', 'Versions', readonly=True),
		'state': fields.selection([('uninstalled', 'Uninstalled'), ('preinstall','To install'),('installed','Installed'),('running','Running')], 'State'),
#		'active': fields.boolean('Active'),
	}
	
	# update the list of available packages
	def update(self, cr, uid, ids, *args):
		vobj = self.pool.get('module.module.version')

		# update installed_version
		
		# get the index page containing available packages from the server
		index_page = urllib2.urlopen('http://www.tinyerp.org/download/modules_test').read()
#		print index_page
		# parses it
		modules = re.findall('.*<a href="([a-zA-Z0-9.\-]+)_([a-zA-Z0-9.\-]+)\.tar\.gz">.*', index_page)
		# create module.module records and module.module.version as needed
		for name, version in modules:
			print "name", name, "version", version
			# open module package on the remote server and extract its __terp__.py
			url = 'http://www.tinyerp.org/download/modules_test/' + name + '_' + version + ".tar.gz"
			tar = tarfile.open(mode="r|gz", fileobj=urllib2.urlopen(url))
			info = {}
			# we need to go through the whole tar file, because we use a stream and we
			# are not allowed to search backward in the stream, so we can't extract one
			# particular file directly
			for tarinfo in tar:
				if tarinfo.name.endswith('__terp__.py'):
					info = eval(tar.extractfile(tarinfo).read())
					break
			tar.close()
			print info
			ids = self.search(cr, uid, [('name','=',name)])
			print ids
			if not ids:
				id = self.create(cr, uid, {
					'name': name, 
					'shortdesc': info.get('name', False), 
					'description': info.get('description', False),
					'author': info.get('author', False),
					'website': info.get('website', False),
					'state': 'uninstalled',
				})
				print "module_id", id
			else:
				assert len(ids)==1, "There shouldn't be two modules with the same name"
				id = ids[0]
				
			version_ids = vobj.search(cr, uid, [('module','=',id),('name','=',version)])
			print version_ids
			if not version_ids:
				version_id = vobj.create(cr, uid, {
					'name': version, 
					'module': id,
					'state': 'uninstalled',
				})
				print "version_id", version_id
				# update latest_version
				#TODO: compute latest version
				self.write(cr, uid, [id], {'latest_version': version_id})
#			else:
#				assert len(version_ids)==1, "There shouldn't be two versions with the same name"
#				version_id = version_ids[0]
			
		return True

	def install(self, cr, uid, ids, *args):
		objs = self.browse(cr, uid, ids)
		vobj = self.pool.get('module.module.version')
		
		# get the id of latest version for each module
		version_ids = Set([o.latest_version.id for o in objs])
#		for o in objs:
#			version_ids.add()
#		version_ids = reduce(lambda dic, o: dic.update({o.latest_version.id:True}), objs, {})
		print "version_ids", version_ids
		
		# add the list of dependencies
		dependencies_ids = vobj.get_dependencies(cr, uid, list(version_ids))
		print "depends", dependencies_ids
		version_ids.update(dependencies_ids)
#		version_ids = reduce(lambda dic, dep: dic.update({dep:True}), dependencies_ids, version_ids)
		print "version_ids2", version_ids
			
		# remove existing version of modules
		self.remove(cr, uid, ids)
		
		# install all selected modules and their dependencies
		vobj.install(cr, uid, list(version_ids))
		return True
	

	# remove existing version of modules if they exist
	def remove(self, cr, uid, ids, *args):
		objs = self.browse(cr, uid, ids)
		adp = tools.config['addons_path']
		addons = os.listdir(adp)
		for o in objs:
			if o.name in addons:
				shutil.rmtree(os.path.join(adp, o.name))
		return True
module()

class module_version(osv.osv):
	_name = "module.module.version"
	_description = "Module Version"
	_columns = {
		'name': fields.char('Name', size=64),
		'module': fields.many2one('module.module', "Module"),
		'dependencies': fields.one2many('module.module.dependency', 'version', 'Dependencies'),
		'state': fields.selection([('uninstalled','Uninstalled'), ('preinstall','To install'), ('installed','Installed'), ('running','Running')], 'State'),
	}

	def install(self, cr, uid, ids, *args):
		print "install versions", ids
		objs = self.browse(cr, uid, ids)

		for o in objs:
			# download and unpack to destination folder
			url = 'http://www.tinyerp.org/download/modules_test/' + o.module.name + '_' + o.name + ".tar.gz"
			tar = tarfile.open(mode="r|gz", fileobj=urllib2.urlopen(url))
			for tarinfo in tar:
				tar.extract(tarinfo, tools.config['addons_path'])
		return True
	
	def get_dependencies(self, cr, uid, ids, *args):
		dobj = self.pool.get('module.module.dependency')
		print "get_depends", ids
		# for each dependency, get dependencies
		objs = self.browse(cr, uid, ids)
		depends = []
		for o in objs:
			print "name", o.name
			o_depends = dobj.resolve(cr, uid, [d.id for d in o.dependencies])
			print "depends", o_depends
			depends.extend(o_depends)
			
#			depends.extend([d.id for d in o.dependencies])
		print "total depends", depends
		# merge the list 
		# return the list of ids
		return depends
module_version()

# a module dependency record represents one dependency of a particular version of a module
# it can depend on a range of version of one module
class module_dependency(osv.osv):
	_name = "module.module.dependency"
	_description = "Module dependency"
	_columns = {
		'dependency_for': fields.many2one('module.module.version', 'Version'),
		'module': fields.many2one('module.module', 'Module'),
		'version_pattern': fields.char('Version pattern', size=128),
	}
	
	# returns the ids of module version records which match all dependencies
	# [version_id, ...]
	def resolve(self, cr, uid, ids):
		vobj = self.pool.get('module.module.version')
		objs = self.browse(cr, uid, ids)
		res = {}
		for o in objs:
			pattern = o.version_pattern and eval(o.version_pattern) or []
			print "pattern", pattern
			res[o.id] = vobj.search(cr, uid, [('module','=',o.module.id)]+pattern)
		#TODO: add smart dependencies resolver here
		# it should compute the best version for each module
		return [r[0] for r in res.itervalues()]
module_dependency()		
