#!/usr/bin/env python

from datetime import date
import random

from evennia import TICKER_HANDLER
from evennia.utils import delay
from evennia.prototypes.spawner import spawn

from commands.consumables import CmdSetTrolley, CmdSetMakeConsumable
from typeclasses.exits import Opener
from typeclasses.objects import Object
from utils.scoring import Scores
from utils.word_list import routput   # , choices


class Litterable(Object):
    """
    Objects, that if dropped, are deleted instead.
    The default thing for a producer.
    """
    def at_pre_drop(self, dropper):
        """
        Let's keep this world tidy.
        """
        dropper.announce_action(f"$You() $conj(drop) the {self.name}.")
        self.delete()


class Consumable(Litterable):
    """
    Create with a command:

    @create/drop berries;berry : typeclasses.consumables.Consumable
    @desc berries = Bright red, almost violet, berry.
    # How many berries are there?
    @set berries/amount = 10
    # How many berries do you eat at a time:
    @set berries/eat_amount = 3
    @set berries/eat_msg = "Sweet and slightly tart. <<Delicious ^ Tangy ^ Mmmm>>."
    @set berries/finish_msg = "Those were <<delicious ^ great>>."
    """
    amount = 4
    eat_amount = 1

    def at_object_creation(self):
        self.db.amount = self.amount
        self.db.eat_amount = self.eat_amount

    def do_eat(self, eater):
        """
        Consume (eat) or destroy this object.
        Indicate this with messages to eater.
        """
        self.db.amount = self.db.amount - self.db.eat_amount
        if self.db.eat_msgs:
            msg = random.choice(self.db.eat_msgs)
        elif self.db.eat_msg:
            msg = self.db.eat_msg
        else:
            msg = "You take a bite."
        eater.msg(routput(msg))

        if self.key == 'candy':
            eater.score(Scores.eat_candy)
            eater.announce_action("$You() $conj(eat) some candy.",
                                  exclude=eater)

        if self.db.amount <= 0:
            if self.db.finish_msgs:
                msg = random.choice(self.db.finish_msgs)
            elif self.db.finish_msg:
                msg = self.db.finish_msg
            else:
                msg = None

            if msg:
                eater.msg(routput(msg))
            self.delete()


class Herb(Litterable):
    """
    Essentially a marker for something that can go in a cauldron.
    """
    pass


class Edible(Consumable):
    """
    Like a Consumable, but this packs a punch.

    When eaten, it kicks off a temporary script that acts like a
    spell.
    """
    def do_eat(self, eater):
        """
        Consume (eat) AND destroy this object.
        We assume one dose is sufficient.
        """
        msg = self.db.eat_msg or f"You take a bite of {self.name}."
        eater.msg(routput(msg))
        self.delete()
        if self.db.spell:
            eater.scripts.add(self.db.spell, autostart=True)
        elif self.db.spell_msgs:
            eater.delay_sequence(self.db.spell_msgs)


class Producer(Object):
    """
    An object that produces a Consumable object.
    Use it to create a 'berry bush', for instance:

    @create/drop berry bush;bush : typeclasses.consumables.Producer
    @desc bush = A bush laden with bright red brambleberries.

    # Then describe what to produce:
    @set bush/make_name = "berries"
    @set bush/make_aliases = ["berry"]
    @set bush/make_verb = "pick some"
    # This one is optional as it defaults to Consumable:
    @set bush/make_class = "typeclasses.consumables.Consumable"
    @set bush/make_desc = "Bright red berry with flecks of <<purple ^ violet ^ orange>>."

    # How many berries are there when you pick them?
    @set bush/make_amount = 10
    # How many berries do you eat at a time:
    @set bush/make_eat_amount = 3

    # Either a single msg:
    @set bush/make_eat_msg = "Sweet and slightly tart. <<Delicious ^ Tangy ^ Mmmm>>."
    # Or many:
    @set bush/make_eat_msgs = [ "Sweet and slightly tart.", "<<Delicious ^ Tangy ^ Mmmm>>.",
        "Ooo...that one was sour.", "That was a bit ripe, but still good.",
        "So sweet with a hint of <<orange ^ maple>>." ]
    @set bush/make_finish_msg = "Those were <<delicious ^ great>>."
    """
    def at_object_creation(self):
        self.cmdset.add_default(CmdSetMakeConsumable)

    def at_pre_get(self, getter, **kwargs):
        """
        Can't get a producer.
        """
        self.do_make(getter)
        return False

    def do_make(self, picker, **kwargs):
        """
        Create a configurable consumable, given to picker.
        """
        # If the producer has configured limits, then we exit early:
        #  @set reeds/make_limit = 1
        #  @set reeds/make_limit_msg = "One ten-foot pole is sufficient."
        limit = self.db.make_limit
        if limit:
            owns = picker.has(self.db.make_name)
            if len(owns) >= limit:
                picker.msg(self.db.make_limit_msg or "You can't get any more.")
                return

        consumable = spawn({
            "typeclass": self.db.make_class or (kwargs['type']
                                               if 'type' in kwargs else
                                               "typeclasses.consumables.Consumable"),
            "key": self.db.make_name,
            "aliases": self.db.make_aliases,
            "desc": routput(self.db.make_desc),
        })[0]
        consumable.db.amount = self.db.make_amount or 3
        consumable.db.eat_amount = self.db.make_eat_amount or 1

        if self.db.make_eat_msg:
            consumable.db.eat_msg = self.db.make_eat_msg
        if self.db.make_eat_msgs:
            consumable.db.eat_msgs = self.db.make_eat_msgs
        if self.db.make_finish_msg:
            consumable.db.finish_msg = self.db.make_finish_msg
        if self.db.make_finish_msgs:
            consumable.db.finish_msgs = self.db.make_finish_msgs

        if self.db.make_spell:
            consumable.db.spell = self.db.make_spell
        if self.db.make_spell_msgs:
            consumable.db.spell_msgs = self.db.make_spell_msgs

        consumable.location = picker
        picker.announce_action(f"$You() {self.db.make_verb or '$conj(get)'} {self.db.make_name}{self.db.make_note or ''}.")


# ----------------------------------------------------------------------

TEACUP_DESCS = [
    "A rustic, clay teacup carefully crafted on a wheel. Beautiful <<dark red ^ olive green ^ navy blue>> glaze.",
    "A fine example of Royal Albert teacup, sporting a design of violet azaleas. Perfect for a black tea.",
    "A Wings of Grace style teacup with blue butterflies. Perfect for some Earl Grey.",
    "A dark brown Yixing clay teacup. Perfect for an Oolong tea.",
    "A light jade green handle-less teacup. A good choice for a green tea.",
]


class Trolley(Producer):
    """
    A special producer that has a plate of specific dishes for
    that day. I need this special because I'm not sure how to populate
    the database with complex arrays of data.
    """
    scones = [
        {
            "name": "blueberry scone",
            "taste": "blueberries",
        },
        {
            "name": "chocolate chip scone",
            "taste": "chocolate",
        },
        {
            "name": "lemon poppy seed scone",
            "taste": "lemon",
        },
        {
            "name": "cinnamon scone",
            "taste": "spice",
        },
        {
            "name": "cranberry orange scone",
            "tastes": ["tartness of the cranberries", "orange"],
        },
        {
            "name": "pumpkin scone",
            "taste": "spices",
        },
        {
            "name": "cheddar cheese and chive scone",
            "expression": "cheesy"
        },
        {
            "name": "cherry almond scone",
            "expression": "nutty",
        },
        {
            "name": "apple cinnamon scone",
            "tastes": ["apple", "cinnamon"],
        },
    ]

    teacup_prototype = {
        "typeclass": "typeclasses.drinkables.TeaCup",
        "key": "teacup",
        "aliases": "cup",
        "desc": "A nice teacup.",
    }

    def at_object_creation(self):
        self.cmdset.add_default(CmdSetTrolley)
        TICKER_HANDLER.add(interval=60 * 60 * 24,
                           callback=self.do_bake)

    def produce_teacup(self, caller):
        """
        Create and associate with 'caller', a newly created teacup.
        """
        if caller.has("teacup"):
            caller.msg("You already have a teacup.")
        else:
            cuptype = self.teacup_prototype
            cuptype['desc'] = routput(random.choice(TEACUP_DESCS))

            cup = spawn(cuptype)[0]
            cup.location = caller
            caller.msg("You pick up a teacup.")

    def produce_wood(self, caller):
        """
        Create and give to 'caller' a log of wood.
        """
        wood = spawn({
            "creator_id": 1,
            "typeclass": "typeclasses.things.Wood",
            "key": "log",
            "aliases": ["wood", "kindling", "logs"],
        })[0]
        wood.location = caller
        caller.msg(routput("You <<get ^ pick up ^ grab>> "
                           "<<some wood ^ couple logs ^ a log ^ a few pieces of wood>> "
                           "<<from the woodbox ^ from the box on the hearth ^ for the fireplace ^ >>."))

    def do_open(self, opener):
        """
        Sure we can open the cabinets, but what does that mean?
        """
        opener.execute_cmd("look cabinet")

    def do_close(self, opener):
        """
        Sure we can open the cabinets, but what does that mean?
        """
        opener.msg("Okay.")

    def do_bake(self):
        """
        Set the scone that we bake today.
        """
        todays_scone = self.scones[date.today().day % len(self.scones)]
        name = todays_scone.get("name")

        self.db.make_name = name
        self.db.make_aliases = ["scone", "scones"]
        self.db.make_desc = routput("A <<tasty ^ yummy ^ delicious>>-looking {0}", name)
        self.db.make_amount = 3
        self.db.make_eat_amount = 1

        msgs = [
            "Delicious.",
            "So good",
            "Really good.",
            'Mmmmm',
        ]
        if todays_scone.get('taste'):
            msgs += ['You <<can ^ >> <<really ^ >> <<notice ^ taste>> the ' + todays_scone.get('taste') + '.']
        if todays_scone.get('tastes'):
            for taste in todays_scone.get('tastes'):
                msgs += ['You <<can ^ >> <<really ^ >> <<notice ^ taste>> the ' + taste + '.']
        if todays_scone.get('expression'):
            msgs += [f"<<Really ^ So>> {todays_scone.get('expression')}."]

        self.db.make_eat_msgs = msgs
        self.db.make_finish_msg = "That was <<delicious ^ great ^ really good>>."

        self.location.msg_contents(routput("<<New ^ Aromatic ^ Fresh ^ Freshly baked>> scones <<magically ^ suddenly ^ mystically ^ enchantingly ^ >> appear <<on a plate ^ on plates ^ >> on the trolley."))


class Sconce(Producer, Opener):
    """
    A producer that also implements a Push in order to open a door.
    """
    def do_pull(self, puller):
        """
        Opens a door, and sets a timer to close it.
        """
        puller.announce_action(f"$You() $conj(pull) on the {self.name}.")
        if self.db.exit:
            if self.db.pull_msg:
                self.location.msg_contents(self.db.pull_msg)
            self.do_open()
            delay(30, self.do_close)
            delay(31, self.db.room.msg_contents, "The bookcase slams shut.")
