#!/usr/bin/env python

from itertools import batched
from random import choice
from re import compile, sub, split, match

from evennia.utils import logger, dedent

def paragraph(text):
    """
    Removes initial spaces as well as final newlines.

    Blank lines, however, are preserved.
    """
    # Shame we can chain this or _thread_, but:
    return sub(r";;", "\n\n",
               sub(r"\n", " ",
                   sub(r"\n\n+", ";;",
                       dedent(text)))).strip()

def fix_msg(text):
    if text and isinstance(text, str) and len(text) > 0:
        return capitalize_line(text)
    elif text and isinstance(text, tuple):
        return (capitalize_line(text[0]), text[1])
    else:
        return text

def capitalize_line(line):
    """
    Return a capitalized version of 'line'.
    """
    # logger.info(f"Fixing {line}")
    if not line or line.strip() == "":
        return line
    elif match(r"^\|[bm][a-z].*", line):
        return "The " + line
    elif match(r"^\|[A-z][a-z].*", line):
        return line[0:2] + line[2].upper() + line[3:]
    else:
        return line[0].upper() + line[1:]

def squish(text):
    "Remove series of spaces from the text."
    no_start = sub(r"\( +", "(", text)
    no_after = sub(r" +\)", ")", no_start)
    no_inside = sub(r"\" *(.*?) *\"", "\"\\1\"", no_after)
    return sub('[ \t]+', ' ', no_inside).strip()

def _routput_choose(text):
    """
    Pick a choice when text is: one ^ two ^ three
    Done by splitting the text, and calling random.choice().
    """
    choices = split(r" *\^ *", text)
    return choice(choices)

def _routput_empty(entry):
    """
    Return True if entry is empty, e.g. None or blank.
    False otherwise. Note: Spaces are ignored.
    """
    if entry:
        if isinstance(entry, str) and entry.strip() == '':
            return True
        if isinstance(entry, (list, tuple)) and len(entry) == 0:
            return True
        return False
    return True

def _routput_pair(no_choice, choices = None):
    """
    Return a list based on the ^-separated options in 'choices'.

    Give a pair of split items, the 'no_choice' is the first section
    (before the << ... >>>), and the 'choices' is the section between
    the '<< ... >>' delimiter.
    """

    # While unlikely, the text before << and the options between the
    # << ...>> text may be blank:
    if _routput_empty(no_choice) and _routput_empty(choices):
        return ''

    # If we start a string with <<...>>, then thte 'no_choice' option
    # is blank, so we only return a choice:
    if _routput_empty(no_choice):
        return _routput_choose(choices)

    # With text before, but not inside the <<...>> section, we just
    # return the first part:
    if _routput_empty(choices):
        return no_choice

    return no_choice + ' ' + _routput_choose(choices)

def routput(text, *substitutions):
    """
    Return string with internal word choices replaced randomly.
    For instance, the string:

    'This feels <<^ very ^ quite>> <<nice^cozy^comfortable>>.'

    Could return any of the following strings:

    'This feels quite nice.'
    'This feels cozy.'
    'This feels very comfortable.'
    """
    if text:
        # section is a list of some phrase followed by another phrase
        # that contains ^-separate choices:
        sections = split(r" *<< *(.*?) *>> *", text)

        # Parts are the first section followed by the _rendered_
        # choices, so this^that is either 'this' or 'that':
        parts = [_routput_pair(*pair) for pair in batched(sections, 2)]

        # The parts may have empty strings, so we filter those out,
        # leaving on the phrases to join:
        phrases = [phrase for phrase in parts  if not _routput_empty(phrase)]

        if substitutions:
            proposal = ' '.join(phrases).format(*substitutions)
        else:
            proposal = ' '.join(phrases)

        # If a choice is at the end of a sentence, and we could have
        # "no choice", as in:
        #     "He searches <<thoroughly ^ quickly ^>>."
        # We don't want a version that looks like:
        #     "He searches # ."
        # with a space before the punctuation:
        #
        # Also, we should remove all double spaces that may show:
        return squish(sub(r"\s+([!?.,])", "\\1", proposal))


def choices(text, *substitutions):
    """
    Returns a section of 'text' based on separating semicolons.

    Note that text can already be separated as a list or tuple.
    """
    if isinstance(text, str):
        text = split(r"\s*;;\s*", text)

    if text:
        return routput(choice(text), *substitutions)


def pluralize(noun):
    """Convert a singular noun to its plural form."""
    # Basic pluralization rules

    # Change 'y' to 'ies' if preceded by a consonant
    if noun.endswith('y') and noun[-2] not in 'aeiou':
        return noun[:-1] + 'ies'

    # Add 'es' for words ending in 's', 'x', 'z', 'ch', or 'sh'
    elif noun.endswith('s') or noun.endswith('x') or \
         noun.endswith('z') or noun.endswith('ch') or \
         noun.endswith('sh'):
        return noun + 'es'

    # Default case for regular nouns
    else:
        return noun + 's'

# print(pluralize("dog"))    # Outputs: dogs
# print(pluralize("candy"))  # Outputs: candies
# print(pluralize("box"))    # Outputs: boxes
# print(pluralize("bush"))   # Outputs: bushes
# print(pluralize("city"))    # Outputs: cities
