
from vsg import token
from vsg import violation

from vsg.rule_group import structure


def uniquify_standards(dMap):
    lReturn = []
    for sKey in list(dMap.keys()):
        for sWord in dMap[sKey]:
            if not sWord in lReturn:
                lReturn.append(sWord)
    return lReturn


dMap = {}
# https://redirect.cs.umbc.edu/portal/help/VHDL/VHDL-Handbook.pdf
dMap['1987'] = ['abs', 'access', 'after', 'alias', 'all', 'and', 'architecture', 'array', 'assert', 'attribute', 'begin', 'block', 'body', 'buffer', 'bus', 'case', 'component', 'configuration', 'constant', 'disconnect', 'downto', 'else', 'elsif', 'end', 'entity', 'exit', 'file', 'for', 'function', 'generate', 'generic', 'group', 'guarded', 'if', 'impure', 'in', 'inertial', 'inout', 'is', 'label', 'library', 'linkage', 'literal', 'loop', 'map', 'mod', 'nand', 'new', 'next', 'nor', 'not', 'null', 'of', 'on', 'open', 'or', 'others', 'out', 'package', 'port', 'postponed', 'procedure', 'process', 'pure', 'range', 'record', 'register', 'reject', 'rem', 'report', 'return', 'rol', 'ror', 'select', 'severity', 'signal', 'shared', 'sla', 'sll', 'sra', 'srl', 'subtype', 'then', 'to', 'transport', 'type', 'unaffected', 'units', 'until', 'use', 'variable', 'wait', 'when', 'while', 'with', 'xnor', 'xor']
# http://www.dacya.ucm.es/dani/manual.pdf
dMap['1993'] = ['abs', 'access', 'after', 'alias', 'all', 'and', 'architecture', 'array', 'assert', 'attribute', 'begin', 'block', 'body', 'buffer', 'bus', 'case', 'component', 'configuration', 'constant', 'disconnect', 'downto', 'else', 'elsif', 'end', 'entity', 'exit', 'file', 'for', 'function', 'generate', 'generic', 'group', 'guarded', 'if', 'impure', 'in', 'inertial', 'inout', 'is', 'label', 'library', 'linkage', 'literal', 'loop', 'map', 'mod', 'nand', 'new', 'next', 'nor', 'not', 'null', 'of', 'on', 'open', 'or', 'others', 'out', 'package', 'port', 'postponed', 'procedure', 'process', 'pure', 'range', 'record', 'register', 'reject', 'rem', 'report', 'return', 'rol', 'ror', 'select', 'severity', 'signal', 'shared', 'sla', 'sll', 'sra', 'srl', 'subtype', 'then', 'to', 'transport', 'type', 'unaffected', 'units', 'until', 'use', 'variable', 'wait', 'when', 'while', 'with', 'xnor', 'xor']
# https://edg.uchicago.edu/~tang/VHDLref.pdf
dMap['2000'] = ['abs', 'access', 'after', 'alias', 'all', 'and', 'architecture', 'array', 'assert', 'attribute', 'begin', 'block', 'body', 'buffer', 'bus', 'case', 'component', 'configuration', 'constant', 'disconnect', 'downto', 'else', 'elsif', 'end', 'entity', 'exit', 'file', 'for', 'function', 'generate', 'generic', 'group', 'guarded', 'if', 'impure', 'in', 'inertial', 'inout', 'is', 'label', 'library', 'linkage', 'literal', 'loop', 'map', 'mod', 'nand', 'new', 'next', 'nor', 'not', 'null', 'of', 'on', 'open', 'or', 'others', 'out', 'package', 'port', 'postponed', 'procedural', 'procedure', 'process', 'protected', 'pure', 'range', 'record', 'reference', 'register', 'reject', 'rem', 'report', 'return', 'rol', 'ror', 'select', 'severity', 'signal', 'shared', 'sla', 'sll', 'sra', 'srl', 'subtype', 'then', 'to', 'transport', 'type', 'unaffected', 'units', 'until', 'use', 'variable', 'wait', 'when', 'while', 'with', 'xnor', 'xor']
# https://vhdl-manual.narod.ru/books/ieee_manual.pdf
dMap['2002'] = ['abs', 'access', 'after', 'alias', 'all', 'and', 'architecture', 'array', 'assert', 'attribute', 'begin', 'block', 'body', 'buffer', 'bus', 'case', 'component', 'configuration', 'constant', 'disconnect', 'downto', 'else', 'elsif', 'end', 'entity', 'exit', 'file', 'for', 'function', 'generate', 'generic', 'group', 'guarded', 'if', 'impure', 'in', 'inertial', 'inout', 'is', 'label', 'library', 'linkage', 'literal', 'loop', 'map', 'mod', 'nand', 'new', 'next', 'nor', 'not', 'null', 'of', 'on', 'open', 'or', 'others', 'out', 'package', 'port', 'postponed', 'procedural', 'procedure', 'process', 'protected', 'pure', 'range', 'record', 'reference', 'register', 'reject', 'rem', 'report', 'return', 'rol', 'ror', 'select', 'severity', 'signal', 'shared', 'sla', 'sll', 'sra', 'srl', 'subtype', 'then', 'to', 'transport', 'type', 'unaffected', 'units', 'until', 'use', 'variable', 'wait', 'when', 'while', 'with', 'xnor', 'xor']
# Unknown
dMap['2008'] = ['abs', 'access', 'after', 'alias', 'all', 'and', 'architecture', 'array', 'assert', 'assume', 'assume_guarantee', 'attribute', 'begin', 'block', 'body', 'buffer', 'bus', 'case', 'component', 'configuration', 'constant', 'context', 'cover', 'default', 'disconnect', 'downto', 'else', 'elsif', 'end', 'entity', 'exit', 'fairness', 'file', 'for', 'force', 'function', 'generate', 'generic', 'group', 'guarded', 'if', 'impure', 'in', 'inertial', 'inout', 'is', 'label', 'library', 'linkage', 'literal', 'loop', 'map', 'mod', 'nand', 'new', 'next', 'nor', 'not', 'null', 'of', 'on', 'open', 'or', 'others', 'out', 'package', 'parameter', 'port', 'postponed', 'procedure', 'process', 'property', 'protected', 'pure', 'range', 'record', 'register', 'reject', 'release', 'rem', 'report', 'restrict', 'restrict_guarantee', 'return', 'rol', 'ror', 'select', 'sequence', 'severity', 'signal', 'shared', 'sla', 'sll', 'sra', 'srl', 'strong', 'subtype', 'then', 'to', 'transport', 'type', 'unaffected', 'units', 'until', 'use', 'variable', 'vmode', 'vprop', 'vunit', 'wait', 'when', 'while', 'with', 'xnor', 'xor']
dMap['all'] = uniquify_standards(dMap)


lIdentifiers = []
lIdentifiers.append(token.architecture_body.identifier)
lIdentifiers.append(token.architecture_body.identifier)
lIdentifiers.append(token.attribute_declaration.identifier)
lIdentifiers.append(token.component_declaration.identifier)
lIdentifiers.append(token.configuration_declaration.identifier)
lIdentifiers.append(token.constant_declaration.identifier)
lIdentifiers.append(token.context_declaration.identifier)
lIdentifiers.append(token.entity_aspect.architecture_identifier)
lIdentifiers.append(token.entity_declaration.identifier)
lIdentifiers.append(token.entity_designator.entity_tag)
lIdentifiers.append(token.file_declaration.identifier)
lIdentifiers.append(token.full_type_declaration.identifier)
lIdentifiers.append(token.group_declaration.identifier)
lIdentifiers.append(token.identifier.identifier)
lIdentifiers.append(token.identifier_list.identifier)
lIdentifiers.append(token.incomplete_type_declaration.identifier)
lIdentifiers.append(token.instantiated_unit.architecture_identifier)
lIdentifiers.append(token.interface_constant_declaration.identifier)
lIdentifiers.append(token.interface_file_declaration.identifier)
lIdentifiers.append(token.interface_function_specification.parameter_keyword)
lIdentifiers.append(token.interface_function_specification.return_keyword)
lIdentifiers.append(token.interface_procedure_specification.parameter_keyword)
lIdentifiers.append(token.interface_signal_declaration.identifier)
lIdentifiers.append(token.interface_unknown_declaration.identifier)
lIdentifiers.append(token.interface_variable_declaration.identifier)
lIdentifiers.append(token.package_declaration.identifier)
lIdentifiers.append(token.parameter_specification.identifier)
lIdentifiers.append(token.primary_unit_declaration.identifier)
lIdentifiers.append(token.secondary_unit_declaration.identifier)
lIdentifiers.append(token.signal_declaration.identifier)
lIdentifiers.append(token.subprogram_instantiation_declaration.identifier)
lIdentifiers.append(token.subtype_declaration.identifier)
lIdentifiers.append(token.variable_declaration.identifier)
### Simple Names
lIdentifiers.append(token.architecture_body.architecture_simple_name)
lIdentifiers.append(token.component_declaration.component_simple_name)
lIdentifiers.append(token.configuration_declaration.configuration_simple_name)
lIdentifiers.append(token.context_declaration.context_simple_name)
lIdentifiers.append(token.entity_declaration.entity_simple_name)
lIdentifiers.append(token.package_body.package_simple_name)
lIdentifiers.append(token.package_body.end_package_simple_name)
lIdentifiers.append(token.package_declaration.end_package_simple_name)
lIdentifiers.append(token.physical_type_definition.simple_name)
lIdentifiers.append(token.protected_type_body.protected_type_simple_name)
lIdentifiers.append(token.protected_type_declaration.protected_type_simple_name)
lIdentifiers.append(token.record_element_constraint.record_element_simple_name)
lIdentifiers.append(token.record_type_definition.record_type_simple_name)
### Designators
lIdentifiers.append(token.alias_declaration.alias_designator)
lIdentifiers.append(token.attribute_specification.attribute_designator)
lIdentifiers.append(token.function_specification.designator)
lIdentifiers.append(token.interface_function_specification.designator)
lIdentifiers.append(token.interface_procedure_specification.designator)
lIdentifiers.append(token.procedure_specification.designator)
lIdentifiers.append(token.subprogram_body.designator)


class rule_001(structure.Rule):
    '''
    This rule checks for VHDL reserved words being used as identifiers and names.

    |configuring_vhdl_reserved_words_link|

    **Violation**

    .. code-block:: vhdl

       entity null is
       end null;   
    '''

    def __init__(self):
        structure.Rule.__init__(self, 'reserved', '001')
        self.configuration.append('standard')
        self.standard = 'all'
        self.fixable = False
        self.configuration_documentation_link = 'configuring_vhdl_reserved_words_link'

    def _get_tokens_of_interest(self, oFile):
        lReturn = oFile.get_tokens_matching(lIdentifiers)
        return lReturn

    def _analyze(self, lToi):
        lReservedWords = dMap[self.standard]
        for oToi in lToi:
            if token_value_is_a_reserved_word(oToi, lReservedWords):
                self.create_violation(oToi)

    def create_violation(self, oToi):
        oToken = oToi.get_tokens()[0]
        sSolution = 'Invalid use of reserved word ' + oToken.get_value()
        oViolation = violation.New(oToi.get_line_number(), oToi, sSolution)
        self.add_violation(oViolation)


def token_value_is_a_reserved_word(oToi, lReservedWords):
    oToken = oToi.get_tokens()[0]
    if oToken.get_lower_value() in lReservedWords:
        return True
    return False
