# Copyright (c) 2008-2009 Aryeh Leib Taurog, http://www.aryehleib.com
# All rights reserved.
#
# Modified from original contribution by Aryeh Leib Taurog, which was
# released under the New BSD license.

import unittest

from django.contrib.gis.geos.mutable_list import ListMixin
from django.utils import six


class UserListA(ListMixin):
    _mytype = tuple
    def __init__(self, i_list, *args, **kwargs):
        self._list = self._mytype(i_list)
        super(UserListA, self).__init__(*args, **kwargs)

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

    def __str__(self):  return str(self._list)

    def __repr__(self): return repr(self._list)

    def _set_list(self, length, items):
        # this would work:
        # self._list = self._mytype(items)
        # but then we wouldn't be testing length parameter
        itemList = ['x'] * length
        for i, v in enumerate(items):
            itemList[i] = v

        self._list = self._mytype(itemList)

    def _get_single_external(self, index):
        return self._list[index]

class UserListB(UserListA):
    _mytype = list

    def _set_single(self, index, value):
        self._list[index] = value

def nextRange(length):
    nextRange.start += 100
    return range(nextRange.start, nextRange.start + length)

nextRange.start = 0

class ListMixinTest(unittest.TestCase):
    """
    Tests base class ListMixin by comparing a list clone which is
    a ListMixin subclass with a real Python list.
    """
    limit = 3
    listType = UserListA

    def lists_of_len(self, length=None):
        if length is None: length = self.limit
        pl = list(range(length))
        return pl, self.listType(pl)

    def limits_plus(self, b):
        return range(-self.limit - b, self.limit + b)

    def step_range(self):
        return list(range(-1 - self.limit, 0)) + list(range(1, 1 + self.limit))

    def test01_getslice(self):
        'Slice retrieval'
        pl, ul = self.lists_of_len()
        for i in self.limits_plus(1):
            self.assertEqual(pl[i:], ul[i:], 'slice [%d:]' % (i))
            self.assertEqual(pl[:i], ul[:i], 'slice [:%d]' % (i))

            for j in self.limits_plus(1):
                self.assertEqual(pl[i:j], ul[i:j], 'slice [%d:%d]' % (i,j))
                for k in self.step_range():
                    self.assertEqual(pl[i:j:k], ul[i:j:k], 'slice [%d:%d:%d]' % (i,j,k))

            for k in self.step_range():
                self.assertEqual(pl[i::k], ul[i::k], 'slice [%d::%d]' % (i,k))
                self.assertEqual(pl[:i:k], ul[:i:k], 'slice [:%d:%d]' % (i,k))

        for k in self.step_range():
            self.assertEqual(pl[::k], ul[::k], 'slice [::%d]' % (k))

    def test02_setslice(self):
        'Slice assignment'
        def setfcn(x,i,j,k,L): x[i:j:k] = range(L)
        pl, ul = self.lists_of_len()
        for slen in range(self.limit + 1):
            ssl = nextRange(slen)
            ul[:] = ssl
            pl[:] = ssl
            self.assertEqual(pl, ul[:], 'set slice [:]')

            for i in self.limits_plus(1):
                ssl = nextRange(slen)
                ul[i:] = ssl
                pl[i:] = ssl
                self.assertEqual(pl, ul[:], 'set slice [%d:]' % (i))

                ssl = nextRange(slen)
                ul[:i] = ssl
                pl[:i] = ssl
                self.assertEqual(pl, ul[:], 'set slice [:%d]' % (i))

                for j in self.limits_plus(1):
                    ssl = nextRange(slen)
                    ul[i:j] = ssl
                    pl[i:j] = ssl
                    self.assertEqual(pl, ul[:], 'set slice [%d:%d]' % (i, j))

                    for k in self.step_range():
                        ssl = nextRange( len(ul[i:j:k]) )
                        ul[i:j:k] = ssl
                        pl[i:j:k] = ssl
                        self.assertEqual(pl, ul[:], 'set slice [%d:%d:%d]' % (i, j, k))

                        sliceLen = len(ul[i:j:k])
                        self.assertRaises(ValueError, setfcn, ul, i, j, k, sliceLen + 1)
                        if sliceLen > 2:
                            self.assertRaises(ValueError, setfcn, ul, i, j, k, sliceLen - 1)

                for k in self.step_range():
                    ssl = nextRange( len(ul[i::k]) )
                    ul[i::k] = ssl
                    pl[i::k] = ssl
                    self.assertEqual(pl, ul[:], 'set slice [%d::%d]' % (i, k))

                    ssl = nextRange( len(ul[:i:k]) )
                    ul[:i:k] = ssl
                    pl[:i:k] = ssl
                    self.assertEqual(pl, ul[:], 'set slice [:%d:%d]' % (i, k))

            for k in self.step_range():
                ssl = nextRange(len(ul[::k]))
                ul[::k] = ssl
                pl[::k] = ssl
                self.assertEqual(pl, ul[:], 'set slice [::%d]' % (k))


    def test03_delslice(self):
        'Delete slice'
        for Len in range(self.limit):
            pl, ul = self.lists_of_len(Len)
            del pl[:]
            del ul[:]
            self.assertEqual(pl[:], ul[:], 'del slice [:]')
            for i in range(-Len - 1, Len + 1):
                pl, ul = self.lists_of_len(Len)
                del pl[i:]
                del ul[i:]
                self.assertEqual(pl[:], ul[:], 'del slice [%d:]' % (i))
                pl, ul = self.lists_of_len(Len)
                del pl[:i]
                del ul[:i]
                self.assertEqual(pl[:], ul[:], 'del slice [:%d]' % (i))
                for j in range(-Len - 1, Len + 1):
                    pl, ul = self.lists_of_len(Len)
                    del pl[i:j]
                    del ul[i:j]
                    self.assertEqual(pl[:], ul[:], 'del slice [%d:%d]' % (i,j))
                    for k in list(range(-Len - 1, 0)) + list(range(1, Len)):
                        pl, ul = self.lists_of_len(Len)
                        del pl[i:j:k]
                        del ul[i:j:k]
                        self.assertEqual(pl[:], ul[:], 'del slice [%d:%d:%d]' % (i,j,k))

                for k in list(range(-Len - 1, 0)) + list(range(1, Len)):
                    pl, ul = self.lists_of_len(Len)