#! /usr/bin/env python
# encoding: utf-8

import os,shlex,sys,time
try:import cPickle
except ImportError:import pickle as cPickle
import Environment,Utils,Options,Logs
from Logs import warn
from Constants import*
try:
	from urllib import request
except:
	from urllib import urlopen
else:
	urlopen=request.urlopen
conf_template='''# project %(app)s configured on %(now)s by
# waf %(wafver)s (abi %(abi)s, python %(pyver)x on %(systype)s)
# using %(args)s
#
'''
class ConfigurationError(Utils.WscriptError):
	pass
autoconfig=False
def find_file(filename,path_list):
	for directory in Utils.to_list(path_list):
		if os.path.exists(os.path.join(directory,filename)):
			return directory
	return''
def find_program_impl(env,filename,path_list=[],var=None,environ=None):
	if not environ:
		environ=os.environ
	try:path_list=path_list.split()
	except AttributeError:pass
	if var:
		if env[var]:return env[var]
		if var in environ:env[var]=environ[var]
	if not path_list:path_list=environ.get('PATH','').split(os.pathsep)
	ext=(Options.platform=='win32')and'.exe,.com,.bat,.cmd'or''
	for y in[filename+x for x in ext.split(',')]:
		for directory in path_list:
			x=os.path.join(directory,y)
			if os.path.isfile(x):
				if var:env[var]=x
				return x
	return''
class ConfigurationContext(Utils.Context):
	tests={}
	error_handlers=[]
	def __init__(self,env=None,blddir='',srcdir=''):
		self.env=None
		self.envname=''
		self.environ=dict(os.environ)
		self.line_just=40
		self.blddir=blddir
		self.srcdir=srcdir
		self.all_envs={}
		self.cwd=self.curdir=os.getcwd()
		self.tools=[]
		self.setenv(DEFAULT)
		self.lastprog=''
		self.hash=0
		self.files=[]
		self.tool_cache=[]
		if self.blddir:
			self.post_init()
	def post_init(self):
		self.cachedir=os.path.join(self.blddir,CACHE_DIR)
		path=os.path.join(self.blddir,WAF_CONFIG_LOG)
		try:os.unlink(path)
		except(OSError,IOError):pass
		try:
			self.log=open(path,'w')
		except(OSError,IOError):
			self.fatal('could not open %r for writing'%path)
		app=Utils.g_module.APPNAME
		if app:
			ver=getattr(Utils.g_module,'VERSION','')
			if ver:
				app="%s (%s)"%(app,ver)
		now=time.ctime()
		pyver=sys.hexversion
		systype=sys.platform
		args=" ".join(sys.argv)
		wafver=WAFVERSION
		abi=ABI
		self.log.write(conf_template%vars())
	def __del__(self):
		if hasattr(self,'log')and self.log:
			self.log.close()
	def fatal(self,msg):
		raise ConfigurationError(msg)
	def check_tool(self,input,tooldir=None,funs=None):
		tools=Utils.to_list(input)
		if tooldir:tooldir=Utils.to_list(tooldir)
		for tool in tools:
			tool=tool.replace('++','xx')
			if tool=='java':tool='javaw'
			if tool.lower()=='unittest':tool='unittestw'
			mag=(tool,id(self.env),funs)
			if mag in self.tool_cache:
				continue
			self.tool_cache.append(mag)
			module=None
			try:
				module=Utils.load_tool(tool,tooldir)
			except Exception,e:
				ex=e
				if Options.options.download:
					_3rdparty=os.path.normpath(Options.tooldir[0]+os.sep+'..'+os.sep+'3rdparty')
					for x in Utils.to_list(Options.remote_repo):
						for sub in['branches/waf-%s/wafadmin/3rdparty'%WAFVERSION,'trunk/wafadmin/3rdparty']:
							url='/'.join((x,sub,tool+'.py'))
							try:
								web=urlopen(url)
								if web.getcode()!=200:
									continue
							except Exception,e:
								continue
							else:
								loc=None
								try:
									loc=open(_3rdparty+os.sep+tool+'.py','wb')
									loc.write(web.read())
									web.close()
								finally:
									if loc:
										loc.close()
								Logs.warn('downloaded %s from %s'%(tool,url))
								try:
									module=Utils.load_tool(tool,tooldir)
								except:
									Logs.warn('module %s from %s is unusable'%(tool,url))
									try:
										os.unlink(_3rdparty+os.sep+tool+'.py')
									except:
										pass
									continue
						else:
							break
					if not module:
						Logs.error('Could not load the tool %r or download a suitable replacement from the repository (sys.path %r)\n%s'%(tool,sys.path,e))
						raise ex
				else:
					Logs.error('Could not load the tool %r in %r (try the --download option?):\n%s'%(tool,sys.path,e))
					raise ex
			if funs is not None:
				self.eval_rules(funs)
			else:
				func=getattr(module,'detect',None)
				if func:
					if type(func)is type(find_file):func(self)
					else:self.eval_rules(func)
			self.tools.append({'tool':tool,'tooldir':tooldir,'funs':funs})
	def sub_config(self,k):
		self.recurse(k,name='configure')
	def pre_recurse(self,name_or_mod,path,nexdir):
		return{'conf':self,'ctx':self}
	def post_recurse(self,name_or_mod,path,nexdir):
		if not autoconfig:
			return
		self.hash=hash((self.hash,getattr(name_or_mod,'waf_hash_val',name_or_mod)))
		self.files.append(path)
	def store(self,file=''):
		if not os.path.isdir(self.cachedir):
			os.makedirs(self.cachedir)
		if not file:
			file=open(os.path.join(self.cachedir,'build.config.py'),'w')
		file.write('version = 0x%x\n'%HEXVERSION)
		file.write('tools = %r\n'%self.tools)
		file.close()
		if not self.all_envs:
			self.fatal('nothing to store in the configuration context!')
		for key in self.all_envs:
			tmpenv=self.all_envs[key]
			tmpenv.store(os.path.join(self.cachedir,key+CACHE_SUFFIX))
	def set_env_name(self,name,env):
		self.all_envs[name]=env
		return env
	def retrieve(self,name,fromenv=None):
		try:
			env=self.all_envs[name]
		except KeyError:
			env=Environment.Environment()
			env['PREFIX']=os.path.abspath(os.path.expanduser(Options.options.prefix))
			self.all_envs[name]=env
		else:
			if fromenv:warn("The environment %s may have been configured already"%name)
		return env
	def setenv(self,name):
		self.env=self.retrieve(name)
		self.envname=name
	def add_os_flags(self,var,dest=None):
		try:self.env.append_value(dest or var,Utils.to_list(self.environ[var]))
		except KeyError:pass
	def check_message_1(self,sr):
		self.line_just=max(self.line_just,len(sr))
		for x in('\n',self.line_just*'-','\n',sr,'\n'):
			self.log.write(x)
		Utils.pprint('NORMAL',"%s :"%sr.ljust(self.line_just),sep='')
	def check_message_2(self,sr,color='GREEN'):
		self.log.write(sr)
		self.log.write('\n')
		Utils.pprint(color,sr)
	def check_message(self,th,msg,state,option=''):
		sr='Checking for %s %s'%(th,msg)
		self.check_message_1(sr)
		p=self.check_message_2
		if state:p('ok '+str(option))
		else:p('not found','YELLOW')
	def check_message_custom(self,th,msg,custom,option='',color='PINK'):
		sr='Checking for %s %s'%(th,msg)
		self.check_message_1(sr)
		self.check_message_2(custom,color)
	def msg(self,msg,result,color=None):
		self.start_msg('Checking for '+msg)
		if not isinstance(color,str):
			color=result and'GREEN'or'YELLOW'
		self.end_msg(result,color)
	def start_msg(self,msg):
		try:
			if self.in_msg:
				return
		except:
			self.in_msg=0
		self.in_msg+=1
		self.line_just=max(self.line_just,len(msg))
		for x in('\n',self.line_just*'-','\n',msg,'\n'):
			self.log.write(x)
		Utils.pprint('NORMAL',"%s :"%msg.ljust(self.line_just),sep='')
	def end_msg(self,result,color):
		self.in_msg-=1
		if self.in_msg:
			return
		if not color:
			color='GREEN'
		if result==True:
			msg='ok'
		elif result==False:
			msg='not found'
			color='YELLOW'
		else:
			msg=str(result)
		self.log.write(msg)
		self.log.write('\n')
		Utils.pprint(color,msg)
	def find_program(self,filename,path_list=[],var=None,mandatory=False):
		ret=None
		if var:
			if self.env[var]:
				ret=self.env[var]
			elif var in os.environ:
				ret=os.environ[var]
		if not isinstance(filename,list):filename=[filename]
		if not ret:
			for x in filename:
				ret=find_program_impl(self.env,x,path_list,var,environ=self.environ)
				if ret:break
		self.check_message_1('Checking for program %s'%' or '.join(filename))
		self.log.write('  find program=%r paths=%r var=%r\n  -> %r\n'%(filename,path_list,var,ret))
		if ret:
			Utils.pprint('GREEN',str(ret))
		else:
			Utils.pprint('YELLOW','not found')
			if mandatory:
				self.fatal('The program %r is required'%filename)
		if var:
			self.env[var]=ret
		return ret
	def cmd_to_list(self,cmd):
		if isinstance(cmd,str)and cmd.find(' '):
			try:
				os.stat(cmd)
			except OSError:
				return shlex.split(cmd)
			else:
				return[cmd]
		return cmd
	def __getattr__(self,name):
		r=self.__class__.__dict__.get(name,None)
		if r:return r
		if name and name.startswith('require_'):
			for k in['check_','find_']:
				n=name.replace('require_',k)
				ret=self.__class__.__dict__.get(n,None)
				if ret:
					def run(*k,**kw):
						r=ret(self,*k,**kw)
						if not r:
							self.fatal('requirement failure')
						return r
					return run
		self.fatal('No such method %r'%name)
	def eval_rules(self,rules):
		self.rules=Utils.to_list(rules)
		for x in self.rules:
			f=getattr(self,x)
			if not f:self.fatal("No such method '%s'."%x)
			try:
				f()
			except Exception,e:
				ret=self.err_handler(x,e)
				if ret==BREAK:
					break
				elif ret==CONTINUE:
					continue
				else:
					self.fatal(e)
	def err_handler(self,fun,error):
		pass
def conf(f):
	setattr(ConfigurationContext,f.__name__,f)
	return f
def conftest(f):
	ConfigurationContext.tests[f.__name__]=f
	return conf(f)

