from ._compat import unittest
from ._adapt import DEFAULT_URI, IS_GAE, IS_IMAP, drop
from pydal._compat import integer_types
from pydal import DAL, Field
from pydal.helpers.methods import smart_query


@unittest.skipIf(IS_IMAP, "Skip nosql")
class TestSmartQuery(unittest.TestCase):
    def testRun(self):
        db = DAL(DEFAULT_URI, check_reserved=['all'])

        # -----------------------------------------------------------------------------
        # Seems further imports are required for the commented field types below

        # db.define_table('referred_table',
        #                 Field('represent_field', 'string'))
        # NOTE : Don't forget to uncomment the line # drop(db.referred_table) at the very end below
        #        if the above are uncommented

        db.define_table('a_table',
                        Field('string_field', 'string'),
                        Field('text_field', 'text'),
                        Field('boolean_field', 'boolean'),
                        Field('integer_field', 'integer'),
                        Field('double_field', 'double'),
                        # Field('decimal_field', 'decimal'),
                        # Field('date_field', 'date'),
                        # Field('time_field', 'time'),
                        # Field('datetime_field', 'datetime'),
                        # Field('reference_field', 'reference referred_table'),
                        # Field('list_string_field', 'list:string'),
                        # Field('list_integer_field', 'list:integer'),
                        # Field('list_reference_field', 'list:reference referred_table')
                        )

        fields = [db.a_table.id,
                  db.a_table.string_field,
                  db.a_table.text_field,
                  db.a_table.boolean_field,
                  db.a_table.integer_field,
                  db.a_table.double_field,
                  # db.a_table.decimal_field,
                  # db.a_table.date_field,
                  # db.a_table.time_field,
                  # db.a_table.reference_field,
                  # db.a_table.list_string_field,
                  # db.a_table.list_integer_field,
                  # db.a_table.list_reference_field
                  ]
        # -----------------------------------------------------------------------------

        # -----------------------------------------------------------------------------
        # Test with boolean field
        # Operator under test
        # operators = \
        #         [(' starts with ','startswith'),
        #          (' ends with ','endswith'),
        #          ('contains', 'N/A'),
        #          ('like', 'N/A')
        #          ]
        #
        #

        keywords = 'a_table.boolean_field = True'
        q = (db.a_table.boolean_field == True)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        if not IS_GAE:
            # Test string field query
            # starts with
            keywords = 'a_table.string_field starts with "pydal"'
            q = (db.a_table.string_field.startswith('pydal'))
            smart_q = smart_query(fields, keywords)
            self.assertEqual(smart_q, q)

            # ends with
            keywords = 'a_table.string_field ends with "Rocks!!"'
            q = (db.a_table.string_field.endswith('Rocks!!'))
            smart_q = smart_query(fields, keywords)
            self.assertEqual(smart_q, q)

            # contains
            keywords = 'a_table.string_field contains "Rocks"'
            q = (db.a_table.string_field.contains('Rocks'))
            smart_q = smart_query(fields, keywords)
            self.assertEqual(smart_q, q)

        # Don't work for some reason
        # # like
        # keywords = 'a_table.string_field like "%Rocks%"'
        # q = (db.a_table.string_field.like('%Rocks%'))
        # smart_q = smart_query(fields, keywords)
        # self.assertTrue(smart_q == q)
        # -----------------------------------------------------------------------------

        # -----------------------------------------------------------------------------
        # Tests with integer field
        # For generating these tests
        # def generate_tests():
        #     operators = \
        #         [('=', '='),
        #          ('==', '='),
        #          (' is ','='),
        #          (' equal ', '='),
        #          (' equals ', '='),
        #          (' equal to ', '='),
        #          ('<>', '!='),
        #          (' not equal ', '!='),
        #          (' not equal to ', '!='),
        #          ('<', '<'),
        #          (' less than ', '<'),
        #          ('<=', '<='),
        #          ('=<', '<='),
        #          (' less or equal ', '<='),
        #          (' less or equal than ', '<='),
        #          (' equal or less ', '<='),
        #          (' equal or less than ', '<='),
        #          ('>', '>'),
        #          (' greater than ', '>'),
        #          ('=>', '>='),
        #          ('>=', '>='),
        #          (' greater or equal ', '>='),
        #          (' greater or equal than ', '>='),
        #          (' equal or greater ', '>='),
        #          (' equal or greater than ', '>=')]  # JUST APPEND MORE OPERATORS HERE
        #
        #     for op in operators:
        #         print """
        #         # {op}
        #         keywords = 'a_table.integer_field {test_op} 1'
        #         q = (db.a_table.integer_field {result_op} 1)
        #         smart_q = smart_query(fields, keywords)
        #         self.assertTrue(smart_q == q)""".format(op=op,
        #                                                 test_op=op[0],
        #                                                 result_op='==' if op[1] == '=' else op[1])

        # ('=', '=')
        keywords = 'a_table.integer_field = 1'
        q = (db.a_table.integer_field == 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # ('==', '=')
        keywords = 'a_table.integer_field == 1'
        q = (db.a_table.integer_field == 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' is ','=')
        keywords = 'a_table.integer_field is 1'
        q = (db.a_table.integer_field == 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' equal ', '=')
        keywords = 'a_table.integer_field  equal  1'
        q = (db.a_table.integer_field == 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' equals ', '=')
        keywords = 'a_table.integer_field  equals  1'
        q = (db.a_table.integer_field == 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' equal to ', '=')
        keywords = 'a_table.integer_field  equal to  1'
        q = (db.a_table.integer_field == 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # This one not allow over integer it seems
        # # ('<>', '!=')
        # keywords = 'a_table.integer_field <> 1'
        # q = (db.a_table.integer_field != 1)
        # smart_q = smart_query(fields, keywords)
        # self.assertTrue(smart_q == q)

        # (' not equal ', '!=')
        keywords = 'a_table.integer_field  not equal  1'
        q = (db.a_table.integer_field != 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' not equal to ', '!=')
        keywords = 'a_table.integer_field  not equal to  1'
        q = (db.a_table.integer_field != 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # ('<', '<')
        keywords = 'a_table.integer_field < 1'
        q = (db.a_table.integer_field < 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' less than ', '<')
        keywords = 'a_table.integer_field  less than  1'
        q = (db.a_table.integer_field < 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # ('<=', '<=')
        keywords = 'a_table.integer_field <= 1'
        q = (db.a_table.integer_field <= 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # This one is invalid, maybe we should remove it from smart_query
        # # ('=<', '<=')
        # keywords = 'a_table.integer_field =< 1'
        # q = (db.a_table.integer_field <= 1)
        # smart_q = smart_query(fields, keywords)
        # self.assertTrue(smart_q == q)

        # (' less or equal ', '<=')
        keywords = 'a_table.integer_field  less or equal  1'
        q = (db.a_table.integer_field <= 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' less or equal than ', '<=')
        keywords = 'a_table.integer_field  less or equal than  1'
        q = (db.a_table.integer_field <= 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' equal or less ', '<=')
        keywords = 'a_table.integer_field  equal or less  1'
        q = (db.a_table.integer_field <= 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' equal or less than ', '<=')
        keywords = 'a_table.integer_field  equal or less than  1'
        q = (db.a_table.integer_field <= 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # ('>', '>')
        keywords = 'a_table.integer_field > 1'
        q = (db.a_table.integer_field > 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' greater than ', '>')
        keywords = 'a_table.integer_field  greater than  1'
        q = (db.a_table.integer_field > 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # This one is invalid, maybe we should remove it from smart_query
        # # ('=>', '>=')
        # keywords = 'a_table.integer_field => 1'
        # q = (db.a_table.integer_field >= 1)
        # smart_q = smart_query(fields, keywords)
        # self.assertTrue(smart_q == q)

        # ('>=', '>=')
        keywords = 'a_table.integer_field >= 1'
        q = (db.a_table.integer_field >= 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' greater or equal ', '>=')
        keywords = 'a_table.integer_field  greater or equal  1'
        q = (db.a_table.integer_field >= 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' greater or equal than ', '>=')
        keywords = 'a_table.integer_field  greater or equal than  1'
        q = (db.a_table.integer_field >= 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' equal or greater ', '>=')
        keywords = 'a_table.integer_field  equal or greater  1'
        q = (db.a_table.integer_field >= 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)

        # (' equal or greater than ', '>=')
        keywords = 'a_table.integer_field  equal or greater than  1'
        q = (db.a_table.integer_field >= 1)
        smart_q = smart_query(fields, keywords)
        self.assertEqual(smart_q, q)
        # -----------------------------------------------------------------------------

        # -----------------------------------------------------------------------------
        # Belongs and not belongs

        # NOTE : The below tests don't works
        # Issue : https://github.com/web2py/pydal/issues/161

        # (' in ', 'belongs') -> field.belongs(1, 2, 3)
        # keywords = 'a_table.integer_field in "1, 2, 3"'
        # q = (db.a_table.integer_field.belongs([1, 2, 3]))
        # smart_q = smart_query(fields, keywords)
        # self.assertEqual(smart_q, q)

        # keywords = 'a_table.id in "1, 2, 3"'
        # q = (db.a_table.id.belongs([1, 2, 3]))
        # smart_q = smart_query(fields, keywords)
        # self.assertEqual(smart_q, q)
        #
        # # (' not in ' , 'notbelongs'),
        # keywords = 'a_table.integer_field not in "1, 2, 3"'
        # q = (~db.a_table.id.belongs([1, 2, 3]))
        # smart_q = smart_query(fields, keywords)
        # self.assertTrue(smart_q == q)

        # -----------------------------------------------------------------------------
        # cleanup table
        drop(db.a_table)
        # drop(db.referred_table)
        # -----------------------------------------------------------------------------

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

