import re


def find_descriptions(txt, g7, ssp):
    """Collects structure definitions as follows:
    
    - Sections '#+ TAG (Name) `g7:FULL.TAG`'
    - Sections '#+ `RULE` :=' with only one level-n struct
    - Rows in tables 'Tag | Name<br/>URI | Description'
    
    Returns a {section header:[list,of,uris]} mapping
    """
    
    # structure sections
    for name,uri,desc in re.findall(r'#+ `[^`]*`[^\n]*\(([^)]*)\)[^\n]*`([^:`\n]*:[^`\n]*)`[^\n]*\n+((?:\n+(?!#)|[^\n])*)', txt):
        if uri not in ssp:
            raise Exception('Found section for '+uri+' but no gedstruct')
        if uri.startswith('g7:'):
            g7.setdefault(uri[3:],('structure',[],ssp[uri]))[1].extend((
                name.strip(),
                desc.strip()
            ))
            for other in re.findall(r'[Aa] type of `(\S*)`', desc):
                m = re.search('^#+ +`'+other+r'`[^\n`]*\n((?:[^\n]+|\n+(?!#))*)', txt, re.M)
                if m:
                    g7[uri[3:]][1].append(m.group(1).strip())
    
    # error check that gedstruct and sections align
    for uri in ssp:
        if 'pseudostructure' in uri: continue
        if uri.startswith('g7:') and uri[3:] not in g7:
            raise Exception('Found gedstruct for '+uri+' but no section')

    # gedstruct sections
    for uri, desc in re.findall(r'#+ *`[^`]*` *:=[^\n]*\n+`+[^\n]*\n+n [^\n]*\} *(\S+:\S+) *(?:\n [^\n]*)*\n`+[^\n]*\n+((?:[^\n]|\n(?!#))*)', txt):
        g7[uri[3:]][1].append(desc.strip())
    
    tagsets = {}
    # tag tables
    for table in re.finditer(r'\n#+ (\S[-A-Za-z0-9 ]*[a-z0-9])[^#]*?Tag *\| *Name[^|\n]*\| *Description[^\n]*((?:\n[^\n|]*\|[^\n|]*\|[^\n]*)*)', txt):
        pfx = ''
        header = table.group(1)
        if header.startswith('Fam'): pfx = 'FAM-'
        if header.startswith('Indi'): pfx = 'INDI-'
        for tag, name, desc in re.findall(r'`([A-Z_0-9]+)` *\| *([^|\n]*?) *\| *([^|\n]*[^ |\n]) *', table.group(2)):
            if '<br' in name: name = name[:name.find('<br')]
            if tag not in g7: tag = pfx+tag
            if tag not in g7:
                raise Exception('Found table for '+tag+' but no section or structure')
            if g7[tag][0] != 'structure':
                raise Exception('Found table for '+tag+' but that\'s a '+g7[tag][0]+' not a structure')
            tagsets.setdefault(header,[]).append(tag)
            g7[tag][1].append(name.strip())
            g7[tag][1].append(desc.strip())
    return tagsets