#
#  Copyright (c) 2006 Pau Arumi, Bram de Jong, Mohamed Sordo
#  and Universitat Pompeu Fabra
#
# 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 os, time, sys, subprocess, tempfile
from listeners import NullResultListener, MultiListener, ConsoleResultListener
import testfarm.utils as utils
 
def is_string( data ):
	try: # TODO : find another clean way to tho this check
		data.isalpha()
		return True
	except AttributeError:
		return False
 
def get_command_and_parsers(maybe_dict):
	info_parser = None
	stats_parser = None
	status_ok_parser = None
	try:
		cmd = 'echo no command specified'
		if maybe_dict.has_key(CMD) :
			cmd = maybe_dict[CMD]
		if maybe_dict.has_key(INFO) :
			info_parser = maybe_dict[INFO]
		if maybe_dict.has_key(STATS) :
			stats_parser = maybe_dict[STATS]
		if maybe_dict.has_key(CD) : # TODO : maybe remove
			destination_dir = maybe_dict[CD]
			os.chdir( destination_dir )
		if maybe_dict.has_key(STATUS_OK) :
			status_ok_parser = maybe_dict[STATUS_OK]
	except AttributeError:
		cmd = maybe_dict
	return (cmd, info_parser, stats_parser, status_ok_parser)
 
 
def run_command(command, verbose=False):
	log = utils.buffer()
	out = log
	error = utils.quotedFile(log, "\033[31m", "\033[0m")
	message=''
	if verbose:
		message = "Running: '%s'"%command
		out = utils.tee(log, sys.stdout)
		error = utils.tee(error, sys.stderr)
 
	ok = utils.run(command, log=out, err=error, fatal=False, message=message)
	return log.output(), ok
 
 
class SubTask:
	"Defines a subtask, with a set of commands"
	def __init__(self, name, commands, mandatory = False):
		self.name = name
		self.commands = commands
		self.mandatory = mandatory
 
	def is_mandatory(self):
		"Returns if the subtask is mandatory or not"
		return self.mandatory
 
	def do_subtask(self, listener, verbose=False): #TODO : Refactor
		"Executes the subtask and all its commands"
 
		listener.listen_begin_subtask( self.name )
		initial_working_dir = os.path.abspath(os.curdir)
		temp_file = tempfile.NamedTemporaryFile()
		temp_file_name = temp_file.name
		if sys.platform =='win32':
			temp_file_name = 'C:\\testfarmtemp.txt'
		for command_definition in self.commands :
			# 1 : Create a temp file to save working directory
			cmd, info_parser, stats_parser, status_ok_parser = get_command_and_parsers(command_definition)
			pwd_cmd = 'pwd'
			if sys.platform == 'win32' : pwd_cmd = 'cd'
			cmd_with_pwd = cmd + " && %s > '%s'" %(pwd_cmd, temp_file_name)
			# 2 : Begin command run
			listener.listen_begin_command( cmd )
			output, command_ok = run_command(cmd_with_pwd, verbose=verbose)
 
			status_ok = status_ok_parser( output ) if status_ok_parser else command_ok
			info  = info_parser(output)  if info_parser  else ''
			stats = stats_parser(output) if stats_parser else {}
			if status_ok : output = ''
 
			f = open( temp_file_name )
			current_dir = f.read().strip()
			f.close()
 
			if not status_ok :
				os.chdir ( initial_working_dir )
				listener.listen_end_command( cmd, status_ok, output, info, stats )
				listener.listen_end_subtask( self.name )
				temp_file.close()
				return False
			# 3: End command run
			os.chdir ( initial_working_dir )
			listener.listen_end_command( cmd, status_ok, output, info, stats )
			if current_dir:
				os.chdir( current_dir )
		os.chdir ( initial_working_dir )
		listener.listen_end_subtask( self.name )
		temp_file.close()
		return True
 
class Task :
	# Attributes : name, subtasks[], deployment[]
	"Defines a task, with a set of subtasks"
	def __init__(self, project, client, task_name = '-- unnamed task --'):
		self.name = task_name;
		assert is_string(project.name), '< %s > is not a valid project name (should be a string)' % str(project_name)
		self.project = project
		assert is_string(client.name), '< %s > is not a valid client name (should be a string)' % str(client_name)
		self.client = client
		self.subtasks = []
		self.deployment = None #TODO : use this as unique development task
		self.not_idle_checking_cmd = ""
		self.seconds_idle = 0
		self.last_commiter=""
		self.last_revision=""
		self.repositories_to_check = []
		self.changes = []
		self.sandboxes = []
 
	# Deprecated: use add_sandbox with an XSandbox object
	def set_repositories_to_keep_state_of(self, sandboxes):
		#TODO supposes that sandboxes are in ~!!!
		from testfarm.svnsandbox import SvnSandbox
		for repo in sandboxes :
			self.sandboxes.append(SvnSandbox("~/%s"%repo))
 
	def add_sandbox(self, sandbox) :
		self.sandboxes.append(sandbox)
 
	def get_name(self):
		return self.name;
 
	def get_num_subtasks(self): # Note : Deployment task is considered as a separated task
		return len( self.subtasks )
 
	def set_check_for_new_commits(self, checking_cmd, minutes_idle = 5 ):
		"Sets the checking command and seconds to idle"
		self.not_idle_checking_cmd = checking_cmd
		self.seconds_idle = minutes_idle * 60
 
	def add_deployment(self, commands): #TODO must be unique
		"A separated subtask to deploy"
		self.add_subtask("Deployment", commands, mandatory = True)
 
	def add_subtask(self, subtaskname, commands, mandatory = False):
		"Adds a subtask"
		self.subtasks.append(SubTask(subtaskname, commands, mandatory))
 
	def _has_new_commits(self, verbose) :
		if self.not_idle_checking_cmd and not self.sandboxes :
			# No way of knowing whether has new commits, suppose always needs to be run
			return True
		# TODO: early return
		new_commits_found = False
		if self.not_idle_checking_cmd :
			output, has_changes = run_command( self.not_idle_checking_cmd, verbose=verbose )
			if has_changes :
				print "Pending commits found"
				new_commits_found = True
		for sandbox in self.sandboxes :
			if sandbox.hasPendingChanges() :
				print "Dirty: ", sandbox.sandbox
				new_commits_found = True
		return new_commits_found
 
	def do_checking_for_new_commits(self, listeners, verbose=False):
		"Checks if there is a new commit in the version control system"
		listener = MultiListener(listeners)
 
		if not self._has_new_commits(verbose) :
			listener.listen_found_new_commits( False, self.seconds_idle )
			return False
 
		for sandbox in self.sandboxes :
			for author, revision, msg in sandbox.guilty() :
				self.changes.append(
					(os.path.basename(sandbox.sandbox), revision, author))
				print os.path.basename(sandbox.sandbox), revision, author
 
		listener.listen_found_new_commits( True, self.seconds_idle )
		return True
 
	def do_subtasks( self, listeners = [ NullResultListener() ], verbose=False):
		"Executes all subtasks and sends results"
		listener = MultiListener(listeners)
		all_ok = True
		failed_subtasks = []
		listener.listen_task_info(self)
		listener.listen_begin_task( self.name, self.changes )
		if self.sandboxes :
			sub_task_name = "Update Sandbox"
			listener.listen_begin_subtask(sub_task_name)
			for sandbox in self.sandboxes :
				fake_command = "Change log for %s"%sandbox.location()
				listener.listen_begin_command(fake_command)
				output = ''.join([
					":: %s - %s\n%s\n\n"%(revision, author, message)
					for revision, author, message in sandbox.guilty()])
				listener.listen_end_command(fake_command, True, '', output, {})
			for sandbox in self.sandboxes :
				fake_command = "Updating %s, %s -> %s"%(
					sandbox.location(),
					sandbox.state(),
					sandbox.remoteState(),
					)
				listener.listen_begin_command(fake_command)
				sandbox.update()
				listener.listen_end_command(fake_command, True, '', '', {})
			listener.listen_end_subtask(sub_task_name)
 
		for subtask in self.subtasks :
			subtask_ok = subtask.do_subtask(listener, verbose=verbose)
			all_ok = all_ok and subtask_ok
			if not subtask_ok and subtask.is_mandatory() : # if it is a failing mandatory task, force the end of repository
				break
			if not subtask_ok :
				failed_subtasks.append(subtask.name) # TODO
		listener.listen_end_task( self.name, all_ok )
 
		return all_ok
 
	def stop_execution_gently(self, listeners = []): # TODO : Refactor, only for ServerListener
		"Asks listeners to stop the task execution gently if the execution was aborted by user"
		listener = MultiListener(listeners)
		listener.listen_end_task_gently(self.name)
 
CMD = 1
INFO = 2
STATS = 3
CD = 4
STATUS_OK = 5