"""For functions concerning notation notes as they are related to"""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../../../../../nbs/52_markdown.obsidian.personal.notation.in_standard_information_note.ipynb.

# %% auto 0
__all__ = ['notat_str_from_doub_asts_in_std_info_note', 'notat_str_from_html_tags', 'notation_notes_linked_in_see_also_section',
           'notations_and_main_notes', 'notation_note_is_linked_in_see_also_section', 'add_notation_note_to_see_also',
           'add_missing_notation_links_to_information_notes']

# %% ../../../../../nbs/52_markdown.obsidian.personal.notation.in_standard_information_note.ipynb 2
import warnings
import os
from os import PathLike
from typing import Optional, Union
from pathlib import Path

from .....helper.definition_and_notation import notation_asterisk_indices
from .....helper.html import remove_html_tags_in_text 
from .....helper.latex import latex_indices 
from ....markdown.file import MarkdownFile, MarkdownLineEnum
from ...links import ObsidianLink
from ..information_notes import bulleted_links_of_type_in_section
from ..note_type import note_is_of_type, PersonalNoteTypeEnum
from .parse import main_of_notation
from ...vault import VaultNote, NoteDoesNotExistError

# %% ../../../../../nbs/52_markdown.obsidian.personal.notation.in_standard_information_note.ipynb 9
def notat_str_from_doub_asts_in_std_info_note(
        info_note: VaultNote
        ) -> list[str]: # Each str is a LaTeX str, beginning and trailing dollar signs `$` (single or double) included.
    """
    Return the LaTeX str's with notations in a standard information note.

    For this function, A LaTeX str is deemed to be a notation if it is surrounded by double
    asterisks `**`
    """
    mf = MarkdownFile.from_vault_note(info_note)
    notations = []
    for part in mf.parts:
        indices = notation_asterisk_indices(part['line'])
        notations.extend([
            part['line'][start+2:end-2] for start, end in indices])
    return notations

# %% ../../../../../nbs/52_markdown.obsidian.personal.notation.in_standard_information_note.ipynb 15
def notat_str_from_html_tags(
        info_note: VaultNote
        ) -> list[tuple[str, str]]: # Each str is a LaTeX str, the first of which is the text of and surrounded by the HTML tag and the second of which is a string (without surrounding dollar signs) specifying the actual notation introduced in the first text.
    """
    Return the LaTeX str's with notations in a standard information note.

    For this function, HTML tags with the `notation` attr are deemed to
    contain (newly introduced) notations. The `notation` attribute can
    have multiple strings separated by double semicolons `;;` and
    not surrounded by dollar signs `$`. 

    The following are some examples of HTML tags with the format described
    above:

    1. <span notation="">$H^i$</span>
    2. <span notation="h^i">$h^i := \dim_k H^i$</span>
    3. <span notation="IJ;;I+J">$$IJ = \langle ab: a \in I, b \in J \rangle, \quad I+J = \{a+b: a \in I, b \in J \}$</span>

    **Raises**
    - UserWarning
        - If an HTML tag with a `notation` attr does not surround a pure
          math mode string.
    """
    text = info_note.text()
    _, tag_data = remove_html_tags_in_text(text)
    pairs = []
    for tag, _, _ in tag_data:
        if not 'notation' in tag.attrs:
            continue
        notat_strs = tag.attrs['notation'].split(';;')
        pairs.extend([(tag.text, notat_str) for notat_str in notat_strs])
        
        if latex_indices(tag.text) != [(0, len(tag.text))]:
            warnings.warn(
                rf"""In the note {info_note.name} at {info_note.path()},
                there is a notation {tag.text}, but it is not
                recognized as a pure LaTeX math mode string""")
    return pairs

# %% ../../../../../nbs/52_markdown.obsidian.personal.notation.in_standard_information_note.ipynb 19
def notation_notes_linked_in_see_also_section(
        info_note: VaultNote,
        vault: PathLike, # Path to the vault directory.
        as_vault_notes: bool = True # If `True`, returns each notation note as a `VaultNote` object.  Otherwise, returns the name of each notation note. Defaults to `True`.
        ) -> Union[list[VaultNote], list[str]]: # Each entry corresponds to a notation note in the vault.
    """Return a list of notation notes listed in the
    `See Also` section of the standard information note.

    In the current implementation of this function, only 
    "notation notes" that actually exist are included in
    the returned list.
    """
    links = bulleted_links_of_type_in_section(
        info_note, vault, section="See Also",
        note_type=PersonalNoteTypeEnum.NOTATION_NOTE)
    note_names = [link.file_name for link in links]
    if as_vault_notes:
        return [VaultNote(vault, name=note_name, update_cache=False) for note_name in note_names]
    else:
        return note_names


# %% ../../../../../nbs/52_markdown.obsidian.personal.notation.in_standard_information_note.ipynb 28
def notations_and_main_notes(
        vault: PathLike, # Path to the vault directory.
        subdirectory: Optional[PathLike] = None, # Path to the subdirectory, relative to `vault`, to find the notation notes. Searches for all notation notes here and in subdirectories of this subdirectory. If `None`, then the `note parameter is used to determined the subdirectory. If `subdirectory` is the empty str, then all notation notes in the vault are searched. Defaults to `None`. 
        note: Optional[VaultNote] = None # A note in the vault. The directory that this note is in determines the `subdirectory` parameter if the argument passed to `subdirectory` is the blank str. This note can usually be an index note, e.g. `'_index_silverman'`. Defaults to `None`, in which case `subdirectory` must be specified.
        ) -> dict[str, Union[str, None]]: # A key is the unique name of a notation note in the vault and its corresponding value is the name of the main note of the notation note. Each main note may not actually exist, but each notation note definitely exists. If the notation note has no main note (i.e. has no links to other notes), then the value is `None`.
    """Return a `dict` with all of notation notes in a specified
    subdirectory of a vault and the names of the main notes of these
    notation notes.
    
    **Returns**
    - `dict`

    **Raises**

    - ValueError
        - If `subdirectory` and `note` are both `None`.
    """
    if note is None and subdirectory is None:
        raise ValueError(
            'Both the `subdirectory` and `note` parameters are None.')
    # vault = vault if vault is not None else ''
    if subdirectory is None:
        subdirectory = Path(note.rel_path).parent
    subdirectory_path = Path(vault) / subdirectory
    notes_in_subdirectory = subdirectory_path.glob(f'**/*.md') 
    relative_paths = [Path(note_path).relative_to(subdirectory_path)
                      for note_path in notes_in_subdirectory]
    vn_objects = [VaultNote(vault, rel_path=Path(subdirectory) / rel_path)
                  for rel_path in relative_paths]
    return {vn.name: main_of_notation(vn) for vn in vn_objects
            if note_is_of_type(vn, PersonalNoteTypeEnum.NOTATION_NOTE)}

# %% ../../../../../nbs/52_markdown.obsidian.personal.notation.in_standard_information_note.ipynb 36
def notation_note_is_linked_in_see_also_section(
        notation_note: VaultNote,
        info_note: Optional[VaultNote] = None # The note in which to find the link to `notation_note`. Defaults to `None`, in which case the main note is determined to be the first linked note of `notation_note`.
        ) -> bool:
    """Return `True` if a notation note is linked in the `See Also`
    section of a standard information note. 
    """
    if not info_note:
        info_note = main_of_notation(notation_note, as_note=True)
    notes = notation_notes_linked_in_see_also_section(
        info_note, vault=notation_note.vault, as_vault_notes=False)
    return notation_note.name in notes


# %% ../../../../../nbs/52_markdown.obsidian.personal.notation.in_standard_information_note.ipynb 43
def add_notation_note_to_see_also(
        notation_note: VaultNote,
        info_note: Optional[VaultNote] = None, # The note in which to link `notation_note`. Defaults to `None`, in which case the main note is determined to be the first linked note of `notation_note`.
        do_not_repeat: bool = True # If `True`, do not add a link to `notation_note` in if there is already a such a link.
        ) -> None:
    """Add a link to a notation note in the `See Also` section of
    a standard information note.

    **Raises**

    - NoteDoesNotExistError
        - If the information note to link to does not exist.
    
    """
    if not info_note:
        info_note = main_of_notation(notation_note, as_note=True)
    if not info_note.exists():
        raise NoteDoesNotExistError
    if do_not_repeat and notation_note_is_linked_in_see_also_section(
            notation_note, info_note):
        return
    mf = MarkdownFile.from_vault_note(info_note)
    link = ObsidianLink(False, notation_note.name, 0, 0)
    mf.add_line_in_section(
        'See Also', {'type': MarkdownLineEnum.UNORDERED_LIST,
                     'line': f'- {str(link)}'})
    mf.write(info_note)



# %% ../../../../../nbs/52_markdown.obsidian.personal.notation.in_standard_information_note.ipynb 56
def add_missing_notation_links_to_information_notes(
        vault: PathLike, # Path to the vault directory.
        subdirectory: Optional[PathLike] = None, # Path to the subdirectory, relative to `vault`, to find the notation notes and their main notes. Searches for all notation notes here and in subdirectories of this subdirectory. If `None`, then the `note` parameter is used to determine `subdirectory`. Defaults to `None`. 
        note: Optional[VaultNote] = None # A note in the vault. The directory that this note is in determines the `subdirectory` parameter if it is `None`.  Defaults to `None`, in which case `subdirectory` must be specified.
        ) -> None:
    # TODO: deal with possibility that note does not exist.
    """For each notation note in a specified subdirectory, Add links
    to notation notes in their main information notes if the notation
    links are not already present.
    
    **Raises**

    - ValueError
        - If `subdirectory` and `note` are both `None`.
    """
    mains_dict = notations_and_main_notes(vault, subdirectory, note)
    to_check = {key: value for key, value in mains_dict.items()
                if value is not None}
    for notation, main in to_check.items():
        if not main:
            continue
        notation_note = VaultNote(vault, name=notation)
        try:
            main_note = VaultNote(vault, name=main)  # TODO add the subdirectory parameter here appropriately. See Also `notations_and_main_note` 
            add_notation_note_to_see_also(notation_note, main_note)
        except NoteDoesNotExistError:
            continue
