# Licensed to the .NET Foundation under one or more agreements.
# The .NET Foundation licenses this file to you under the Apache 2.0 License.
# See the LICENSE file in the project root for more information.


#
# PART 2. how IronPython choose the overload methods
#

import unittest

from iptest import IronPythonTestCase, is_cli, run_test, skipUnlessIronPython
from iptest.type_util import array_int, array_byte, array_object, myint, mystr, types

class PT_int_old:
    def __int__(self): return 200

class PT_int_new(object):
    def __int__(self): return 300

def _self_defined_method(name): return len(name) == 4 and name[0] == "M"

def _result_pair(s, offset=0):
    fn = s.split()
    val = [int(x[1:]) + offset for x in fn]
    return dict(zip(fn, val))

def _first(s): return _result_pair(s, 0)
def _second(s): return _result_pair(s, 100)

def _merge(*args):
    ret = {}
    for arg in args:
        for (k, v) in arg.iteritems(): ret[k] = v
    return ret

def _my_call(func, arg):
    if isinstance(arg, tuple):
        l = len(arg)
        if l == 0: func()
        elif l == 1: func(arg[0])
        elif l == 2: func(arg[0], arg[1])
        elif l == 3: func(arg[0], arg[1], arg[2])
        elif l == 4: func(arg[0], arg[1], arg[2], arg[3])
        elif l == 5: func(arg[0], arg[1], arg[2], arg[3], arg[4])
        elif l == 6: func(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5])
        else: func(*arg)
    else:
        func(arg)

if is_cli:
    import clr
    import System
    clrRefInt = clr.Reference[int](0)
    UInt32Max = System.UInt32.MaxValue
    Byte10   = System.Byte.Parse('10')
    SBytem10 = System.SByte.Parse('-10')
    Int1610  = System.Int16.Parse('10')
    Int16m20 = System.Int16.Parse('-20')
    UInt163  = System.UInt16.Parse('3')

    arrayInt = array_int((10, 20))
    tupleInt = ((10, 20), )
    listInt  = ([10, 20], )
    tupleLong1, tupleLong2  = ((10L, 20L), ), ((System.Int64.MaxValue, System.Int32.MaxValue * 2),)
    arrayByte = array_byte((10, 20))
    arrayObj = array_object(['str', 10])


@skipUnlessIronPython()
class MethodBinder2Test(IronPythonTestCase):
    def setUp(self):
        super(MethodBinder2Test, self).setUp()

        self.load_iron_python_test()
        import System
        from IronPythonTest.BinderTest import I, C1, C3

        class PT_I(I): pass

        class PT_C1(C1): pass

        class PT_C3_int(C3):
            def __int__(self): return 1

        class PT_I_int(I):
            def __int__(self): return 100

        self.pt_i = PT_I()
        self.pt_c1 = PT_C1()
        self.pt_i_int = PT_I_int()
        self.pt_int_old = PT_int_old()
        self.pt_int_new = PT_int_new()


    def _try_arg(self, target, arg, mapping, funcTypeError, funcOverflowError, verbose=False):
        '''try the pass-in argument 'arg' on all methods 'target' has.
        mapping specifies (method-name, flag-value)
        funcOverflowError contains method-name, which will cause OverflowError when passing in 'arg'
        '''
        from IronPythonTest.BinderTest import Flag

        if verbose: print arg,
        for funcname in dir(target):
            if not _self_defined_method(funcname) : continue
            
            if verbose: print funcname,
            func = getattr(target, funcname)

            if funcname in funcOverflowError: expectError = OverflowError
            elif funcname in funcTypeError:  expectError = TypeError
            else: expectError = None

            if isinstance(arg, types.lambdaType):
                arg = arg()
            try:
                _my_call(func, arg)
            except Exception, e:
                if expectError == None:
                    self.fail("unexpected exception %s when func %s with arg %s (%s)\n%s" % (e, funcname, arg, type(arg), func.__doc__))

                if funcname in mapping.keys():  # No exception expected:
                    self.fail("unexpected exception %s when func %s with arg %s (%s)\n%s" % (e, funcname, arg, type(arg), func.__doc__))

                if not isinstance(e, expectError):
                    self.fail("expect '%s', but got '%s' (flag %s) when func %s with arg %s (%s)\n%s" % (expectError, e, Flag.Value, funcname, arg, type(arg), func.__doc__))
            else:
                if not funcname in mapping.keys(): # Expecting exception
                    self.fail("expect %s, but got no exception (flag %s) when func %s with arg %s (%s)\n%s" % (expectError, Flag.Value, funcname, arg, type(arg), func.__doc__))
                
                left, right = Flag.Value, mapping[funcname]
                if left != right:
                    self.fail("left %s != right %s when func %s on arg %s (%s)\n%s" % (left, right, funcname, arg, type(arg), func.__doc__))
                Flag.Value = -99           # reset
        if verbose: print
    
    def test_other_concerns(self):
        from IronPythonTest.BinderTest import C1, C3, COtherOverloadConcern, Flag
        target = COtherOverloadConcern()
        
        # the one asking for Int32 is private
        target.M100(100)
        self.assertEqual(Flag.Value, 200); Flag.Value = 99
        
        # static / instance
        target.M110(target, 100)
        self.assertEqual(Flag.Value, 110); Flag.Value = 99
        COtherOverloadConcern.M110(100)
        self.assertEqual(Flag.Value, 210); Flag.Value = 99
        
        self.assertRaises(TypeError, COtherOverloadConcern.M110, target, 100)
        
        # static / instance 2
        target.M111(100)
        self.assertEqual(Flag.Value, 111); Flag.Value = 99
        COtherOverloadConcern.M111(target, 100)
        self.assertEqual(Flag.Value, 211); Flag.Value = 99
            
        self.assertRaises(TypeError, target.M111, target, 100)
        self.assertRaises(TypeError, COtherOverloadConcern.M111, 100)
        
        # statics
        target.M120(target, 100)
        self.assertEqual(Flag.Value, 120); Flag.Value = 99
        target.M120(100)
        self.assertEqual(Flag.Value, 220); Flag.Value = 99

        COtherOverloadConcern.M120(target, 100)
        self.assertEqual(Flag.Value, 120); Flag.Value = 99
        COtherOverloadConcern.M120(100)
        self.assertEqual(Flag.Value, 220); Flag.Value = 99
        
        # generic
        target.M130(100)
        self.assertEqual(Flag.Value, 130); Flag.Value = 99

        target.M130(100.1234)
        self.assertEqual(Flag.Value, 230); Flag.Value = 99

        target.M130(C1())
        self.assertEqual(Flag.Value, 230); Flag.Value = 99

        for x in [100, 100.1234]:
            target.M130[int](x)
            self.assertEqual(Flag.Value, 230); Flag.Value = 99

        class PT_C3_int(C3):
            def __int__(self): return 1
        
        # narrowing levels and __int__ conversion
        target.M140(PT_C3_int(), PT_C3_int())
        self.assertEqual(Flag.Value, 140); Flag.Value = 99



######### generated python code below #########

    def test_arg_ClrReference(self):
        import clr
        from IronPythonTest.BinderTest import C1, C2, COverloads_ClrReference
        target = COverloads_ClrReference()
        for (arg, mapping, funcTypeError, funcOverflowError) in [
            (lambda :                         None, _merge(_first('M100 M101 M107 '), _second('M102 M104 M105 M106 ')), 'M103 ', '', ),
            (lambda :      clr.Reference[object](None), _second('M100 M104 M105 M107 '), 'M101 M102 M103 M104 M106 ', '', ),
            (lambda :  clr.Reference[object](None), _second('M100 M104 M105 M107 '), 'M101 M102 M103 M106 ', '', ),
            (lambda :        clr.Reference[int](9), _merge(_first('M100 M102 M103 M104 '), _second('M105 M107 ')), 'M101 M106 ', '', ),
            (lambda :    clr.Reference[bool](True), _merge(_first('M100 M105 '), _second('M101 M102 M104 M107 ')), 'M103 M106 ', '', ),
            (lambda : clr.Reference[type](complex), _merge(_first('M100 '), _second('M104 M105 M107 ')), 'M101 M102 M103 M106 ', '', ),
            (lambda :      clr.Reference[C1](C1()), _merge(_first('M100 M106 M107 '), _second('M104 M105 ')), 'M101 M102 M103 ', '', ),
            (lambda :      clr.Reference[C1](C2()), _merge(_first('M100 M106 M107 '), _second('M104 M105 ')), 'M101 M102 M103 ', '', ),
            ]:
            self._try_arg(target, arg, mapping, funcTypeError, funcOverflowError)

    def test_arg_NoArgNecessary(self):
        from IronPythonTest.BinderTest import COverloads_NoArgNecessary
        target = COverloads_NoArgNecessary()
        for (arg, mapping, funcTypeError, funcOverflowError) in [
            (     tuple(), _merge(_first('M100 M101 M102 M105 '), _second('M103 M104 M106 ')), '', '', ),
            (         100, _merge(_first('M105 M106 '), _second('M101 M102 M103 M104 ')), 'M100 ', '', ),
            (  (100, 200), _second('M102 M104 M105 M106 '), 'M100 M101 M103 ', '', ),
            (   clrRefInt, _merge(_first('M103 M104 '), _second('M100 ')), 'M101 M102 M105 M106 ', '', ),
            ]:
            self._try_arg(target, arg, mapping, funcTypeError, funcOverflowError)

    def test_arg_OneArg_NormalArg(self):
        from IronPythonTest.BinderTest import COverloads_OneArg_NormalArg
        target = COverloads_OneArg_NormalArg()
        for (arg, mapping, funcTypeError, funcOverflowError) in [
            (     tuple(), dict(), 'M100 M101 M102 M103 M104 M105 M106 M107 M108 M109 ', '', ),
            (         100, _first('M100 M101 M102 M103 M104 M105 M106 M107 M108 M109 '), '', '', ),
            (  (100, 200), _second('M102 M107 M108 '), 'M100 M101 M103 M104 M105 M106 M109 ', '', ),
            (   clrRefInt, _second('M100 '), 'M101 M102 M103 M104 M105 M106 M107 M108 M109 ', '', ),
            ]:
            self._try_arg(target, arg, mapping, funcTypeError, funcOverflowError)

    def test_arg_OneArg_RefArg(self):
        from IronPythonTest.BinderTest import COverloads_OneArg_RefArg
        target = COverloads_OneArg_RefArg()
        for (arg, mapping, funcTypeError, funcOverflowError) in [
            (     tuple(), dict(), 'M100 M101 M102 M103 M104 M105 M106 M107 M108 ', '', ),
            (         100, _merge(_first('M100 M101 M103 M105 M108 '), _second('M106 M107 ')), 'M102 M104 ', '', ),
            (  (100, 200), _second('M101 M106 M107 '), 'M100 M102 M103 M104 M105 M108 ', '', ),
            (   clrRefInt, _first('M100 M101 M102 M103 M104 M105 M106 M107 M108 '), '', '', ),
            ]:
            self._try_arg(target, arg, mapping, funcTypeError, funcOverflowError)

    def test_arg_OneArg_NullableArg(self):
        from IronPythonTest.BinderTest import COverloads_OneArg_NullableArg
        target = COverloads_OneArg_NullableArg()
        for (arg, mapping, funcTypeError, funcOverflowError) in [
            (     tuple(), dict(), 'M100 M101 M102 M103 M104 M105 M106 M107 ', '', ),
            (         100, _merge(_first('M100 M107 '), _second('M101 M102 M103 M104 M105 M106 ')), '', '', ),
            (  (100, 200), _second('M100 M105 M106 '), 'M101 M102 M103 M104 M107 ', '', ),
            ]:
            self._try_arg(target, arg, mapping, funcTypeError, funcOverflowError)

    def test_arg_OneArg_TwoArgs(self):
        from IronPythonTest.BinderTest import COverloads_OneArg_TwoArgs
        target = COverloads_OneArg_TwoArgs()
        for (arg, mapping, funcTypeError, funcOverflowError) in [
            (     tuple(), dict(), 'M100 M101 M102 M103 M104 M105 ', '', ),
            (         100, _second('M100 M101 M102 M103 M104 '), 'M105 ', '', ),
            (  (100, 200), _first('M100 M101 M102 M103 M104 M105 '), '', '', ),
            ]:
            self._try_arg(target, arg, mapping, funcTypeError, funcOverflowError)

    def test_arg_OneArg_NormalOut(self):
        from IronPythonTest.BinderTest import COverloads_OneArg_NormalOut
        target = COverloads_OneArg_NormalOut()
        for (arg, mapping, funcTypeError, funcOverflowError) in [
            (     tuple(), dict(), 'M100 M101 M102 M103 M104 M105 ', '', ),
            (         100, _merge(_first('M100 M102 M105 '), _second('M103 M104 ')), 'M101 ', '', ),
            (  (100, 200), _second('M103 M104 '), 'M100 M101 M102 M105 ', '', ),
            ]:
            self._try_arg(target, arg, mapping, funcTypeError, funcOverflowError)

    def test_arg_OneArg_RefOut(self):
        from IronPythonTest.BinderTest import COverloads_OneArg_RefOut
        target = COverloads_OneArg_RefOut()
        for (arg, mapping, funcTypeError, funcOverflowError) in [
            (     tuple(), dict(), 'M100 M101 M102 M103 ', '', ),
            (         100, _merge(_first('M103 '), _second('M100 M101 M102 ')), '', '', ),
            (  (100, 200), _second('M101 M102 '), 'M100 M103 ', '', ),
            ]: