import re # regular expressions used for parsing the tags file
import os # to get the current working directory
import os.path

__context_regular_expression = re.compile(r".*\t\/\^(.*)\$\/;\"\t.*")

class CantFindTagsFile(Exception):
    pass

class Ctags:
    def __init__(self, tags_file_path=None):
        if not tags_file_path:
            tags_file_path = find_tags_file()
        self.tags_file = tags_file_path
        self.tags_root = os.path.dirname(tags_file_path)
        self.py_tags = parse_tags_file(tags_file_path)

class CtagsFunctionSignatures:
    def __init__(self, tags_file_path=None):
        if not tags_file_path:
            tags_file_path = find_tags_file()
        self.tags_file = tags_file_path
        self.tags_root = os.path.dirname(tags_file_path)
        self.function_signatures = parse_function_signatures(tags_file_path)

def find_tags_file(directory=None, tags_file_name="tags"):
    """looks for a file in the current or given directory and
    all parent directories of that directory up until some directory
    that is in PHPSH_CEILING_DIRECTORIES or the root. Directories
    in PHPSH_CEILING_DIRECTORIES are not checked. The root is checked
    unless it is in PHPSH_CEILING_DIRECTORIES. If the provided directory
    is in PHPSH_CEILING_DIRECTORIES, it will be checked."""

    ceiling_config = os.environ.get("PHPSH_CEILING_DIRECTORIES", "")
    ceiling_directories = filter(bool, ceiling_config.split(os.pathsep))

    if not directory:
        directory = os.environ.get("PWD") or os.getcwd()

    while True:
        tags_path = os.path.join(directory, tags_file_name)
        if os.path.isfile(tags_path):
            return tags_path

        next_search_dir = os.path.dirname(directory)
        if next_search_dir == directory or \
           next_search_dir in ceiling_directories:
            raise CantFindTagsFile
        directory = next_search_dir

def parse_tags_file(tags_file_path):
    """parses a tags file generated by ctags,
    returns a dict of identifiers with info about them"""
    tags_file = open(tags_file_path)
    py_tags = {}
    for line in tags_file:
        if line[:6] == '!_TAG_': # tag program metadata
            continue
        cols = line.rstrip().split("\t")
        identifier = cols[0]
        file_path = cols[1]
        type = cols[-1] # type is the last field
        try:
            (context, ) = __context_regular_expression.match(line).groups()
        except ValueError:
            continue
        except AttributeError:
            continue

        if not py_tags.has_key(identifier):
            py_tags[identifier] = []
        py_tags[identifier].append(
            {"file": file_path, "context": context, "type": type})

    return py_tags

__function_args_regular_expression = re.compile(
    '^\S+\s+(\S+).*function\s+(.*)')
def parse_function_signatures(tags_file_path):
    tags_file = open(tags_file_path)
    functions_to_signatures = {}
    for line in tags_file:
        if line.startswith('!_TAG_') or line.rstrip()[-1] != "f":
            continue
        try:
            line_to_content_end = line[:line.rfind('$')]
            if line_to_content_end.endswith(' {'):
                line_to_content_end = line_to_content_end[:-2]
            if line_to_content_end.endswith(';'):
                line_to_content_end = line_to_content_end[:-1]
            signature = __function_args_regular_expression.match(
                line_to_content_end).groups()
        except ValueError:
            continue
        except AttributeError:
            continue
        except IndexError:
            continue

        cols = line.rstrip().split("\t")
        identifier = cols[0]
        try:
            functions_to_signatures[identifier].append(signature)
        except KeyError:
            functions_to_signatures[identifier] = [signature]

    return functions_to_signatures
