'''Most important file in Js2Py implementation: PyJs class - father of all PyJs objects'''
from copy import copy
import re
#from translators import translator

from .translators.friendly_nodes import REGEXP_CONVERTER
from .utils.injector import fix_js_args
from types import FunctionType, ModuleType, GeneratorType, BuiltinFunctionType, MethodType, BuiltinMethodType
import traceback



# python 3 support
import six
if six.PY3:
    basestring = str
    long = int
    xrange = range
    unicode = str


def str_repr(s):
    if six.PY2:
        return repr(s.encode('utf-8'))
    else:
        return repr(s)

def MakeError(name, message):
    """Returns PyJsException with PyJsError inside"""
    return JsToPyException(ERRORS[name](Js(message)))




def to_python(val):
    if not isinstance(val, PyJs):
        return val
    if isinstance(val, PyJsUndefined) or isinstance(val, PyJsNull):
        return None
    elif isinstance(val, PyJsNumber):
        # this can be either float or long/int better to assume its int/long when a whole number...
        v = val.value
        try:
            i = int(v) if v==v else v # nan...
            return v if i!=v else i
        except:
            return v
    elif isinstance(val, (PyJsString, PyJsBoolean)):
        return val.value
    elif isinstance(val, PyObjectWrapper):
        return val.__dict__['obj']
    return JsObjectWrapper(val)

def to_dict(js_obj, known=None): # fixed recursion error in self referencing objects
    res = {}
    if known is None:
        known = {}
    if js_obj in known:
        return known[js_obj]
    known[js_obj] = res
    for k in js_obj:
        name = k.value
        input = js_obj.get(name)
        output = to_python(input)
        if isinstance(output, JsObjectWrapper):
            if output._obj.Class=='Object':
                output = to_dict(output._obj, known)
                known[input] = output
            elif output._obj.Class=='Array':
                output = to_list(output._obj)
                known[input] = output
        res[name] = output
    return res


def to_list(js_obj, known=None):
    res = len(js_obj)*[None]
    if known is None:
        known = {}
    if js_obj in known:
        return known[js_obj]
    known[js_obj] = res
    for k in js_obj:
        try:
            name = int(k.value)
        except:
            continue
        input = js_obj.get(str(name))
        output = to_python(input)
        if isinstance(output, JsObjectWrapper):
            if output._obj.Class in ['Array', 'Arguments']:
                output = to_list(output._obj, known)
                known[input] = output
            elif output._obj.Class in ['Object']:
                output = to_dict(output._obj)
                known[input] = output
        res[name] = output
    return res


def HJs(val):
    if hasattr(val, '__call__'): #
        @Js
        def PyWrapper(this, arguments, var=None):
            args = tuple(to_python(e) for e in arguments.to_list())
            try:
                py_res = val.__call__(*args)
            except Exception as e:
                message = 'your Python function failed!  '
                try:
                    message += e.message
                except:
                    pass
                raise MakeError('Error', message)
            return py_wrap(py_res)

        try:
            PyWrapper.func_name = val.__name__
        except:
            pass
        return PyWrapper
    if isinstance(val, tuple):
        val = list(val)
    return Js(val)

def Js(val):
    '''Converts Py type to PyJs type'''
    if isinstance(val, PyJs):
        return val
    elif val is None:
        return undefined
    elif isinstance(val, basestring):
        return PyJsString(val, StringPrototype)
    elif isinstance(val, bool):
        return true if val else false
    elif isinstance(val, float) or isinstance(val, int) or isinstance(val, long):
        # This is supposed to speed things up. may not be the case
        if val in NUM_BANK:
            return NUM_BANK[val]
        return PyJsNumber(float(val), NumberPrototype)
    elif isinstance(val, FunctionType):
        return PyJsFunction(val, FunctionPrototype)
    #elif isinstance(val, ModuleType):
    #    mod = {}
    #    for name in dir(val):
    #        value = getattr(val, name)
    #        if isinstance(value, ModuleType):
    #            continue  # prevent recursive module conversion
    #        try:
    #            jsval = HJs(value)
    #        except RuntimeError:
    #            print 'Could not convert %s to PyJs object!' % name
    #            continue
    #        mod[name] = jsval
    #    return Js(mod)
    #elif isintance(val, ClassType):

    elif isinstance(val, dict): # convert to object
         temp = PyJsObject({}, ObjectPrototype)
         for k, v in six.iteritems(val):
             temp.put(Js(k), Js(v))
         return temp
    elif isinstance(val, (list, tuple)): #Convert to array
        return PyJsArray(val, ArrayPrototype)
    elif isinstance(val, JsObjectWrapper):
        return val.__dict__['_obj']
    else: # try to convert to js object
        return py_wrap(val)
        #raise RuntimeError('Cant convert python type to js (%s)' % repr(val))
        #try:
        #    obj = {}
        #    for name in dir(val):
        #        if name.startswith('_'):  #dont wrap attrs that start with _
        #            continue
        #        value = getattr(val, name)
        #        import types
        #        if not isinstance(value, (FunctionType, BuiltinFunctionType, MethodType, BuiltinMethodType,
        #                                  dict, int, basestring, bool, float, long, list, tuple)):
        #            continue
        #        obj[name] = HJs(value)
        #    return Js(obj)
        #except:
        #    raise RuntimeError('Cant convert python type to js (%s)' % repr(val))

def Type(val):
    try:
        return val.TYPE
    except:
        raise RuntimeError('Invalid type: '+str(val))

def is_data_descriptor(desc):
    return desc and ('value' in desc or 'writable' in desc)
    
def is_accessor_descriptor(desc):
    return desc and ('get' in desc or 'set' in desc)
    
def is_generic_descriptor(desc):
    return desc and not (is_data_descriptor(desc) or is_accessor_descriptor(desc))



##############################################################################

class PyJs(object):
    PRIMITIVES =  ['String', 'Number', 'Boolean', 'Undefined', 'Null']
    TYPE = 'Object'
    Class = None
    extensible = True
    prototype = None
    own = {}
    GlobalObject = None
    IS_CHILD_SCOPE = False
    value = None
    
    def __init__(self, value=None, prototype=None, extensible=False):
        '''Constructor for Number String and Boolean'''
        # I dont think this is needed anymore
        # if self.Class=='String' and not isinstance(value, basestring):
        #     raise TypeError
        # if self.Class=='Number':
        #     if not isinstance(value, float):
        #         if not (isinstance(value, int) or isinstance(value, long)):
        #             raise TypeError
        #         value = float(value)
        # if self.Class=='Boolean' and not isinstance(value, bool):
        #     raise TypeError
        self.value = value
        self.extensible = extensible
        self.prototype = prototype
        self.own = {}
        
    def is_undefined(self):
        return self.Class=='Undefined'
    
    def is_null(self):
        return self.Class=='Null'
        
    def is_primitive(self):
        return self.TYPE in self.PRIMITIVES
    
    def is_object(self):
        return not self.is_primitive()

    def _type(self):
        return Type(self)
    
    def is_callable(self):
        return hasattr(self, 'call')
    
    def get_own_property(self, prop):
        return self.own.get(prop)
    
    def get_property(self, prop):
        cand = self.get_own_property(prop)
        if cand:
            return cand
        if self.prototype is not None:
            return self.prototype.get_property(prop)
    
    def get(self, prop): #external use!
         #prop = prop.value
         if self.Class=='Undefined' or self.Class=='Null':
             raise MakeError('TypeError', 'Undefiend and null dont have properties!')
         if not isinstance(prop, basestring):
             prop = prop.to_string().value
         if not isinstance(prop, basestring): raise RuntimeError('Bug')
         cand = self.get_property(prop)
         if cand is None:
             return Js(None)
         if is_data_descriptor(cand): 
             return cand['value']
         if cand['get'].is_undefined():
             return cand['get']
         return cand['get'].call(self)
    
    def can_put(self, prop):  #to check
        desc = self.get_own_property(prop)
        if desc: #if we have this property
            if is_accessor_descriptor(desc):
                return desc['set'].is_callable() # Check if setter method is defined           
            else:  #data desc 
                return desc['writable']
        if self.prototype is not None:
            return self.extensible
        inherited = self.get_property(prop)
        if inherited is None:
            return self.extensible
        if is_accessor_descriptor(inherited):
            return not inherited['set'].is_undefined()
        elif self.extensible:
            return inherited['writable']
        return False
            
    
    def put(self, prop, val, op=None):  #external use!
        '''Just like in js: self.prop op= val
           for example when op is '+' it will be self.prop+=val
           op can be either None for simple assignment or one of:
           * / % + - << >> & ^ |'''
        if self.Class=='Undefined' or self.Class=='Null':
             raise MakeError('TypeError', 'Undefiend and null dont have properties!')
        if not isinstance(prop, basestring):
             prop = prop.to_string().value
        #we need to set the value to the incremented one
        if op is not None:
            val = getattr(self.get(prop), OP_METHODS[op])(val)
        if not self.can_put(prop):
            return val
        own_desc = self.get_own_property(prop)
        if is_data_descriptor(own_desc):
            if self.Class=='Array': #only array has different define_own_prop
                self.define_own_property(prop, {'value':val})
            else:
                self.own[prop]['value'] = val
            return val
        desc = self.get_property(prop)
        if is_accessor_descriptor(desc):
            desc['set'].call(self, (val,))
        else:
            new = {'value' : val,
                   'writable' : True,
                   'configurable' : True,
                   'enumerable' : True}
            if self.Class=='Array':
                self.define_own_property(prop, new)
            else:
                self.own[prop] = new
        return val
                
    def has_property(self, prop):
        return self.get_property(prop) is not None
    
    def delete(self, prop):
        if not isinstance(prop, basestring):
            prop = prop.to_string().value
        desc = self.get_own_property(prop)
        if desc is None: 
            return Js(True)
        if desc['configurable']:
            del self.own[prop]
            return Js(True)
        return Js(False)
    
    def default_value(self, hint=None):  # made a mistake at the very early stage and made it to prefer string... caused lots! of problems
        order = ('valueOf', 'toString')
        if hint=='String' or (hint is None and self.Class=='Date'):
            order = ('toString', 'valueOf')
        for meth_name in order:
            method = self.get(meth_name)
            if method is not None and method.is_callable():
                cand = method.call(self)
                if cand.is_primitive():
                    return cand
        raise MakeError('TypeError', 'Cannot convert object to primitive value')
        

    def define_own_property(self, prop, desc): #Internal use only. External through Object
        # prop must be a Py string. Desc is either a descriptor or accessor.
        #Messy method -  raw translation from Ecma spec to prevent any bugs. # todo check this
        current = self.get_own_property(prop)

        extensible = self.extensible
        if not current: #We are creating a new property
            if not extensible:
                return False
            if is_data_descriptor(desc) or is_generic_descriptor(desc):
                DEFAULT_DATA_DESC = {'value': undefined, #undefined
                             'writable': False,
                             'enumerable': False,
                             'configurable': False}
                DEFAULT_DATA_DESC.update(desc)
                self.own[prop] = DEFAULT_DATA_DESC
            else:
                DEFAULT_ACCESSOR_DESC = {'get': undefined, #undefined
                                 'set': undefined, #undefined
                                 'enumerable': False,
                                 'configurable': False}
                DEFAULT_ACCESSOR_DESC.update(desc)
                self.own[prop] = DEFAULT_ACCESSOR_DESC
            return True
        if not desc or desc==current: #We dont need to change anything.
            return True
        configurable = current['configurable']  
        if not configurable:  #Prevent changing configurable or enumerable
            if desc.get('configurable'):
                return False
            if 'enumerable' in desc and desc['enumerable']!=current['enumerable']:
                return False
        if is_generic_descriptor(desc):
            pass
        elif is_data_descriptor(current)!=is_data_descriptor(desc):
            if not configurable:
                return False
            if is_data_descriptor(current):
                del current['value']
                del current['writable']
                current['set'] = undefined #undefined
                current['get'] = undefined #undefined
            else:
                del current['set']
                del current['get']
                current['value'] = undefined #undefined
                current['writable'] = False 
        elif is_data_descriptor(current) and is_data_descriptor(desc):
            if not configurable:
                if not current['writable'] and desc.get('writable'):
                    return False
            if not current['writable'] and 'value' in desc and current['value']!=desc['value']:
                return False
        elif is_accessor_descriptor(current) and is_accessor_descriptor(desc):
            if not configurable:
                if 'set' in desc and desc['set'] is not current['set']:
                    return False
                if 'get' in desc and desc['get'] is not current['get']:
                    return False
        current.update(desc)
        return True

    #these methods will work only for Number class
    def is_infinity(self):
        assert self.Class=='Number'
        return self.value==float('inf') or self.value==-float('inf')

    def is_nan(self):
        assert self.Class=='Number'
        return self.value!=self.value #nan!=nan evaluates to true

    def is_finite(self):
        return not (self.is_nan() or self.is_infinity())
    #Type Conversions. to_type. All must return pyjs subclass instance
    
    def to_primitive(self, hint=None):
        if self.is_primitive():
            return self
        if hint is None and (self.Class=='Number' or self.Class=='Boolean'):  # favour number for Class== Number or Boolean default = String
            hint = 'Number'
        return self.default_value(hint)
            
    def to_boolean(self):
        typ = Type(self)
        if typ=='Boolean': #no need to convert
            return self
        elif typ=='Null' or typ=='Undefined': #they are both always false
            return false
        elif typ=='Number' or typ=='String': #false only for 0, '' and NaN
            return Js(bool(self.value and self.value==self.value)) # test for nan (nan -> flase)
        else: #object -  always true
            return true

    def to_number(self):
        typ = Type(self)
        if typ=='Null':  #null is 0
            return Js(0)
        elif typ=='Undefined':  # undefined is NaN
            return NaN
        elif typ=='Boolean':    # 1 for true 0 for false
            return Js(int(self.value))
        elif typ=='Number':# or self.Class=='Number':   # no need to convert
            return self
        elif typ=='String':
            s = self.value.strip() #Strip white space
            if not s: # '' is simply 0
                return Js(0)
            if 'x' in s or 'X' in s[:3]: #hex (positive only)
                try: # try to convert
                    num = int(s, 16)
                except ValueError: # could not convert > NaN
                    return NaN
                return Js(num)
            sign = 1 #get sign
            if s[0] in '+-':
                if s[0]=='-': 
                    sign = -1
                s = s[1:]
            if s=='Infinity': #Check for infinity keyword. 'NaN' will be NaN anyway.
                return Js(sign*float('inf'))
            try: #decimal try
                num = sign*float(s) # Converted
            except ValueError:
                return NaN # could not convert to decimal  > return NaN
            return Js(num) 
        else: #object -  most likely it will be NaN.
            return self.to_primitive('Number').to_number()
            
    def to_string(self):
        typ = Type(self)
        if typ=='Null':
            return Js('null')
        elif typ=='Undefined':
            return Js('undefined')
        elif typ=='Boolean':
            return Js('true') if self.value else Js('false')
        elif typ=='Number': #or self.Class=='Number':
            if self.is_nan():
                return Js('NaN')
            elif self.is_infinity():
                sign = '-' if self.value<0 else ''
                return Js(sign+'Infinity')
            elif isinstance(self.value, long) or self.value.is_integer():  # dont print .0
                return Js(unicode(int(self.value)))
            return Js(unicode(self.value)) # accurate enough
        elif typ=='String':
            return self
        else: #object
            return self.to_primitive('String').to_string()
            
            
    def to_object(self):
        typ = self.TYPE
        if typ=='Null' or typ=='Undefined':
            raise MakeError('TypeError', 'undefined or null can\'t be converted to object')
        elif typ=='Boolean': # Unsure here... todo repair here
            return Boolean.create(self)
        elif typ=='Number': #?
            return Number.create(self)
        elif typ=='String': #? 
            return String.create(self)
        else: #object
            return self

    def to_int32(self):
        num = self.to_number()
        if num.is_nan() or num.is_infinity():
            return 0
        int32 = int(num.value) % 2**32
        return int(int32 - 2**32 if int32 >= 2**31 else int32)

    def strict_equality_comparison(self, other):
        return PyJsStrictEq(self, other)

    def cok(self):
        """Check object coercible"""
        if self.Class in ['Undefined', 'Null']:
            raise MakeError('TypeError', 'undefined or null can\'t be converted to object')

    def to_int(self):
        num = self.to_number()
        if num.is_nan():
            return 0
        elif num.is_infinity():
            return 10**20 if num.value>0 else -10**20
        return int(num.value)

    def to_uint32(self):
        num = self.to_number()
        if num.is_nan() or num.is_infinity():
            return 0
        return int(num.value) % 2**32

    def to_uint16(self):
        num = self.to_number()
        if num.is_nan() or num.is_infinity():
            return 0
        return int(num.value) % 2**16

    def to_int16(self):
        num = self.to_number()
        if num.is_nan() or num.is_infinity():
            return 0
        int16 = int(num.value) % 2**16
        return int(int16 - 2**16 if int16 >= 2**15 else int16)



    def same_as(self, other):
        typ = Type(self)
        if typ!=other.Class:
            return False
        if typ=='Undefined' or typ=='Null':
            return True
        if typ=='Boolean' or typ=='Number' or typ=='String':
            return self.value==other.value
        else: #object
            return self is other #Id compare.

    #Not to be used by translation (only internal use)
    def __getitem__(self, item):
        return self.get(str(item) if not isinstance(item, PyJs) else item.to_string().value)

    def __setitem__(self, item, value):
        self.put(str(item) if not isinstance(item, PyJs) else item.to_string().value,  Js(value))

    def __len__(self):
        try:
            return self.get('length').to_uint32()
        except:
            raise TypeError('This object (%s) does not have length property'%self.Class)
    #Oprators-------------
    #Unary, other will be implemented as functions. Increments and decrements 
    # will be methods of Number class
    def __neg__(self): #-u
        return Js(-self.to_number().value)
    
    def __pos__(self): #+u
        return self.to_number()
    
    def __invert__(self): #~u
        return Js(Js(~self.to_int32()).to_int32())
    
    def neg(self): # !u  cant do 'not u' :(
        return Js(not self.to_boolean().value)
    
    def __nonzero__(self): 
        return self.to_boolean().value

    def __bool__(self):
        return self.to_boolean().value
        
    def typeof(self): 
        if self.is_callable():
            return Js('function')
        typ = Type(self).lower()
        if typ=='null':
            typ = 'object'
        return Js(typ)
        
    #Bitwise operators
    #  <<, >>,  &, ^, |
    
    # << 
    def __lshift__(self, other):
        lnum = self.to_int32()
        rnum = other.to_uint32()
        shiftCount = rnum & 0x1F
        return Js(Js(lnum << shiftCount).to_int32())
    
    # >>
    def __rshift__(self, other):
        lnum = self.to_int32()
        rnum = other.to_uint32()
        shiftCount = rnum & 0x1F
        return Js(Js(lnum >> shiftCount).to_int32())


    # >>>
    def pyjs_bshift(self, other):
        lnum = self.to_uint32()
        rnum = other.to_uint32()
        shiftCount = rnum & 0x1F
        return Js(Js(lnum >> shiftCount).to_uint32())
     
    # & 
    def __and__(self, other):
        lnum = self.to_int32()
        rnum = other.to_int32()
        return Js(Js(lnum & rnum).to_int32())
    
    # ^
    def __xor__(self, other):
        lnum = self.to_int32()
        rnum = other.to_int32()
        return Js(Js(lnum ^ rnum).to_int32())

    # |
    def __or__(self, other):
        lnum = self.to_int32()
        rnum = other.to_int32()
        return Js(Js(lnum | rnum).to_int32())

    # Additive operators
    # + and - are implemented here
        
    # +
    def __add__(self, other):
        a = self.to_primitive()
        b = other.to_primitive()
        if a.TYPE=='String' or b.TYPE=='String':
            return Js(a.to_string().value+b.to_string().value)
        a = a.to_number()
        b = b.to_number()
        return Js(a.value+b.value)
    
    # -
    def __sub__(self, other):
        return Js(self.to_number().value-other.to_number().value)
    
    #Multiplicative operators
    # *, / and % are implemented here
    
    # *
    def __mul__(self, other):
        return Js(self.to_number().value*other.to_number().value)
        
    # /
    def __div__(self, other):
        a = self.to_number().value
        b = other.to_number().value
        if b:
            return Js(a/b)
        if not a or a!=a:
            return NaN
        return Infinity if a>0 else -Infinity
    
    # %
    def __mod__(self, other):
        a = self.to_number().value
        b = other.to_number().value
        if abs(a)==float('inf') or not b:
            return NaN
        if abs(b)==float('inf'):
            return Js(a)
        pyres = Js(a%b) #different signs in python and javascript
                        #python has the same sign as b and js has the same 
                        #sign as a.
        if a<0 and pyres.value>0:
            pyres.value -= abs(b)
        elif a>0 and pyres.value<0:
            pyres.value += abs(b)
        return Js(pyres)
        
    #Comparisons (I dont implement === and !== here, these
    # will be implemented as external functions later)
    # <, <=, !=, ==, >=, > are implemented here.
    
    def abstract_relational_comparison(self, other, self_first=True):
        ''' self<other if self_first else other<self.
           Returns the result of the question: is self smaller than other?
           in case self_first is false it returns the answer of:
                                               is other smaller than self.
           result is PyJs type: bool or undefined'''
        px = self.to_primitive('Number')
        py = other.to_primitive('Number')
        if not self_first: #reverse order
            px, py = py, px
        if not (px.Class=='String' and py.Class=='String'):
            px, py = px.to_number(), py.to_number()
            if px.is_nan() or py.is_nan():
                return undefined
            return Js(px.value<py.value) # same cmp algorithm
        else:
            # I am pretty sure that python has the same
            # string cmp algorithm but I have to confirm it
            return Js(px.value<py.value) 
        
    #<
    def __lt__(self, other): 
        res = self.abstract_relational_comparison(other, True)
        if res.is_undefined():
            return false
        return res
    
    #<=
    def __le__(self, other): 
        res = self.abstract_relational_comparison(other, False)
        if res.is_undefined():
            return false
        return res.neg() 
    
    #>=
    def __ge__(self, other): 
        res = self.abstract_relational_comparison(other, True)
        if res.is_undefined():
            return false
        return res.neg() 
    
    #>
    def __gt__(self, other): 
        res = self.abstract_relational_comparison(other, False)
        if res.is_undefined():
            return false
        return res
        
    def abstract_equality_comparison(self, other):
        ''' returns the result of JS == compare.
           result is PyJs type: bool'''
        tx, ty = self.TYPE, other.TYPE
        if tx==ty:
            if tx=='Undefined' or tx=='Null':
                return true
            if tx=='Number' or tx=='String' or tx=='Boolean':
                return Js(self.value==other.value)
            return Js(self is other) # Object
        elif (tx=='Undefined' and ty=='Null') or (ty=='Undefined' and tx=='Null'):
            return true
        elif tx=='Number' and ty=='String':
            return self.abstract_equality_comparison(other.to_number())
        elif tx=='String' and ty=='Number':
            return self.to_number().abstract_equality_comparison(other)
        elif tx=='Boolean':
            return self.to_number().abstract_equality_comparison(other)
        elif ty=='Boolean':
            return self.abstract_equality_comparison(other.to_number())
        elif (tx=='String' or tx=='Number') and other.is_object():
            return self.abstract_equality_comparison(other.to_primitive())
        elif (ty=='String' or ty=='Number') and self.is_object():
            return self.to_primitive().abstract_equality_comparison(other)
        else:
           return false
                
    #==
    def __eq__(self, other): 
        return self.abstract_equality_comparison(other)
           
    #!=
    def __ne__(self, other): 
        return self.abstract_equality_comparison(other).neg()
    
    #Other methods (instanceof)
    
    def instanceof(self, other):
        '''checks if self is instance of other'''
        if not hasattr(other, 'has_instance'):
            return false
        return other.has_instance(self)
        
    #iteration
    def __iter__(self):
        #Returns a generator of all own enumerable properties
        # since the size od self.own can change we need to use different method of iteration.
        # SLOW! New items will NOT show up.
        returned = {}
        if not self.IS_CHILD_SCOPE:
            cands = sorted(name for name in self.own if self.own[name]['enumerable'])
        else:
            cands = sorted(name for name in self.own)
        for cand in cands:
            check = self.own.get(cand)
            if check and check['enumerable']:
                yield Js(cand)

    
    def contains(self, other):
        if not self.is_object():
            raise MakeError('TypeError',"You can\'t use 'in' operator to search in non-objects")
        return Js(self.has_property(other.to_string().value))
        
    #Other Special methods
    def __call__(self, *args):
        '''Call a property prop as a function (this will be global object).
        
        NOTE: dont pass this and arguments here, these will be added
        automatically!'''
        if not self.is_callable():
            raise  MakeError('TypeError', '%s is not a function'%self.typeof())
        return self.call(self.GlobalObject, args)


    def create(self, *args):
        '''Generally not a constructor, raise an error'''
        raise  MakeError('TypeError', '%s is not a constructor'%self.Class)

    def __unicode__(self):
        return self.to_string().value

    def __repr__(self):
        if self.Class=='Object':
            res = []
            for e in self:
                res.append(str_repr(e.value)+': '+str_repr(self.get(e)))
            return '{%s}'%', '.join(res)
        elif self.Class=='String':
            return str_repr(self.value)
        elif self.Class=='Array':
            res = []
            for e in self:
                res.append(repr(self.get(e)))
            return '[%s]'%', '.join(res)
        else:
            val = str_repr(self.to_string().value)
            return val

    def _fuck_python3(self): # hack to make object hashable in python 3 (__eq__ causes problems)
        return object.__hash__(self)

    def callprop(self, prop, *args):
        '''Call a property prop as a method (this will be self).
        
        NOTE: dont pass this and arguments here, these will be added
        automatically!'''
        if not isinstance(prop, basestring):
            prop = prop.to_string().value
        cand = self.get(prop)
        if not cand.is_callable():
            raise  MakeError('TypeError','%s is not a function'%cand.typeof())
        return cand.call(self, args)

    def to_python(self):
        """returns equivalent python object.
         for example if this object is javascript array then this method will return equivalent python array"""
        return to_python(self)

    def to_py(self):
        """returns equivalent python object.
         for example if this object is javascript array then this method will return equivalent python array"""
        return self.to_python()


if six.PY3:
    PyJs.__hash__ = PyJs._fuck_python3
    PyJs.__truediv__ = PyJs.__div__
#Define some more classes representing operators:

def PyJsStrictEq(a, b):
    '''a===b'''
    tx, ty = Type(a), Type(b)
    if tx!=ty:
        return false
    if tx=='Undefined' or tx=='Null':
        return true
    if a.is_primitive(): #string bool and number case
        return Js(a.value==b.value)
    return Js(a is b) # object comparison
    
  
def PyJsStrictNeq(a, b):
    ''' a!==b'''
    return PyJsStrictEq(a, b).neg()
    
def PyJsBshift(a, b):
    """a>>>b"""
    return a.pyjs_bshift(b)


def PyJsComma(a, b):
    return b

class PyJsException(Exception):
    def __str__(self):
        if self.mes.Class=='Error':
            return self.mes.callprop('toString').value
        else:
            return unicode(self.mes)

class PyJsSwitchException(Exception): pass


PyJs.MakeError = staticmethod(MakeError)

def JsToPyException(js):
    temp = PyJsException()
    temp.mes = js
    return temp

def PyExceptionToJs(py):
    return py.mes

#Scope class it will hold all the variables accessible to user
class Scope(PyJs):
    Class = 'global'
    extensible = True
    IS_CHILD_SCOPE = True
    # todo speed up
    # in order to speed up this very important class the top scope should behave differently than
    # child scopes, child scope should not have this property descriptor thing because they cant be changed anyway
    # they are all confugurable= False

    def __init__(self, scope, closure=None):
        """Doc"""
        self.prototype = closure
        if closure is None:
            # global, top level scope
            self.own = {}
            for k, v in six.iteritems(scope):
                # set all the global items
                self.define_own_property(k, {'value': v, 'configurable': False,
                                                'writable': False, 'enumerable': False})
        else:
            # not global, less powerful but faster closure.
            self.own = scope # simple dictionary which maps name directly to js object.

    def register(self, lval):
        # registered keeps only global registered variables
        if self.prototype is None:
            # define in global scope
            if lval in self.own:
                self.own[lval]['configurable'] = False
            else:
                self.define_own_property(lval, {'value': undefined, 'configurable': False,
                                                'writable': True, 'enumerable': True})
        elif lval not in self.own:
            # define in local scope since it has not been defined yet
            self.own[lval] = undefined # default value

    def registers(self, lvals):
        """register multiple variables"""
        for lval in lvals:
            self.register(lval)

    def put(self, lval, val, op=None):
        if self.prototype is None:
            # global scope put, simple
            return PyJs.put(self, lval, val, op)
        else:
            # trying to put in local scope
            # we dont know yet in which scope we should place this var
            if lval in self.own:
                if op: # increment operation
                     val = getattr(self.own[lval], OP_METHODS[op])(val)
                self.own[lval] = val
                return val
            else:
                #try to put in the lower scope since we cant put in this one (var wasn't registered)
                return self.prototype.put(lval, val, op)

    def force_own_put(self, prop, val, configurable=False):
        if self.prototype is None: # global scope
            self.own[prop] = {'value': val, 'writable': True, 'enumerable':True, 'configurable':configurable}
        else:
            self.own[prop] = val

    def get(self, prop, throw=True):
        #note prop is always a Py String
        if not isinstance(prop, basestring):
            prop = prop.to_string().value
        if self.prototype is not None:
            # fast local scope
            cand = self.own.get(prop)
            if cand is None:
                return self.prototype.get(prop, throw)
            return cand
        # slow, global scope
        if prop not in self.own:
            if throw:
                raise MakeError('ReferenceError', '%s is not defined' % prop)
            return undefined
        return PyJs.get(self, prop)

    def delete(self, lval):
        if self.prototype is not None:
            if lval in self.own:
                return false
            return self.prototype.delete(lval)
        # we are in global scope here. Must exist and be configurable to delete
        if lval not in self.own:
            # this lval does not exist, why do you want to delete it???
            return true
        if self.own[lval]['configurable']:
            del self.own[lval]
            return true
        # not configurable, cant delete
        return false

    def pyimport(self, name, module):
        self.register(name)
        self.put(name, py_wrap(module))

    def __repr__(self):
        return u'[Object Global]'

    def to_python(self):
        return to_python(self)

class This(Scope):
    IS_CHILD_SCOPE = False
    def get(self, prop, throw=False):
        return Scope.get(self, prop, throw)

class JsObjectWrapper(object):
    def __init__(self, obj):
        self.__dict__['_obj'] = obj

    def __call__(self, *args):
        args = tuple(Js(e) for e in args)
        if '_prop_of' in self.__dict__:
            parent, meth =  self.__dict__['_prop_of']
            return to_python(parent._obj.callprop(meth, *args))
        return to_python(self._obj(*args))

    def __getattr__(self, item):
        if item == 'new' and self._obj.is_callable():
            # return instance initializer
            def PyJsInstanceInit(*args):
                args = tuple(Js(e) for e in args)
                return self._obj.create(*args).to_python()
            return PyJsInstanceInit
        cand = to_python(self._obj.get(str(item)))
        # handling method calling... obj.meth(). Value of this in meth should be self
        if isinstance(cand, self.__class__):
            cand.__dict__['_prop_of'] = self, str(item)
        return cand

    def __setattr__(self, item, value):
        self._obj.put(str(item), Js(value))

    def __getitem__(self, item):
        cand = to_python(self._obj.get(str(item)))
        if isinstance(cand, self.__class__):
            cand.__dict__['_prop_of'] = self, str(item)
        return cand

    def __setitem__(self, item, value):
        self._obj.put(str(item), Js(value))

    def __iter__(self):
        if self._obj.Class=='Array':
            return iter(self.to_list())
        elif self._obj.Class=='Object':
            return iter(self.to_dict())
        else:
            raise MakeError('TypeError', '%s is not iterable in Python' % self._obj.Class)

    def __repr__(self):
        if self._obj.is_primitive() or self._obj.is_callable():
            return repr(self._obj)
        elif self._obj.Class in ['Array', 'Arguments']:
            return repr(self.to_list())
        return repr(self.to_dict())

    def __len__(self):
        return len(self._obj)

    def __nonzero__(self):
        return bool(self._obj)

    def __bool__(self):
        return bool(self._obj)

    def to_dict(self):
        return to_dict(self.__dict__['_obj'])

    def to_list(self):
        return to_list(self.__dict__['_obj'])



class PyObjectWrapper(PyJs):
    Class = 'PyObjectWrapper'
    def __init__(self, obj):
        self.obj = obj

    def get(self, prop):
        if not isinstance(prop, basestring):
            prop = prop.to_string().value
        try:
            if prop.isdigit():
                return py_wrap(self.obj[int(prop)])
            return py_wrap(getattr(self.obj, prop))
        except:
            return undefined

    def put(self, prop, val, throw=False):
        if not isinstance(prop, basestring):
            prop = prop.to_string().value
        try:
            setattr(self.obj, prop, to_python(val))
        except AttributeError:
            raise MakeError('TypeError', 'Read only object probably...')
        return val

    def __call__(self, *args):
        py_args = tuple(to_python(e) for e in args)
        try:
            py_res = self.obj.__call__(*py_args)
        except Exception as e:
            message = 'your Python function failed!  '
            try:
                message += e.message
            except:
                pass
            raise MakeError('Error', message)
        return py_wrap(py_res)


    def callprop(self, prop, *args):
        py_args = tuple(to_python(e) for e in args)
        if not isinstance(prop, basestring):
            prop = prop.to_string().value
        return self.get(prop)(*py_args)

    def delete(self, prop):
        if not isinstance(prop, basestring):
            prop = prop.to_string().value
        try:
            if prop.isdigit():
                del self.obj[int(prop)]
            else:
                delattr(self.obj, prop)
            return true
        except:
            return false

    def __repr__(self):
        return 'PyObjectWrapper(%s)' % str(self.obj)

    def to_python(self):
        return self.obj

    def to_py(self):
        return self.obj


def py_wrap(py):
    if isinstance(py, (FunctionType, BuiltinFunctionType, MethodType, BuiltinMethodType,
                       dict, int, str, bool, float, list, tuple, long, basestring)) or py is None :
        return HJs(py)
    return PyObjectWrapper(py)



  
##############################################################################
#Define types
    
#Object
class PyJsObject(PyJs):
    Class = 'Object'
    def __init__(self, prop_descs={}, prototype=None, extensible=True):
        self.prototype = prototype
        self.extensible = extensible
        self.own = {}
        for prop, desc in six.iteritems(prop_descs):
            self.define_own_property(prop, desc)

    def __repr__(self):
        return repr(self.to_python().to_dict())

    
    
ObjectPrototype = PyJsObject()


#Function
class PyJsFunction(PyJs):
    Class = 'Function'
    def __init__(self, func, prototype=None, extensible=True, source=None):
        cand = fix_js_args(func)
        has_scope = cand is func
        func = cand
        self.argcount = six.get_function_code(func).co_argcount - 2 - has_scope
        self.code = func
        self.source = source if source else '{ [python code] }'
        self.func_name = func.__name__ if not func.__name__.startswith('PyJs_anonymous') else ''
        self.extensible = extensible
        self.prototype = prototype
        self.own = {}
        #set own property length to the number of arguments
        self.define_own_property('length', {'value': Js(self.argcount), 'writable': False,
                                            'enumerable': False, 'configurable': False})

        if self.func_name:
            self.define_own_property('name', {'value': Js(self.func_name), 'writable': False,
                                            'enumerable': False, 'configurable': True})

        # set own prototype
        proto = Js({})
        # constructor points to this function
        proto.define_own_property('constructor',{'value': self, 'writable': True,
                                                 'enumerable': False, 'configurable': True})
        self.define_own_property('prototype', {'value': proto, 'writable': True,
                                                 'enumerable': False, 'configurable': False})

    def _set_name(self, name):
        '''name is py type'''
        if self.own.get('name'):
            self.func_name = name
            self.own['name']['value'] = Js(name)

    def construct(self, *args):
        proto = self.get('prototype')
        if not proto.is_object(): # set to standard prototype
            proto = ObjectPrototype
        obj = PyJsObject(prototype=proto)
        cand = self.call(obj, *args)
        return cand if cand.is_object() else obj
    
    def call(self, this, args=()):
        '''Calls this function and returns a result 
        (converted to PyJs type so func can return python types)
        
        this must be a PyJs object and args must be a python tuple of PyJs objects.
        
        arguments object is passed automatically and will be equal to Js(args) 
        (tuple converted to arguments object).You dont need to worry about number 
        of arguments you provide if you supply less then missing ones will be set 
        to undefined (but not present in arguments object).
        And if you supply too much then excess will not be passed 
        (but they will be present in arguments object).
        '''
        if not hasattr(args, '__iter__'):  #get rid of it later
            args = (args,)
        args = tuple(Js(e) for e in args) # this wont be needed later

        arguments = PyJsArguments(args, self) # tuple will be converted to arguments object.
        arglen = self.argcount #function expects this number of args.
        if len(args)>arglen:
            args = args[0:arglen]
        elif len(args)<arglen:
            args += (undefined,)*(arglen-len(args))
        args += this, arguments  #append extra params to the arg list
        try:
            return Js(self.code(*args))
        except NotImplementedError:
            raise
        except RuntimeError as e: # maximum recursion
            raise MakeError('RangeError', e.message if not isinstance(e, NotImplementedError) else 'Not implemented!')
        
    def has_instance(self, other):
        # I am not sure here so instanceof may not work lol.
        if not other.is_object():
            return false
        proto = self.get('prototype')
        if not proto.is_object():
            raise TypeError('Function has non-object prototype in instanceof check')
        while True:
            other = other.prototype
            if not other:  # todo make sure that the condition is not None or null
                return false
            if other is proto:
                return true

    def create(self, *args):
        proto = self.get('prototype')
        if not proto.is_object():
            proto = ObjectPrototype
        new = PyJsObject(prototype=proto)
        res = self.call(new, args)
        if res.is_object():
            return res
        return new

class PyJsBoundFunction(PyJsFunction):
    def __init__(self, target, bound_this, bound_args):
        self.target = target
        self.bound_this = bound_this
        self.bound_args = bound_args
        self.argcount = target.argcount
        self.code = target.code
        self.source = target.source
        self.func_name = target.func_name
        self.extensible = True
        self.prototype = FunctionPrototype
        self.own = {}
        # set own property length to the number of arguments
        self.define_own_property('length', {'value': target.get('length')-Js(len(self.bound_args)), 'writable': False,
                                            'enumerable': False, 'configurable': False})

        if self.func_name:
            self.define_own_property('name', {'value': Js(self.func_name), 'writable': False,
                                              'enumerable': False, 'configurable': True})

        # set own prototype
        proto = Js({})
        # constructor points to this function
        proto.define_own_property('constructor', {'value': self, 'writable': True,
                                                  'enumerable': False, 'configurable': True})
        self.define_own_property('prototype', {'value': proto, 'writable': True,
                                               'enumerable': False, 'configurable': False})


    def call(self, this, args=()):
        return self.target.call(self.bound_this, self.bound_args+args)

    def has_instance(self, other):
        return self.target.has_instance(other)

PyJs.PyJsBoundFunction = PyJsBoundFunction

OP_METHODS = {'*': '__mul__',
              '/': '__div__',
              '%': '__mod__',
              '+': '__add__',
              '-': '__sub__',
              '<<': '__lshift__',
              '>>': '__rshift__',
              '&': '__and__',
              '^': '__xor__',
              '|': '__or__',
              '>>>': 'pyjs_bshift'}

def Empty():
    return Js(None)

#Number
class PyJsNumber(PyJs):  #Note i dont implement +0 and -0. Just 0.
    TYPE = 'Number'
    Class = 'Number'



NumberPrototype = PyJsObject({}, ObjectPrototype)
NumberPrototype.Class = 'Number'
NumberPrototype.value = 0

Infinity = PyJsNumber(float('inf'), NumberPrototype)
NaN = PyJsNumber(float('nan'), NumberPrototype)
PyJs.NaN = NaN
PyJs.Infinity = Infinity

# This dict aims to increase speed of string creation by storing character instances
CHAR_BANK = {}
NUM_BANK = {}
PyJs.CHAR_BANK = CHAR_BANK
#String
# Different than implementation design in order to improve performance
#for example I dont create separate property for each character in string, it would take ages.
class PyJsString(PyJs):
    TYPE = 'String'
    Class = 'String'
    extensible = False
    def __init__(self, value=None, prototype=None):
        '''Constructor for Number String and Boolean'''
        if not isinstance(value, basestring):
            raise TypeError # this will be internal error
        self.value = value
        self.prototype = prototype
        self.own = {}
         # this should be optimized because its mych slower than python str creation (about 50 times!)
        # Dont create separate properties for every index. Just
        self.own['length'] = {'value': Js(len(value)), 'writable': False,
                             'enumerable': False, 'configurable': False}
        if len(value)==1:
            CHAR_BANK[value] = self #, 'writable': False,
                               # 'enumerable': True, 'configurable': False}

    def get(self, prop):
        if not isinstance(prop, basestring):
                prop = prop.to_string().value
        try:
            index = int(prop)
            if index<0:
                return undefined
            char = self.value[index]
            if char not in CHAR_BANK:
                Js(char) # this will add char to CHAR BANK
            return CHAR_BANK[char]
        except Exception:
            pass
        return PyJs.get(self, prop)

    def can_put(self, prop):
        return False

    def __iter__(self):
        for i in xrange(len(self.value)):
            yield Js(i)  # maybe create an int bank?


StringPrototype = PyJsObject({}, ObjectPrototype)
StringPrototype.Class = 'String'
StringPrototype.value = ''

CHAR_BANK[''] = Js('')

#Boolean
class PyJsBoolean(PyJs):
    TYPE = 'Boolean'
    Class = 'Boolean'

BooleanPrototype = PyJsObject({}, ObjectPrototype)
BooleanPrototype.Class = 'Boolean'
BooleanPrototype.value = False

true = PyJsBoolean(True, BooleanPrototype)
false = PyJsBoolean(False, BooleanPrototype)


#Undefined
class PyJsUndefined(PyJs):
    TYPE = 'Undefined'
    Class = 'Undefined'
    def __init__(self):
        pass

undefined = PyJsUndefined()

#Null
class PyJsNull(PyJs):
    TYPE = 'Null'
    Class = 'Null'
    def __init__(self):
        pass
null = PyJsNull()
PyJs.null = null

class PyJsArray(PyJs):
    Class = 'Array'
    def __init__(self, arr=[], prototype=None):
        self.extensible = True
        self.prototype = prototype
        self.own = {'length' : {'value': Js(0), 'writable': True,
                                            'enumerable': False, 'configurable': False}}
        for i, e in enumerate(arr):
            self.define_own_property(str(i), {'value': Js(e), 'writable': True,
                                              'enumerable': True, 'configurable': True})

    def define_own_property(self, prop, desc):
        old_len_desc = self.get_own_property('length')
        old_len = old_len_desc['value'].value  #  value is js type so convert to py.
        if prop=='length':
            if 'value' not in desc:
                return PyJs.define_own_property(self, prop, desc)
            new_len =  desc['value'].to_uint32()
            if new_len!=desc['value'].to_number().value:
                raise MakeError('RangeError', 'Invalid range!')
            new_desc = {}
            for k,v in desc.iteritems():
            	new_desc[k]=v 
            new_desc['value'] = Js(new_len)
            if new_len>=old_len:
                return PyJs.define_own_property(self, prop, new_desc)
            if not old_len_desc['writable']:
                return False
            if 'writable' not in new_desc or new_desc['writable']==True:
                new_writable = True
            else:
                new_writable = False
                new_desc['writable'] = True
            if not PyJs.define_own_property(self, prop, new_desc):
                return False
            if new_len<old_len:
                # not very efficient for sparse arrays, so using different method for sparse:
                if old_len>30*len(self.own):
                    for ele in self.own.keys():
                        if ele.isdigit() and int(ele)>=new_len:
                            if not self.delete(ele): # if failed to delete set len to current len and reject.
                                new_desc['value'] = Js(old_len+1)
                                if not new_writable:
                                    new_desc['writable'] = False
                                PyJs.define_own_property(self, prop, new_desc)
                                return False
                    old_len = new_len
                else: # standard method
                    while new_len<old_len:
                        old_len -= 1
                        if not self.delete(str(int(old_len))): # if failed to delete set len to current len and reject.
                            new_desc['value'] = Js(old_len+1)
                            if not new_writable:
                                new_desc['writable'] = False
                            PyJs.define_own_property(self, prop, new_desc)
                            return False
            if not new_writable:
                self.own['length']['writable'] = False
            return True
        elif prop.isdigit():
            index = int(int(prop) % 2**32)
            if index>=old_len and not old_len_desc['writable']:
                return False
            if not PyJs.define_own_property(self, prop, desc):
                return False
            if index>=old_len:
                old_len_desc['value'] = Js(index + 1)
            return True
        else:
            return PyJs.define_own_property(self, prop, desc)

    def to_list(self):
        return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())]

    def __repr__(self):
        return repr(self.to_python().to_list())





ArrayPrototype = PyJsArray([], ObjectPrototype)

class PyJsArguments(PyJs):
    Class = 'Arguments'
    def __init__(self, args, callee):
        self.own = {}
        self.extensible = True
        self.prototype = ObjectPrototype
        self.define_own_property('length', {'value': Js(len(args)), 'writable': True,
                                            'enumerable': False, 'configurable': True})
        self.define_own_property('callee', {'value': callee, 'writable': True,
                                            'enumerable': False, 'configurable': True})
        for i, e in enumerate(args):
            self.put(str(i), Js(e))

    def to_list(self):
        return [self.get(str(e)) for e in xrange(self.get('length').to_uint32())]


#We can define function proto after number proto because func uses number in its init
FunctionPrototype = PyJsFunction(Empty, ObjectPrototype)
FunctionPrototype.own['name']['value'] = Js('')


# I will not rewrite RegExp engine from scratch. I will use re because its much faster.
# I have to only make sure that I am handling all the differences correctly.
REGEXP_DB = {}

class PyJsRegExp(PyJs):
    Class = 'RegExp'
    extensible = True

    def __init__(self, regexp, prototype=None):

        self.prototype = prototype
        self.glob = False
        self.ignore_case = 0
        self.multiline = 0
        # self._cache = {'str':'NoStringEmpty23093',
        #                'iterator': None,
        #                'lastpos': -1,
        #                'matches': {}}
        flags = ''
        if not regexp[-1]=='/':
            #contains some flags (allowed are i, g, m
            spl =  regexp.rfind('/')
            flags = set(regexp[spl+1:])
            self.value = regexp[1:spl]
            if 'g' in flags:
                self.glob = True
            if 'i' in flags:
                self.ignore_case = re.IGNORECASE
            if 'm' in flags:
                self.multiline = re.MULTILINE
        else:
            self.value = regexp[1:-1]

        try:
            if self.value in REGEXP_DB:
                self.pat = REGEXP_DB[regexp]
            else:
                comp =  'None'
                # we have to check whether pattern is valid.
                # also this will speed up matching later
                # todo critical fix patter conversion etc. ..!!!!!
                # ugly hacks porting js reg exp to py reg exp works in 99% of cases ;)
                possible_fixes = [
                    (u'[]', u'[\0]'),
                    (u'[^]', u'[^\0]'),
                    (u'nofix1791', u'nofix1791')
                ]
                reg = self.value
                for fix, rep in possible_fixes:
                    comp = REGEXP_CONVERTER._interpret_regexp(reg, flags)
                    #print 'reg -> comp', reg, '->', comp
                    try:
                        self.pat = re.compile(comp, self.ignore_case | self.multiline)
                        #print reg, '->', comp
                        break
                    except:
                        reg = reg.replace(fix, rep)
                       # print 'Fix', fix, '->', rep, '=', reg
                else:
                    raise
                REGEXP_DB[regexp] = self.pat
        except:
            #print 'Invalid pattern but fuck it', self.value, comp
            raise MakeError('SyntaxError', 'Invalid RegExp pattern: %s -> %s'% (repr(self.value), repr(comp)))
        # now set own properties:
        self.own = {'source' : {'value': Js(self.value), 'enumerable': False, 'writable': False, 'configurable': False},
                    'global' : {'value': Js(self.glob), 'enumerable': False, 'writable': False, 'configurable': False},
                    'ignoreCase' : {'value': Js(bool(self.ignore_case)), 'enumerable': False, 'writable': False, 'configurable': False},
                    'multiline' : {'value': Js(bool(self.multiline)), 'enumerable': False, 'writable': False, 'configurable': False},
                    'lastIndex' : {'value': Js(0), 'enumerable': False, 'writable': True, 'configurable': False}}

    def match(self, string, pos):
        '''string is of course py string'''
        return self.pat.match(string, pos)  # way easier :)
        # assert 0<=pos <= len(string)
        # if not pos:
        #     return re.match(self.pat, string)
        # else:
        #     if self._cache['str']==string:
        #         if pos>self._cache['lastpos']:
        #             for m in self._cache['iterator']:
        #                 start = m.start()
        #                 self._cache['lastpos'] = start
        #                 self._cache['matches'][start] = m
        #                 if start==pos:
        #                     return m
        #                 elif start>pos:
        #                     return None
        #             self._cache['lastpos'] = len(string)
        #             return None
        #         else:
        #             return self._cache['matches'].get(pos)
        #     else:
        #         self._cache['str'] = string
        #         self._cache['matches'] = {}
        #         self._cache['lastpos'] = -1
        #         self._cache['iterator'] = re.finditer(self.pat, string)
        #         return self.match(string, pos)



def JsRegExp(source):
    # Takes regexp literal!
    return PyJsRegExp(source, RegExpPrototype)

RegExpPrototype = PyJsRegExp('/(?:)/', ObjectPrototype)

####Exceptions:
default_attrs = {'writable':True, 'enumerable':False, 'configurable':True}


def fill_in_props(obj, props, default_desc):
    for prop, value in props.items():
        default_desc['value'] = Js(value)
        obj.define_own_property(prop, default_desc)



class PyJsError(PyJs):
    Class = 'Error'
    extensible = True
    def __init__(self, message=None, prototype=None):
        self.prototype = prototype
        self.own = {}
        if message is not None:
            self.put('message', Js(message).to_string())
            self.own['message']['enumerable'] = False

ErrorPrototype = PyJsError(Js(''), ObjectPrototype)
@Js
def Error(message):
    return PyJsError(None if message.is_undefined() else message, ErrorPrototype)
Error.create = Error
err = {'name': 'Error',
       'constructor': Error}
fill_in_props(ErrorPrototype, err, default_attrs)
Error.define_own_property('prototype', {'value': ErrorPrototype,
                                        'enumerable': False,
                                        'writable': False,
                                        'configurable': False})

def define_error_type(name):
    TypeErrorPrototype = PyJsError(None, ErrorPrototype)
    @Js
    def TypeError(message):
        return PyJsError(None if message.is_undefined() else message, TypeErrorPrototype)
    err = {'name': name,
           'constructor': TypeError}
    fill_in_props(TypeErrorPrototype, err, default_attrs)
    TypeError.define_own_property('prototype', {'value': TypeErrorPrototype,
                                                'enumerable': False,
                                                'writable': False,
                                                'configurable': False})
    ERRORS[name] = TypeError

ERRORS = {'Error': Error}
ERROR_NAMES = ['Eval', 'Type', 'Range', 'Reference', 'Syntax', 'URI']

for e in ERROR_NAMES:
    define_error_type(e+'Error')


##############################################################################
# Import and fill prototypes here.

#this works only for data properties
def fill_prototype(prototype, Class, attrs, constructor=False):
    for i in dir(Class):
        e = getattr(Class, i)
        if six.PY2:
            if hasattr(e, '__func__'):
                temp = PyJsFunction(e.__func__, FunctionPrototype)
                attrs = {}
            	for k,v in attrs.iteritems():
            		attrs[k]=v 
                attrs['value'] = temp
                prototype.define_own_property(i, attrs)
        else:
            if hasattr(e, '__call__') and not i.startswith('__'):
                temp = PyJsFunction(e, FunctionPrototype)
                attrs = {}
            	for k,v in attrs.iteritems():
            		attrs[k]=v 
                attrs['value'] = temp
                prototype.define_own_property(i, attrs)
        if constructor:
            attrs['value'] = constructor
            prototype.define_own_property('constructor', attrs)
            



PyJs.undefined = undefined
PyJs.Js = staticmethod(Js)

from .prototypes import jsfunction, jsobject, jsnumber, jsstring, jsboolean, jsarray, jsregexp, jserror


#Object proto
fill_prototype(ObjectPrototype, jsobject.ObjectPrototype, default_attrs)
#Define __proto__ accessor (this cant be done by fill_prototype since)
@Js
def __proto__():
    return this.prototype if this.prototype is not None else null
getter = __proto__
@Js
def __proto__(val):
    if val.is_object():
        this.prototype = val
setter =  __proto__
ObjectPrototype.define_own_property('__proto__', {'set': setter,
                                                  'get': getter,
                                                  'enumerable': False,
                                                  'configurable':True})


#Function proto
fill_prototype(FunctionPrototype, jsfunction.FunctionPrototype, default_attrs)
#Number proto
fill_prototype(NumberPrototype, jsnumber.NumberPrototype, default_attrs)
#String proto
fill_prototype(StringPrototype, jsstring.StringPrototype, default_attrs)
#Boolean proto
fill_prototype(BooleanPrototype, jsboolean.BooleanPrototype, default_attrs)
#Array proto
fill_prototype(ArrayPrototype, jsarray.ArrayPrototype, default_attrs)
#Error proto
fill_prototype(ErrorPrototype, jserror.ErrorPrototype, default_attrs)
#RegExp proto
fill_prototype(RegExpPrototype, jsregexp.RegExpPrototype, default_attrs)
# add exec to regexpfunction (cant add it automatically because of its name :(
RegExpPrototype.own['exec'] = RegExpPrototype.own['exec2']
del RegExpPrototype.own['exec2']

#########################################################################
# Constructors

# String
@Js
def String(st):
    if not len(arguments):
        return Js('')
    return arguments[0].to_string()

@Js
def string_constructor():
    temp = PyJsObject(prototype=StringPrototype)
    temp.Class = 'String'
    #temp.TYPE = 'String'
    if not len(arguments):
        temp.value = ''
    else:
        temp.value = arguments[0].to_string().value
        for i, ch in enumerate(temp.value): # this will make things long...
            temp.own[str(i)] = {'value': Js(ch), 'writable': False,
                                'enumerable': True, 'configurable': True}
    temp.own['length'] = {'value': Js(len(temp.value)), 'writable': False,
                          'enumerable': False, 'configurable': False}
    return temp

String.create = string_constructor

# RegExp
REG_EXP_FLAGS = ['g', 'i', 'm']
@Js
def RegExp(pattern, flags):
    if pattern.Class=='RegExp':
        if not flags.is_undefined():
            raise  MakeError('TypeError', 'Cannot supply flags when constructing one RegExp from another')
        # return unchanged
        return pattern
    #pattern is not a regexp
    if pattern.is_undefined():
        pattern = ''
    else:
        pattern = pattern.to_string().value
        # try:
        #     pattern = REGEXP_CONVERTER._unescape_string(pattern.to_string().value)
        # except:
        #     raise MakeError('SyntaxError', 'Invalid regexp')
    flags = flags.to_string().value if not flags.is_undefined() else ''
    for flag in flags:
        if flag not in REG_EXP_FLAGS:
            raise MakeError('SyntaxError', 'Invalid flags supplied to RegExp constructor "%s"' % flag)
    if len(set(flags))!=len(flags):
        raise MakeError('SyntaxError', 'Invalid flags supplied to RegExp constructor "%s"' % flags)
    pattern  = '/%s/'%(pattern if pattern else '(?:)') + flags
    return JsRegExp(pattern)

RegExp.create = RegExp
PyJs.RegExp = RegExp

# Number

@Js
def Number():
    if len(arguments):
        return arguments[0].to_number()
    else:
        return Js(0)

@Js
def number_constructor():
    temp = PyJsObject(prototype=NumberPrototype)
    temp.Class = 'Number'
    #temp.TYPE = 'Number'
    if len(arguments):
        temp.value = arguments[0].to_number().value
    else:
        temp.value = 0
    return temp

Number.create = number_constructor

# Boolean

@Js
def Boolean(value):
    return value.to_boolean()
@Js
def boolean_constructor(value):
    temp = PyJsObject(prototype=BooleanPrototype)
    temp.Class = 'Boolean'
    #temp.TYPE = 'Boolean'
    temp.value = value.to_boolean().value
    return temp

Boolean.create = boolean_constructor


##############################################################################

def appengine(code):
    try:
        return translator.translate_js(code.decode('utf-8'))
    except:
        return traceback.format_exc()
        
        
        
builtins = ('true','false','null','undefined','Infinity',
            'NaN')

scope = dict(zip(builtins, [eval(e) for e in builtins]))

JS_BUILTINS = {}
for k,v in scope.iteritems():
	JS_BUILTINS[k]=v


# Fill in NUM_BANK
for e in xrange(-2**10,2**14):
    NUM_BANK[e] = Js(e)

if __name__=='__main__':
    print(ObjectPrototype.get('toString').callprop('call'))
    print(FunctionPrototype.own)
    a=  null-Js(49404)
    x = a.put('ser', Js('der'))
    print(Js(0) or Js('p') and Js(4.0000000000050000001))
    FunctionPrototype.put('Chuj', Js(409))
    for e in FunctionPrototype:
        print('Obk', e.get('__proto__').get('__proto__').get('__proto__'), e)
    import code
    s = Js(4)
    b = Js(6)

    s2 = Js(4)
    o =  ObjectPrototype
    o.put('x', Js(100))
    var = Scope(scope)
    e = code.InteractiveConsole(globals())
    #e.raw_input = interactor
    e.interact()


