import sys
import os
sys.path.append(os.getcwd())
import unittest
import itertools
from copy import deepcopy

import utils
import classes
import conf
import world

def dummyf():
    pass

class TestUtils(unittest.TestCase):
    def setUp(self):
        self.irc = classes.FakeIRC('fakeirc', classes.FakeProto, conf.testconf)

    def testTS6UIDGenerator(self):
        uidgen = utils.TS6UIDGenerator('9PY')
        self.assertEqual(uidgen.next_uid(), '9PYAAAAAA')
        self.assertEqual(uidgen.next_uid(), '9PYAAAAAB')

    def test_add_cmd(self):
        # Without name specified, add_cmd adds a command with the same name
        # as the function
        utils.add_cmd(dummyf)
        utils.add_cmd(dummyf, 'TEST')
        # All command names should be automatically lowercased.
        self.assertIn('dummyf', world.commands)
        self.assertIn('test', world.commands)
        self.assertNotIn('TEST', world.commands)

    def test_add_hook(self):
        utils.add_hook(dummyf, 'join')
        self.assertIn('JOIN', world.hooks)
        # Command names stored in uppercase.
        self.assertNotIn('join', world.hooks)
        self.assertIn(dummyf, world.hooks['JOIN'])

    def testIsNick(self):
        self.assertFalse(utils.isNick('abcdefgh', nicklen=3))
        self.assertTrue(utils.isNick('aBcdefgh', nicklen=30))
        self.assertTrue(utils.isNick('abcdefgh1'))
        self.assertTrue(utils.isNick('ABC-def'))
        self.assertFalse(utils.isNick('-_-'))
        self.assertFalse(utils.isNick(''))
        self.assertFalse(utils.isNick(' i lost the game'))
        self.assertFalse(utils.isNick(':aw4t*9e4t84a3t90$&*6'))
        self.assertFalse(utils.isNick('9PYAAAAAB'))
        self.assertTrue(utils.isNick('_9PYAAAAAB\\'))

    def testIsChannel(self):
        self.assertFalse(utils.isChannel(''))
        self.assertFalse(utils.isChannel('lol'))
        self.assertTrue(utils.isChannel('#channel'))
        self.assertTrue(utils.isChannel('##ABCD'))

    def testIsServerName(self):
        self.assertFalse(utils.isServerName('Invalid'))
        self.assertTrue(utils.isServerName('services.'))
        self.assertFalse(utils.isServerName('.s.s.s'))
        self.assertTrue(utils.isServerName('Hello.world'))
        self.assertFalse(utils.isServerName(''))
        self.assertTrue(utils.isServerName('pylink.somenet.local'))
        self.assertFalse(utils.isServerName(' i lost th.e game'))

    def testJoinModes(self):
        res = utils.joinModes({('+l', '50'), ('+n', None), ('+t', None)})
        # Sets are orderless, so the end mode could be scrambled in a number of ways.
        # Basically, we're looking for a string that looks like '+ntl 50' or '+lnt 50'.
        possible = ['+%s 50' % ''.join(x) for x in itertools.permutations('lnt', 3)]
        self.assertIn(res, possible)

        # Without any arguments, make sure there is no trailing space.
        self.assertEqual(utils.joinModes({('+t', None)}), '+t')

        # The +/- in the mode is not required; if it doesn't exist, assume we're
        # adding modes always.
        self.assertEqual(utils.joinModes([('t', None), ('n', None)]), '+tn')

        # An empty query should return just '+'
        self.assertEqual(utils.joinModes(set()), '+')

        # More complex query now with both + and - modes being set
        res = utils.joinModes([('+l', '50'), ('-n', None)])
        self.assertEqual(res, '+l-n 50')

        # If one modepair in the list lacks a +/- prefix, just follow the
        # previous one's.
        res = utils.joinModes([('+l', '50'), ('-n', None), ('m', None)])
        self.assertEqual(res, '+l-nm 50')
        res = utils.joinModes([('+l', '50'), ('m', None)])
        self.assertEqual(res, '+lm 50')
        res = utils.joinModes([('l', '50'), ('-m', None)])
        self.assertEqual(res, '+l-m 50')

        # Rarely in real life will we get a mode string this complex.
        # Let's make sure it works, just in case.
        res = utils.joinModes([('-o', '9PYAAAAAA'), ('+l', '50'), ('-n', None),
                               ('-m', None), ('+k', 'hello'),
                               ('+b', '*!*@*.badisp.net')])
        self.assertEqual(res, '-o+l-nm+kb 9PYAAAAAA 50 hello *!*@*.badisp.net')

    def _reverseModes(self, query, expected, target='#PyLink', oldobj=None):
        res = utils.reverseModes(self.irc, target, query, oldobj=oldobj)
        self.assertEqual(res, expected)

    def testReverseModes(self):
        # Initialize the channe, first.
        utils.applyModes(self.irc, '#PyLink', [])
        # Strings.
        self._reverseModes("+mk-t test", "-mk+t test")
        self._reverseModes("ml-n 111", "-ml+n")
        # Lists.
        self._reverseModes([('+m', None), ('+r', None), ('+l', '3')],
                           {('-m', None), ('-r', None), ('-l', None)})
        # Sets.
        self._reverseModes({('s', None)}, {('-s', None)})
        # Combining modes with an initial + and those without
        self._reverseModes({('s', None), ('+R', None)}, {('-s', None), ('-R', None)})

    def testReverseModesUser(self):
        self._reverseModes({('+i', None), ('l', 'asfasd')}, {('-i', None), ('-l', 'asfasd')},
                           target=self.irc.pseudoclient.uid)

    def testReverseModesExisting(self):
        utils.applyModes(self.irc, '#PyLink', [('+m', None), ('+l', '50'), ('+k', 'supersecret'),
                                             ('+o', '9PYAAAAAA')])

        self._reverseModes({('+i', None), ('+l', '3')}, {('-i', None), ('+l', '50')})
        self._reverseModes('-n', '+n')
        self._reverseModes('-l', '+l 50')
        self._reverseModes('+k derp', '+k supersecret')
        self._reverseModes('-mk *', '+mk supersecret')

        self.irc.proto.spawnClient("tester2")
        oldobj = deepcopy(self.irc.channels['#PyLink'])

        # Existing modes are ignored.
        self._reverseModes([('+t', None)], set())
        self._reverseModes('+n', '+')
        #self._reverseModes('+oo 9PYAAAAAB 9PYAAAAAA', '-o 9PYAAAAAB', oldobj=oldobj)
        self._reverseModes('+o 9PYAAAAAA', '+')
        self._reverseModes('+vM 9PYAAAAAA', '-M')

        # Ignore unsetting prefixmodes/list modes that were never set.
        self._reverseModes([('-v', '10XAAAAAA')], set())
        self._reverseModes('-ob 10XAAAAAA derp!*@*', '+')
        utils.applyModes(self.irc, '#PyLink', [('+b', '*!user@badisp.tk')])
        self._reverseModes('-bb *!*@* *!user@badisp.tk', '+b *!user@badisp.tk')

if __name__ == '__main__':
    unittest.main()
