# Copyright (C) 2017 The Meme Factory, Inc.  http://www.meme.com/
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Karl O. Pinc <kop@meme.com>

import pytest
from enforcer import exceptions as ex
from enforcer import rules_yacc


# Global parser
parser = rules_yacc.parser


# Tests

def test_string():
    '''A simple string works'''
    assert (parser.parse('two words') ==
            [('string', 'two words')] + rules_yacc.IMPLICIT_SUFFIXES)


def test_string_entity():
    '''A string followed by an entity works'''
    assert (parser.parse('two words <entity>') ==
            [('string', 'two words '),
             ('entity', '<entity>')] +
            rules_yacc.IMPLICIT_SUFFIXES)


def test_hashedstring_entity():
    '''A string followed by an entity works'''
    assert (parser.parse('two words <#> <entity>') ==
            [('string', 'two words '),
             ('hash', '<#>'),
             ('string', ' '),
             ('entity', '<entity>')] +
            rules_yacc.IMPLICIT_SUFFIXES)


def test_long_string():
    '''A longer string works'''
    assert (parser.parse('three words yeah') ==
            [('string', 'three words yeah')] +
            rules_yacc.IMPLICIT_SUFFIXES)


def test_entity():
    '''An entity as a name works'''
    assert (parser.parse('<entity>') ==
            [('entity', '<entity>')] + rules_yacc.IMPLICIT_SUFFIXES)


def test_string_date():
    '''A string followed by a name works'''
    assert (parser.parse('two words - <yyyy-mm-dd>') ==
            [('string', 'two words'),
             ('string', ' - '),
             ('date', '<yyyy-mm-dd>')] +
            rules_yacc.IMPLICIT_SUFFIXES)


def test_entity_year():
    '''An entity followed by a year works'''
    assert (parser.parse('<entity> - <yyyy>') ==
            [('entity', '<entity>'), ('string', ' - ') , ('year', '<yyyy>')] +
            rules_yacc.IMPLICIT_SUFFIXES)


def test_string_opt_hash():
    '''String followed by an optional hash works'''
    assert (parser.parse('two words[ - <#>]') ==
            [('string', 'two words'),
             ('optional', [('string', ' - '), ('hash', '<#>')])] +
            rules_yacc.IMPLICIT_VERSION +
            rules_yacc.IMPLICIT_PARENS)


def test_string_opt_hash_opt_user_str():
    '''String followed by an optional hash works by an optional user value'''
    assert (parser.parse('two words[ - <#>][ - <address>]') ==
            [('string', 'two words'),
             ('optional', [('string', ' - '), ('hash', '<#>')]),
             ('optional', [('string', ' - '), ('user_str', '<address>')])] +
            rules_yacc.IMPLICIT_VERSION +
            rules_yacc.IMPLICIT_PARENS)


def test_string_opt_hash_version():
    '''String followed by optional hash by required version'''
    assert (parser.parse('two words[ - <#>] v<#>') ==
            [('string', 'two words'),
             ('optional', [('string', ' - '), ('hash', '<#>')]),
             ('version', ' v<#>')] +
            rules_yacc.IMPLICIT_PARENS)


def test_string_version_paren():
    '''String followed by a version followed by a parenthetical'''
    assert (parser.parse('two words v<#> (some note)') ==
            [('string', 'two words'),
             ('version', ' v<#>'),
             ('paren', ' (some note)')] +
            rules_yacc.IMPLICIT_PARENS)


def test_string_version():
    '''String followed by a version'''
    assert (parser.parse('two words v<#>') ==
            [('string', 'two words'), ('version', ' v<#>')] +
            rules_yacc.IMPLICIT_PARENS)


def test_string_paren():
    '''String followed by paren'''
    assert (parser.parse('two words (some note)') ==
            [('string', 'two words')] +
            rules_yacc.IMPLICIT_VERSION +
            [('paren', ' (some note)')] +
            rules_yacc.IMPLICIT_PARENS)


def test_string_paren_opt_paren():
    '''String followed by paren by optional paren'''
    assert (parser.parse('two words (some note)[ (optional note)]') ==
            [('string', 'two words')] +
            rules_yacc.IMPLICIT_VERSION +
            [('paren', ' (some note)'),
             ('optional', [('paren', ' (optional note)')])] +
            rules_yacc.IMPLICIT_PARENS)


def test_string_opt_paren():
    '''String followed by optional paren'''
    assert (parser.parse('two words[ (optional note)]') ==
            [('string', 'two words')] +
            rules_yacc.IMPLICIT_VERSION +
            [('optional', [('paren', ' (optional note)')])] +
            rules_yacc.IMPLICIT_PARENS)


def test_string_paren_paren():
    '''String followed by paren by paren'''
    assert(parser.parse('two words (some note) (another note)') ==
           [('string', 'two words')] +
           rules_yacc.IMPLICIT_VERSION +
           [('paren', ' (some note)'),
            ('paren', ' (another note)')] +
           rules_yacc.IMPLICIT_PARENS)


def test_string_opt_paren_opt_paren():
    '''String followed by optional paren by optional paren'''
    assert (parser.parse(
        'two words[ (optional note)][ (another opt note)]') ==
        [('string', 'two words')] +
        rules_yacc.IMPLICIT_VERSION +
        [('optional', [('paren', ' (optional note)')]),
         ('optional', [('paren', ' (another opt note)')])] +
        rules_yacc.IMPLICIT_PARENS)


def test_string_user_element_opt_paren():
    '''String followed by a user element followed by an optional paren'''
    assert (parser.parse('word - <address>[ (optional)]') ==
            [('string', 'word'),
             ('string', ' - '),
             ('user_str', '<address>')] +
            rules_yacc.IMPLICIT_VERSION +
            [('optional', [('paren', ' (optional)')])] +
            rules_yacc.IMPLICIT_PARENS)


def test_numbered_bullet():
    '''Hash follwed by period followed by string'''
    assert (parser.parse('<#>. two words') ==
            [('hash', '<#>'),
             ('string', '. '),
             ('string', 'two words')] +
            rules_yacc.IMPLICIT_SUFFIXES)


def test_numbered_bullet_version():
    '''Hash follwed by period followed by string'''
    assert (parser.parse('<#>. two words v<#>') ==
            [('hash', '<#>'),
             ('string', '. '),
             ('string', 'two words'),
             ('version', ' v<#>')] +
            rules_yacc.IMPLICIT_PARENS)


def test_last_4_digits():
    '''String followed by last 4 digits'''
    assert (parser.parse('two words - <last 4 digits>') ==
            [('string', 'two words'),
             ('string', ' - '),
             ('last_4_digits', '<last 4 digits>')] +
            rules_yacc.IMPLICIT_SUFFIXES)


def test_hash_suffixed_string_complex():
    '''String followed by hash'''
    assert (parser.parse('two words <#>') ==
            [('string', 'two words '), ('hash', '<#>')] +
            rules_yacc.IMPLICIT_SUFFIXES)


def test_string_uval_string_uval():
    '''String followed by entity by string by entity'''
    assert (parser.parse('two words <entity> from <entity>') ==
            [('string', 'two words '),
             ('entity', '<entity>'),
             ('string', ' '),
             ('string', 'from'),
             ('string', ' '),
             ('entity', '<entity>')] +
            rules_yacc.IMPLICIT_SUFFIXES)


def test_string_opt_string_uval():
    '''String followed by optional string-user_value'''
    assert (parser.parse('two words[ from <somewhere>]') ==
            [('string', 'two words'),
             ('optional', [('string', ' from '),
                           ('user_str', '<somewhere>')])] +
            rules_yacc.IMPLICIT_VERSION +
            rules_yacc.IMPLICIT_PARENS)


def test_uval_string_uval():
    '''user value followed by string, by user value'''
    assert (parser.parse('<first> of <second>') ==
            [('user_str', '<first>'),
             ('string', ' '),
             ('string', 'of '),
             ('user_str', '<second>')] +
            rules_yacc.IMPLICIT_SUFFIXES)


def test_yaccerror():
    '''Test the yacc bad-token error code'''
    with pytest.raises(ex.YaccError):
        parser.parse(' -  - ')


def test_yacceoferror():
    '''Test the yacc eof error code'''
    with pytest.raises(ex.YaccEOFError):
        parser.parse('something ')
