# coding=utf-8
import sys
import os
import os.path
import codecs
import json
import gc
import imp
import re
from file import File

BASE_PATH = os.path.abspath(os.path.dirname(__file__))
is_python3 = sys.version_info[0] > 2

core_files = ['emmet-app.js', 'python-wrapper.js']

def should_use_unicode():
	"""
	WinXP unable to eval JS in unicode object (while other OSes requires it)
	This function checks if we have to use unicode when reading files
	"""
	ctx = PyV8.JSContext()
	ctx.enter()
	use_unicode = True
	try:
		ctx.eval(u'(function(){return;})()')
	except:
		use_unicode = False

	ctx.leave()

	return use_unicode

def make_path(filename):
	return os.path.normpath(os.path.join(BASE_PATH, filename))

def js_log(message):
	print(message)

def js_file_reader(file_path, use_unicode=True):
	if use_unicode:
		f = codecs.open(file_path, 'r', 'utf-8')
	else:
		f = open(file_path, 'r')

	content = f.read()
	f.close()

	return content

def import_pyv8():
	# Importing non-existing modules is a bit tricky in Python:
	# if we simply call `import PyV8` and module doesn't exists,
	# Python will cache this failed import and will always
	# throw exception even if this module appear in PYTHONPATH.
	# To prevent this, we have to manually test if 
	# PyV8.py(c) exists in PYTHONPATH before importing PyV8
	if 'PyV8' in sys.modules:
		# PyV8 was loaded by ST2, create global alias
		if 'PyV8' not in globals():
			globals()['PyV8'] = __import__('PyV8')
			
		return

	loaded = False
	f, pathname, description = imp.find_module('PyV8')
	bin_f, bin_pathname, bin_description = imp.find_module('_PyV8')
	if f:
		try:
			imp.acquire_lock()
			globals()['_PyV8'] = imp.load_module('_PyV8', bin_f, bin_pathname, bin_description)
			globals()['PyV8'] = imp.load_module('PyV8', f, pathname, description)
			imp.release_lock()
			loaded = True
		finally:
			# Since we may exit via an exception, close fp explicitly.
			if f:
				f.close()
			if bin_f:
				bin_f.close()

	if not loaded:
		raise ImportError('No PyV8 module found')
	

class Context():
	"""
	Creates Emmet JS core context.
	Before instantiating this class, make sure PyV8
	is available in `sys.path`
	
	@param files: Additional files to load with JS core
	@param path: Path to Emmet extensions
	@param contrib: Python objects to contribute to JS execution context
	@param pyv8_path: Location of PyV8 binaries
	"""
	def __init__(self, files=[], ext_path=None, contrib=None, logger=None, reader=js_file_reader):
		self.logger = logger
		self.reader = reader

		try:
			import_pyv8()
		except ImportError as e:
			pass

		self._ctx = None
		self._contrib = contrib
		self._should_load_extension = True

		# detect reader encoding
		self._use_unicode = None
		self._core_files = [] + core_files + files

		self._ext_path = None
		self.set_ext_path(ext_path)
		self._user_data = None

	def log(self, message):
		if self.logger:
			self.logger(message)
		
	def get_ext_path(self):
		return self._ext_path

	def set_ext_path(self, val):
		try:
			if val and val[:1] == '~':
				val = os.path.expanduser(val)

			val = os.path.abspath(val)
		except Exception as e:
			return

		if val == self._ext_path:
			return

		self._ext_path = val
		self.reset()

	def load_extensions(self, path=None):
		if path is None:
			path = self._ext_path;

		if path and os.path.isdir(path):
			ext_files = []
			self.log('Loading Emmet extensions from %s' % self._ext_path)
			for dirname, dirnames, filenames in os.walk(self._ext_path):
				for filename in filenames:
					if filename[0] != '.':
						ext_files.append(os.path.join(dirname, filename))

			self.js().locals.pyLoadExtensions(ext_files)

	def js(self):
		"Returns JS context"
		if not self._ctx:
			try:
				import_pyv8()
			except ImportError as e:
				return None 

			if 'PyV8' not in sys.modules:
				# Binary is not available yet
				return None

			if self._use_unicode is None:
				self._use_unicode = should_use_unicode()
			
			glue = u'\n' if self._use_unicode else '\n'
			core_src = [self.read_js_file(make_path(f)) for f in self._core_files]

			self._ctx = PyV8.JSContext()
			self._ctx.enter()
			self._ctx.eval(glue.join(core_src))

			# for f in self._core_files:
			# 	self._ctx.eval(self.read_js_file(make_path(f)), name=f, line=0, col=0)

			# load default snippets
			self._ctx.locals.pyLoadSystemSnippets(self.read_js_file(make_path('snippets.json')))

			# expose some methods
			self._ctx.locals.log = js_log
			self._ctx.locals.pyFile = File()

			if self._contrib:
				for k in self._contrib:
					self._ctx.locals[k] = self._contrib[k]
		else:
			self._ctx.enter()

		if self._should_load_extension:
			self._ctx.locals.pyResetUserData()
			self._should_load_extension = False
			self.load_extensions()

		if self._user_data:
			self._ctx.locals.pyLoadUserData(self._user_data)
			self._user_data = None

		return self._ctx

	def load_user_data(self, data):
		"Loads user data payload from JSON"
		self._user_data = data
		# self.js().locals.pyLoadUserData(data)

	def reset(self):
		"Resets JS execution context"
		if self._ctx:
			self._ctx.leave()
			self._ctx = None
			try:
				PyV8.JSEngine.collect()
				gc.collect()
			except:
				pass

		self._should_load_extension = True

	def read_js_file(self, file_path):
		return self.reader(file_path, self._use_unicode)

	def eval(self, source):
		self.js().eval(source)

	def eval_js_file(self, file_path):
		self.eval(self.read_js_file(file_path))
