#*****************************************************************************
#   Copyright 2004-2008 Steve Menard
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#	   http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#   
#*****************************************************************************
import _jpype, _jexception, _jcollection
from _pykeywords import KEYWORDS
import sets


_CLASSES = {}

_SPECIAL_CONSTRUCTOR_KEY = "This is the special constructor key"

_JAVAOBJECT = None
_JAVATHROWABLE = None
_COMPARABLE = None
_RUNTIMEEXCEPTION = None

_CUSTOMIZERS = []

_COMPARABLE_METHODS = { 
	"__cmp__" : lambda self, o : self.compareTo(o)
}

def _initialize() :
	global _COMPARABLE, _JAVAOBJECT, _JAVATHROWABLE, _RUNTIMEEXCEPTION
	
	import _jpackage
	_JAVAOBJECT = JClass("java.lang.Object")
	_JAVATHROWABLE = JClass("java.lang.Throwable")
	_RUNTIMEEXCEPTION = JClass("java.lang.RuntimeException")
	_jpype.setJavaLangObjectClass(_JAVAOBJECT)
	_jpype.setGetClassMethod(_getClassFor)
	_jpype.setSpecialConstructorKey(_SPECIAL_CONSTRUCTOR_KEY)

def registerClassCustomizer(c) :
	_CUSTOMIZERS.append(c)
	
def JClass(name) :
	jc = _jpype.findClass(name)
	if jc is None :
		raise _RUNTIMEEXCEPTION.PYEXC("Class %s not found" % name)
		
	return _getClassFor(jc)

def _getClassFor(javaClass) :
	name = javaClass.getName()
	if _CLASSES.has_key(name) :
		return _CLASSES[name]
		
	pyJavaClass = _JavaClass(javaClass)
	_CLASSES[name] = pyJavaClass
	return pyJavaClass
	
def _javaNew(self, *args) :
	return object.__new__(self)
	
def _javaExceptionNew(self, *args) :
	return Exception.__new__(self)

def _javaInit(self, *args) :
	object.__init__(self)
	
	if len(args) == 1 and isinstance(args[0], tuple) and args[0][0] is _SPECIAL_CONSTRUCTOR_KEY :
		self.__javaobject__ = args[0][1]
	else:
		self.__javaobject__ = self.__class__.__javaclass__.newClassInstance(*args)
	
def _javaGetAttr(self, name) :
	try :
		r = object.__getattribute__(self, name)
	except AttributeError, ex :
		if name in dir(self.__class__.__metaclass__) :
			r = object.__getattribute__(self.__class__, name)
		else:
			raise ex
		
	if isinstance(r, _jpype._JavaMethod) :
		return _jpype._JavaBoundMethod(r, self) 
	return r

class _JavaClass(type) :  
	def __new__(mcs, jc) :
		bases = []
		name = jc.getName()
		
		static_fields = {}
		constants = []
		members = {
			"__javaclass__" : jc,
			"__init__" : _javaInit,
			"__str__" : lambda self : self.toString(),
			"__hash__" : lambda self : self.hashCode(),
			"__eq__" : lambda self, o : self.equals(o),
			"__ne__" : lambda self, o : not self.equals(o),
			"__getattribute__" : _javaGetAttr,
		}
		
		if name == 'java.lang.Object' or jc.isPrimitive():
			bases.append(object)
		elif not jc.isInterface() :
			bjc = jc.getBaseClass(jc)
			bases.append( _getClassFor(bjc) )
		
		if _JAVATHROWABLE is not None and jc.isSubclass("java.lang.Throwable") :
			members["PYEXC"] = _jexception._makePythonException(name, bjc)

		itf = jc.getBaseInterfaces()
		for ic in itf :
			bases.append( _getClassFor(ic) )
		
		if len(bases) == 0 :
			bases.append(_JAVAOBJECT)
		
		# add the fields	
		fields = jc.getClassFields()
		for i in fields :
			fname = i.getName()
			if fname in KEYWORDS :
				fname = fname + "_"
				
			if i.isStatic() :
				g = lambda self, fld=i : fld.getStaticAttribute()
				s = None
				if not i.isFinal() :
					s = lambda self, v, fld=i : fld.setStaticAttribute(v)
				static_fields[fname] = property(g, s)
			else:
				g = lambda self, fld=i : fld.getInstanceAttribute(self.__javaobject__)
				s = None
				if not i.isFinal() :
					s = lambda self, v, fld=i : fld.setInstanceAttribute(self.__javaobject__, v)
				members[fname] = property(g, s)
		
		# methods
		methods = jc.getClassMethods() # return tuple of tuple (name, method)
		for jm in methods :
			mname = jm.getName()
			if mname in KEYWORDS :
				mname = mname + "_"
								
			members[mname] = jm

		for i in _CUSTOMIZERS :
			if i.canCustomize(name, jc) :
				i.customize(name, jc, bases, members)
		
		# remove multiple bases that would cause a MRO problem
		toRemove = sets.Set()
		for c in bases :
			for d in bases :
				if c == d :
					continue
				if issubclass(c, d) :
					toRemove.add(d)
					
		for i in toRemove :
			bases.remove(i)

		# Prepare the meta-metaclass
		meta_bases = []
		for i in bases :
			if i is object :
				meta_bases.append(mcs)
			else:
				meta_bases.append(i.__metaclass__)
				
		metaclass = type.__new__(type, name+"$$Static", tuple(meta_bases), static_fields)
		members['__metaclass__'] = metaclass
		result =  type.__new__(metaclass, name, tuple(bases), members)
		
		return result
				
