# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: Bradley M. Bell <bradbell@seanet.com>
# SPDX-FileContributor: 2020-24 Bradley M. Bell
# ----------------------------------------------------------------------------
import re
import xrst
#
pattern_start_literal = re.compile( r'\n::@xrst_line [0-9]+@\n\n' )
pattern_end_literal   = re.compile( r'\n[^ \t]')
#
# pattern_declare, pattern_use
# group(1): is the name of the label
# group(2): is the external destinatieon (empty for internal links).
# gruup(3): is the line where the label is defined or used.
pattern_declare = re.compile(
   r'\n\.\. _([^:\n]+):([^\n]*)@xrst_line ([0-9]+)@'
)
pattern_use = re.compile(
   r'`([^<\n]+)(<[^>\n]*>)`_[^\n]*@xrst_line ([0-9]+)@'
)
# ----------------------------------------------------------------------------
# {xrst_begin sphinx_label dev}
#
# Get Labels Declared Using Sphinx Commands
# #########################################
#
# {xrst_comment_ch #}
#
# Prototype
# *********
# {xrst_literal ,
#     # BEGIN_DEF, # END_DEF
#     # BEGIN_RETURN, # END_RETURN
# }
#
# data
# ****
# is the data for this page.
#
# page_file
# *********
# is the name of the xrst file containing the begin command for this page
# (only used for error reporting).
#
# page_name
# *********
# is the page name corresponding to *data*
# (only used for error reporting).
#
# m_external_label
# ****************
# For each label declared in *data* using sphinx commands,
# and that links to an external web site,
# *m_external_label* [ *label*.lower() ] is a match object
# for the label in *data* .
#
# m_internal_label
# ****************
# For each label declared in *data* using sphinx commands,
# and that links to a page in this web site,
# *internal* [ *label* ] is a match object
# for the label in *data* .
#
# Errors
# ******
# If two external labels have the same lower case value,
# an error is reported using :ref:`system_exit-name` .
#
# {xrst_end sphinx_label}
# BEGIN_DEF
def sphinx_label(data, page_file, page_name) :
   assert type(data) == str
   assert type(page_file) == str
   assert type(page_name) == str
   # END_DEF
   #
   # m_external_label, m_internal_label
   m_external_label  = dict()
   m_internal_label  = dict()
   #
   # literal_list
   literal_list = list()
   m_start = pattern_start_literal.search(data)
   while m_start :
      #
      # m_end
      m_end = pattern_end_literal.search(data, m_start.end() )
      #
      # literal_list
      if m_end == None :
         literal_list.append( (m_start.start(), len(data)) )
      else :
         literal_list.append( (m_start.start(), m_end.end()) )
      #
      # m_start
      m_start = pattern_start_literal.search( data, m_end.end() )
   #
   # pattern
   for pattern in [ pattern_declare, pattern_use ] :
      #
      # m_label
      m_label    = pattern.search(data)
      while m_label :
         #
         # skip
         skip = False
         for pair in literal_list :
            in_literal = pair[0] < m_label.start() and m_label.end() < pair[1]
            skip = skip or in_literal
         #
         if not skip :
            #
            # label_lower, line
            label       = m_label.group(1).strip(' ')
            label_lower = label.lower()
            destination = m_label.group(2).strip(' ')
            line        = m_label.group(3)
            #
            # msg
            msg = None
            for ending in [ '-name', '-title' ] :
               if label_lower.endswith(ending) :
                  msg  = f'Labels that end with {ending} are not allowed\n'
                  msg += 'because they are automatically generated by xrst\n'
                  msg += f'label.lower() = {label_lower}'
            if '@' in label_lower :
               msg  = f'Labels that use the @ character are not allowed\n'
               msg += 'because they are automatically generated by xrst\n'
               msg += f'label.lower() = {label_lower}'
            if label_lower == page_name :
               msg  = 'A label has the same lower case value as the page_name\n'
               msg += f'label.lower() = {label_lower}'
            if label_lower in m_external_label :
               previous_line = m_external_label[label_lower]
               msg  = 'A previous label has the same lower case value\n'
               msg += f'label.lower() = {label_lower}\n'
               msg += f'previous_line = {previous_line}'
            if msg != None :
               xrst.system_exit(msg,
                  file_name = page_file,
                  page_name = page_name,
                  m_obj     = m_label,
                  data      = data,
               )
            #
            # m_external_label, m_internal_label
            external =  destination != ''
            if external :
               m_external_label[label_lower] = m_label
            else :
               m_internal_label[label] = m_label
         #
         # m_label
         m_label = pattern.search(data, m_label.end())
   # BEGIN_RETURN
   #
   for result in [ m_external_label, m_internal_label ] :
      assert type(result) == dict
      for key in result.keys() :
         assert type( result[key] ) == re.Match
   return m_external_label, m_internal_label
   # END_RETURN
