#!/usr/bin/env python

from evennia.prototypes.spawner import spawn
from evennia.utils import logger

from typeclasses.objects import Object
from commands.consumables import CmdSetTeapot, CmdSetFillable
from utils.scoring import Scores
from utils.word_list import routput, choices

import re
import random


def choose_drink(drink_list, name=None, rando=True):
    """
    Return an entry from 'drink_list'.

    The 'name' must match an entry with a similar 'title' key.
    The drink_list should be a list of dicts.
    """
    if name and name != "":
        rx = rf".*{name}.*"
        # logger.info(f"Working on {rx}")
        details = [c for c in drink_list
                   if re.match(rx, c.get("title"), re.IGNORECASE) or
                   name in c.get("aliases", [])]
        if len(details) > 0:
            return details[0]

    if rando:
        return random.choice(drink_list)
    return None


TEAS = [
    {"title": "black",
     "desc": "a rich, bold and <<malty ^ fragrant ^ almost spicy>> black tea",
     },
    {"title": "green",
     "desc": "a green tea with <<grassy ^ floral ^ vanilla ^ vegetal>> notes",
     },
    {"title": "oolong",
     "desc": "a complex and <<earthy ^ floral>> oolong",
     },
    {"title": "rooibos",
     "desc": "an almost <<sweet and ^ >> <<woody ^ nutty ^ vanilla-y>> rooibos herbal infusion",
     },
    {"title": "dandelion",
     "desc": "a roasted herbal infusion of dandelions that tastes a bit like coffee",
     },
    {"title": "mint",
     "desc": "a blend of peppermint and spearmint to create the ultimate minty herbal infusion",
     },
    {"title": "chamomile",
     "desc": "a relaxing, yet subtle infusion of chamomile flowers that makes you want to go to sleep",
     },
    {"title": "earl",
     "desc": "a flowery Earl Grey tea",
     },
    {"title": "herbal",
     "desc": "an herbal infusion of <<chamomile ^ mint ^ chicory root ^ lavender>>",
     },
    {"title": "chai",
     "desc": "a spicy chai tea, tempered with milk and sweetness",
     },
    {"title": "hibiscus",
     "desc": "a dark red, tart herbal infusion of roselle flowers",
     },
    {"title": "matcha",
     "desc": "a frothy and aromatic matcha of the most vibrant green"
     },
    {"title": "white",
     "desc": "a subtle tea with notes of <<rose ^ berries ^ chamomile flowers>>",
     },
    {"title": "pu-erh",
     "desc": "an earthy, almost mushroom flavored, pu-erh tea",
     },
    {"title": "puerh",
     "desc": "an earthy, almost mushroom flavored, pu-erh tea",
     },
    {"title": "barley",
     "desc": "a nutty herbal infusion of barley with just a hint of smokiness",
     },
    {"title": "chaga",
     "desc": "an earthy, mushroomy chaga infusion",},
    {
        "title": "",
        "desc": "",
    },
]


class Container(Object):
    """
    All re-fillable cups, glasses and bottles inherit from this object.

    Database settings:
      type: "tea"
      details:
    """
    # Defaults
    sip_amount = 1
    fill_amount = 1
    details = {"title": "unknown liquid",
               "desc": "Not sure what this is."}
    empty_msgs = ["It is empty."]
    drink_msgs = [
        'You take a <<sip ^ sample ^ swig ^ drink>> << of {0}^ >> << ^ from your {2} >>.',
        'You <<sip ^ sample ^ swig ^ drink>> some of your {0} << ^ from your {2} >>.',
    ]
    fill_msgs = ["$You() $conj(fill) $pron(your) {2} with {0}."]

    def at_object_creation(self):
        """
        called at creation
        """
        self.db.amount = 0

    def do_drink(self, drinker):
        """
        Drink command results, gives a message to drinker.

        And lowers the amount by the 'sip_amount'.
        When empty, resets the container description to its
        'empty_desc' setting.
        """

        amount = self.db.amount or 0
        details = self.db.details or self.details or {}
        sip_amount = self.db.sip_amount or self.sip_amount or 1

        if amount == 0:
            drinker.msg(choices(self.empty_msgs, details["title"],
                                details["desc"], self.name))
        else:
            self.db.amount = amount - sip_amount
            drinker.msg(choices(self.drink_msgs, details["title"],
                                details["desc"], self.name))
            if self.db.amount == 0:
                self.db.desc = self.db.empty_desc

    def do_empty(self):
        """
        Return the title of the contents and empty the container.

        If already empty, return None.
        """
        if self.db.amount == 0:
            return None

        details = self.db.details or self.details or {}
        results = (details["title"], self.db.amount, self.db.desc)
        self.db.amount = 0
        if self.db.empty_name:
            self.key = self.db.empty_name
        if self.db.empty_desc:
            self.db.desc = self.db.empty_desc
        return results

    def do_fill(self, filler, details):
        """
        Fill this container with 'details'.

        The containers description will match the contents.
        """
        if (self.db.amount or 0) > 0:
            already_full = True
        else:
            already_full = False

        # Render and restore the description:
        desc = routput(details['desc'])
        details['desc'] = desc

        self.db.details = details
        self.db.amount = self.db.fill_amount or self.fill_amount or 1

        if already_full:
            msg = f"$You() $conj(empty) $pron(your) {self.name} and then $pron(you) $conj(fill) it with {desc}"
        else:
            msg = choices((self.db.fill_msgs or self.fill_msgs),
                          details["title"], desc, self.name)

        # Save the current description as the "empty" description,
        # for re-using later.
        if not self.db.empty_desc:
            self.db.empty_desc = self.db.desc
        self.db.desc = f"Contains {desc}"

        filler.announce_action(msg)

    def at_pre_drop(self, dropper):
        """
        Let's keep this world tidy.
        """
        dropper.announce_action(f"$You() $conj(drop) the {self.name}, shattering it.")
        self.delete()


class Bottle(Container):
    """
    Generic large container that can be filled.
    """
    fill_amount = 8
    empty_msgs = [
        'Your {2} is empty. Perhaps you can |gfill|n it?',
        'It appears that you need to |gfill|n your {2}.'
    ]

    def at_object_creation(self):
        """
        called at creation
        """
        self.db.amount = 0


class Water(Object):
    """
    Object that contains water or other fillable liquid.

    You must set the following:

       @set this/fill_name = "water"
       @set this/fill_desc = "fizzy water with bubbles trapped from waterfall."

    """
    def at_object_creation(self):
        """
        called at creation
        """
        self.cmdset.add_default(CmdSetFillable, persistent=True)


class TeaCup(Container):
    fill_amount = 4
    sip_amount = 1

    drink_msgs = [
        'You take a sip of {0} tea.',
        'You take a sip of {0} tea <<in ^ from>> your <<teacup ^ cup>>.',
        'You <<sip ^ sample ^ drink>> from your <<teacup ^ cup>>.',
        'You savor {1} in your <<teacup ^ cup>>.',
    ]

    empty_msgs = [
        'Your <<teacup ^ cup>> is empty. Perhaps you can |gmake|n some |gtea|n?',
        "Your cup certainly doesn't runneth over, as it be quite empty.",
        'Alas, you find your cup devoid of any sort of watery infusion.',
        'You notice your cup is empty.',
        'It appears that you need to refill your tea cup.'
    ]

    fill_msgs = [
        'You fill your <<teacup ^ cup>> with {1}.',
        'You pour <<some ^ >> {0} tea into your <<teacup ^ cup>>.',
    ]

    def at_pre_drop(self, dropper):
        if dropper.location.key in ("Cozy House", "Cramped Kitchen"):
            dropper.announce_action("$You() $conj(wash) and $conj(replace) the teacup in the cabinet with the others.")
            self.delete()
        else:
            return True


class Teapot(Object):
    def at_object_creation(self):
        """
        called at creation
        """
        self.cmdset.add_default(CmdSetTeapot, persistent=True)

    def do_make_tea(self, drinker, words):
        """
        Make tea.

        If 'args', try to make a particular type of tea, otherwise,
        pick one at random.
        """
        tea_choice = choose_drink(TEAS, words)
        desc = routput(tea_choice['desc'])

        self.db.tea_choice = tea_choice
        self.db.desc = f"A large, brown teapot full of {desc}."
        drinker.announce_action(f"$You() $conj(make) a teapot of {desc}.")
        drinker.score(Scores.make_tea)

    def do_fill(self, drinker):
        teatype = self.db.tea_choice
        if not teatype:
            drinker.msg("You need to |gmake tea|n first.")
            return

        teacup = drinker.has("teacup")
        if not teacup:
            drinker.msg("You need to |gget teacup|n first.")
            return

        teacup[0].do_fill(drinker, self.db.tea_choice)


COCKTAILS = [
    {
        "title": "Moonlit Mirage",
        "type": "cocktail",
        "desc": "A shimmering, colorful drink, garnished with glitter and a twist of tart, almost sour fruit.",
        "pre": "The bartender grabs a pixie flying by and shakes the glitter from its wings into the glass.",
        "flavors": ["lemon", "tartness", "sweetness"],
        "effects": [
            "You notice the swirling glitter make a pattern.",
            "Your cocktail just changed color to << red ^ pink ^ orange ^ yellow ^ blue ^ purple >>."
        ]
    },
    {
        "title": "Puck's Revenge",
        "type": "cocktail",
        "desc": "A vibrant concoction of fruit juices and sparkling fairy dust, served in a glass shaped like a flower.",
        "flavors": ["fruity", "... fairy dust?"],
        "effects": [
            "Wow, this drink really packs a punch.",
            "You feel a little light-headed.",
        ]
    },
    {
        "title": "Glimmering Gossamer",
        "type": "cocktail",
        "desc": "A delicate blend of elderflower liqueur and sparkling wine, adorned with a butterfly-shaped ice cube.",
        "pre": "The bartender uses a butterfly net to catch an ice cube and then places it in the glass.",
        "flavors": ["elderflower"],
        "effects": ["The butterfly shaped ice cube just started fluttering, and flew away."]
    },
    {
        "title": "Whimsical Willow",
        "type": "cocktail",
        "desc": "A herbal infusion of gin, rosemary, and elderberry, served with a sprig of fresh mint and a splash of tonic.",
        "pre": "As the bartender hands over the cocktail, the garnish of a sprig of mint grows a long twisting vine that he casually clips off.",
        "flavors": ["fresh mint", "rosemary", "elderberry"],
        "effects": [
            "Hrm ... did the bartender always have flowers in his hair?",
            "Hrm ... why are vines sprouting from everyone's hair?",
        ]
    },
    {
        "title": "Charmed Chalice",
        "type": "cocktail",
        "desc": "A mysterious potion that glows softly, made with enchanted spirits and a hint of honey, served in a chalice that changes shape.",
        "pre": "Ghostly apparitions appear in the air, as the bartender grabs one, twists it, to add a drop of ectoplasm to the drink.",
        "flavors": ["honey", "uhm...ectoplasm"],
        "effects": [
            "Your cocktail glass just changed into a flute for sparkling wine.",
            "Your glass has changed shape into a small vase. Good thing the drink is still in it.",
            "Your glass changes shape into a glass bowl, still holding the contents of your drink.",
        ]
    },
    {
        "title": "Enchanted Elixir",
        "type": "cocktail",
        "desc": "A deep blue drink with hints of blueberry and a touch of magic, served with a glowing ice sphere.",
        "pre": "The bartender opens a box that bathes the bar in light. He takes a glowing sphere of ice and garnishes the cocktail before passing it over.",
        "flavors": ["blueberry", "blue"],
        "effects": [
            "The ice sphere in the glass pulses with light and energy.",
            "You see stars everywhere in the room.",
            "The room became brighter...maybe that it just because you can see stars.",
        ]
        # - Glowing orbs of light spin about the drinker’s head
        # - Drinker sees stars … SPELL?
    },
    {
        "title": "Sylvan Serenade",
        "type": "cocktail",
        "desc": "A melodic blend of apple cider and spiced rum, garnished with a cinnamon stick and a slice of star fruit.",
        "pre": "The bartender plays a flute as the jars and ingredients line up and pour themselves into a cocktail shaker.",
        "flavors": ["apple", "cider", "spices", "cinnamon"],
        "effects": [
            "You hear the song of a distant choir.",
            "You hear the distant sounds of flutes and tambourines.",
        ],
    },
    {
        "title": "Brambleberry Bliss",
        "type": "cocktail",
        "desc": "A sweet and tart mix of brambleberries and gin, served over crushed ice with a sprinkle of edible flowers.",
        "pre": "The bartender garnishes the cocktail with a flower pulled from a pot on the bar. The plant immediately wilts and drops away.",
        "flavors": ["brambleberries", "berry", "floral"],
        "effects": [
            "The flowers floating in the glass whispers to you, \"I'm always getting into trouble. I guess that's because I'm a wild flower.\"",
            "A flower floating in the drink whispers to you, \"I got a promotion. I guess I was just outstanding in my field.\"",
            "The flowers in the cocktail whispers to you, \"I like to drive fast. I guess I'm always putting the petal to the metal.\"",
        ],
        # - The flowers talk .. maybe tell jokes.
    },
    {
        "title": "Twilight Tonic",
        "type": "cocktail",
        "desc": "A dark, mysterious drink made with blackcurrant and tonic water, served with a slice of lime and a hint of mint.",
        "pre": "After the cocktail is complete, the branches above the bar part, and light from the moon shines directly on the glass causing bubbles to form.",
        "flavors": ["lime", "blackcurrant", "mint"],
        "effects": [
            "A shadow falls on your face making you look seductively mysterious.",
            "The room briefly grows dim.",
        ]
    },
    {
        "title": "Bee Knees",
        "type": "mead",
        "desc": "Slightly fizzy, slightly sweet, served with a wedge of honeycomb.",
        "pre": "After filling the glass, the bartender takes a bee out of jar, gently squeezes it until vomits a drop of honey to the concoction.",
        "flavors": ["honey", "vanilla"],
        "effects": [
            "You feel like a dwarf in a tunnel digging a hole. Diggy, diggy hole!",
            "Just like being in the Mud World."
        ]
    },
    {
        "title": "whisky",
        "type": "whisky",
        "desc": "A dram of smokey, amber ambrosia.",
        "flavors": ["tar", "peat", "smoke", "brine", "vanilla", "toffee"],
        "effects": [
            "This reminds you of your travels in the Mud World.",
            "Lovely.",
            "Friendships and conversations distilled in a glass.",
            "A dram to << certainly ^ >> << savor ^ cherish ^ enjoy>>.",
            "Here's to friends that aren't here to share."
        ]
    },
    {
        "title": "whiskey",
        "type": "whiskey",
        "desc": "A dram of strong, amber ambrosia.",
        "flavors": ["vanilla", "toffee"],
        "effects": [
            "This reminds you of your travels in the Mud World.",
            "Lovely.",
            "Friendships and conversations distilled in a glass.",
            "A dram to << certainly ^ >> << savor ^ cherish ^ enjoy>>.",
            "Here's to friends that aren't here to share."
        ]
    },
    {
        "title": "ale",
        "aliases": ["beer"],
        "type": "beer",
        "desc": "A malty, only slightly carbonated, opaque beverage with overt herbal notes.",
        "flavors": ["rosemary", "bog myrtle", "yarrow"],
        "effects": [
            "Wonder where they got the recipe, twelfth century England?",
            "Tastes like drinking a loaf of bread."
        ]
    },
    {
        "title": "red wine",
        "type": "wine",
        "desc": "A rich glass a blood red color with notes of raspberries and toffee.",
        "pre": "After popping a cork from a bottle, the bartender then empties the bottle into the glass.",
        "flavors": ["earthiness", "toffee", "raspberries"],
        "effects": [
            "Ah, what was the name of the land in the Mud World where you first had this vintage?",
            "You swirl your glass and watch the legs return to the bottom.",
        ]
    },
    {
        "title": "water",
        "type": "water",
        "desc": "A clean cocktail with absolutely no taste.",
        "flavors": ["water", "blandness"],
        "effects": [
            "Wow, this drink really packs a punch...NOT",
            "Refreshing.",
            "Unintoxicating.",
            "Your liver is thanking you."
        ]
    },
]

DRINK_COCKTAIL_MSGS = [
    # 0 -> drink title or name
    # 1 -> type of drink, e.g. cocktail or whiskey
    # 2 -> one of the flavors
    'You take a <<sip ^ sample ^ drink>> of your << |w{0}|n ^ {1} ^ drink >>.',
    'You <<sip ^ sample ^ drink>> your << |w{0}|n ^ {1} ^ drink >>.',
    'You << notice ^ savor ^ relish ^ enjoy >> the {2}.',
]

EMPTY_COCKTAIL_MSGS = [
    'That {1} went down fast.',
    'Hrm ... did you enjoy that {1}?',
    'You drain your glass.',
    "You've finished that concoction.",
]


class Cocktail(Object):
    fill_amount = 4
    sip_amount = 1

    def make(owner, shaker, name=None):
        """
        Create for 'owner', a drink that matches 'name'.
        If name doesn't match, get a random one.
        """
        details = choose_drink(COCKTAILS, name, rando=False)
        if not details or details == []:
            shaker.msg(f"No match for '{name}'.")
            return

        drink = spawn({
            "typeclass": "typeclasses.drinkables.Cocktail",
            "key": details.get("title"),
            "aliases": ["drink", "glass", "cocktail"],
            "desc": details.get("desc")
        })[0]
        drink.db.cocktail_type = details.get("type")
        drink.db.flavors = details.get("flavors")
        drink.db.effects = details.get("effects")
        drink.db.amount = Cocktail.fill_amount
        drink.location = owner

        pre_msg = details.get("pre", "")
        drink.location.msg(routput(pre_msg + " The << bartender ^ barkeep ^ elf >> << passes ^ slides ^ gives ^ hands >> you a |y{0}|n.", drink.name))
        owner.score(Scores.get_cocktail)

        theroom = drink.location.location
        char = owner.sdesc.get() or owner.get_display_name(theroom)
        msg = routput(pre_msg + " The << bartender ^ barkeep ^ elf >> << passes ^ slides ^ gives ^ hands >> a {0} to {1}.",
                      drink.db.cocktail_type, char)
        theroom.msg_contents(msg, exclude=owner)

    def do_drink(self, drinker):
        """
        Called when owner calls the 'drink' command.
        """
        amount = self.db.amount or 0
        cocktail_type = self.db.cocktail_type or "cocktail"
        cocktail_flavors = self.db.flavors

        if amount > 2:
            flavor = random.choice(cocktail_flavors)
            drinker.msg(choices(DRINK_COCKTAIL_MSGS, self.key, cocktail_type, flavor))
        elif amount > 0:
            drinker.msg(choices(self.db.effects, self.key, cocktail_type))
            drinker.score(Scores.finish_cocktail)
        else:
            self.key = f"{self.db.cocktail_type} glass"
            self.aliases.add('glass')
            self.db.desc = f"An empty {self.db.cocktail_type} glass"
            drinker.msg(choices(EMPTY_COCKTAIL_MSGS, self.key, cocktail_type))

        self.db.amount = self.db.amount - self.sip_amount

    def at_pre_drop(self, dropper):
        if dropper.location.key == 'Wyldwood Bar':
            dropper.msg(routput(f"<< A ^ The >> mushroom << man ^ person >> << bounces ^ shimmies over ^ appears >> and takes the {self.name} << from you ^ >>."))
            self.delete()
        elif dropper.location.key in ("Cozy House", "Cramped Kitchen"):
            dropper.msg(routput(f"The {self.db.cocktail_type} << falls ^ drops >> to the << floor ^ ground >> and shatters! A dust ball from under a chair forms into a little impish-looking fellow, who disappears with glass."))
            self.delete()
        else:
            return True
