#!/usr/bin/env python

from evennia import (
    Command,
    CmdSet,
    TICKER_HANDLER,
    syscmdkeys,
    create_script,
)
from evennia.prototypes.spawner import spawn
from evennia.utils import delay

from enum import Enum
from urllib.request import urlopen

from typeclasses.objects import Object
from typeclasses.characters import Character
from typeclasses.npcs import CarriableNPC
from utils.scoring import Scores
from utils.word_list import routput

import random
import requests


class Fishing(Enum):
    "The acceptable states of fishing."
    CAST = True
    REEL = False


class CmdThrowFish(Command):
    """
    Throw the fish, if you are holding one, back in the water.

    A helper can escort the fish to the nearest body of water if you aren't adjacent to one.
    """
    key = "throw"
    locks = "holds(fish)"

    def func(self):
        self.obj.do_delete(self.caller)


class CmdSetFish(CmdSet):
    def at_cmdset_creation(self):
        self.add(CmdThrowFish)


class CmdCast(Command):
    """
    Cast the pole.
    """
    key = "cast"
    locks = "holds(pole)"

    def func(self):
        self.obj.do_cast(self.caller)


class CmdReel(Command):
    """
    Reel the pole.
    """
    key = "reel"
    locks = "holds(pole)"

    def func(self):
        self.obj.do_reel(self.caller)


class CmdSetFishing(CmdSet):
    def at_cmdset_creation(self):
        self.add(CmdCast)
        self.add(CmdReel)


class Fish(CarriableNPC):
    """
    Everyone wants a fish that tells dad jokes, right!?
    """
    # The number of seconds to check for the time for a message:
    fish_tick = 3

    def at_object_creation(self):
        self.cmdset.add_default(CmdSetFish)
        self.db.name = Fish.get_name()
        self.db.spoken = 0
        TICKER_HANDLER.add(interval=self.fish_tick,
                           callback=self.do_speak)

    def at_heard_say(self, message, from_obj):
        """
        A simple listener and response. This makes it easy to change for
        subclasses of NPCs reacting differently to says.

        """
        return "say", "What was that? I must have water in my ear."

    def at_say(self, message):
        """
        Spoken out loud to the world, even if owned.
        """
        owner = self.location
        if owner.is_typeclass(Character) and owner.is_connected:
            self.location.announce_action(f"$Your() fish says, \"{message}\"")

    def do_speak(self):
        """
        Called at a repeatable sequence by the ticker, and
        it calls at_say() in order to do a type of monologue.
        """
        if self.db.spoken == 1:
            self.at_say("Whew! Thanks for removing the sharp hook. Not sure how I got stuck on that.")
        elif self.db.spoken == 5:
            self.at_say(f"My name's {self.db.name}. What's yours?")
            self.db.desc = f"{self.db.name}. {self.db.desc}"
        elif self.db.spoken == 10:
            self.at_say("Did you say something? I think I have water in my ears, as I can't hear a thing.")
        elif self.db.spoken == 20:
            self.at_say("So... how're you getting along?")
        elif self.db.spoken == 30:
            self.at_say("Right, right. Still can't hear. Hrm.")
        elif self.db.spoken == 60:
            self.at_say("Do you know why we fish are so easy to weigh? ")
        elif self.db.spoken == 63:
            self.at_say("Because we have are own scales.")
        elif self.db.spoken == 70:
            self.at_say("I suppose you could |gthrow|n me back in the water at any time.")
        elif self.db.spoken == 120:
            self.at_say("I suppose I should pay you back for helping me out with that hook thing. I guess you know I keep all my money at ... the riverbank.")
        elif self.db.spoken == 200:
            self.at_say("You know the easiest way to catch a fish, right?")
        elif self.db.spoken == 201:
            self.at_say("Have someone toss it to you.")
        elif self.db.spoken == 205:
            self.at_say("Ouch. Tough crowd.")
        elif self.db.spoken == 300:
            self.at_say("Me and my friends started a musical band.")
        elif self.db.spoken == 301:
            self.at_say("We all play bass.")
        elif self.db.spoken == 302:
            self.at_say("Alright guys, I said, drop the instruments. We are singing aquapella.")
        elif self.db.spoken == 400:
            self.at_say("Did you meet the owners of that new fishing store?")
        elif self.db.spoken == 401:
            self.at_say("Their names are Rod and Annette.")
        elif self.db.spoken == 800:
            self.at_say("I'm not that smart.")
        elif self.db.spoken == 801:
            self.at_say("My friends tell me I'm a dumb bass.")
        elif self.db.spoken == 803:
            self.at_say("Sorry for all the puns. I feel so GILL-ty.")

        # Let's attempt to tell a bad joke once a day:
        elif self.db.spoken > 1000 and self.db.spoken % (24 * 60 * 60 / self.fish_tick) == 0:
            self.at_say(get_joke())
        self.db.spoken += 1

    def do_delete(self, fisher):
        """
        A visual way to delete the fish.
        """
        if fisher.location.key == "Lazy Dock" or fisher.location.key == "Shore":
            fisher.announce_action("$You() <<$conj(toss) ^ $conj(heave) ^ $conj(throw)>> the fish back into the <<water ^ sea>>.")
            fisher.location.msg_contents("The fish says, \"Bye for now. If you want to talk again, just drop me a line!\"")
        else:
            fisher.msg(routput("You <<toss ^ heave ^ throw>> the fish, and an << eagle ^ hawk >> swoops << down ^ >> and snatches it. I'm sure it will carry the fish back to the sea for you."))
        self.delete()

    def get_name():
        return random.choice([
            "Bennie", "Flipper", "Finegan", "Count Bassie"
        ])

    def get_desc():
        return routput(random.choice([
            "A walleye with big bulbous eyes that clearly doesn't get no respect.",
            "A bass with amazing neck confidence giving it a most excellent head bob.",
            "A rainbow trout missing the <<red ^ yellow ^ green ^ blue>> from its iridescent stripe.",
            "A brown trout colored <<red ^ blue ^ purple ^ orange>>.",
            # "A spiny perch",
            # "A salmon",
            # "A pike",
        ]))


def get_joke():
    "Fetch a random joke from the internet."
    r = requests.get("https://icanhazdadjoke.com/",
                     headers={'Accept':'text/plain'})
    return r.text


class FishingPole(Object):
    """
    Can produce a Fish.
    """
    failure_msgs = [
        "$You() $conj(reel) in an empty line.",
        "$You() didn't catch anything.",
        "$You() caught nothing but a bit of weeds, yeck.",
        "$You() caught nothing, but this sure is enjoyable.",
        "Did $you() catch a boot? Nah, the fishing isn't even that good.",
        "While $you() didn't catch anything, could there be anything better that sitting on the dock of the bay? Watching the clouds roll away?",
    ]

    def at_object_creation(self):
        self.cmdset.add_default(CmdSetFishing)

    def do_use(self, user, _):
        if self.location != user:
            user.announce_action("$You() $conj(pick) up the pole to go fishing.")
            delay(6, user.announce_action, "$You() $conj(return) the pole << next to the chair ^ >>.")
        if self.do_cast(user):
            delay(5, self.do_reel, user)

    def do_cast(self, fisher):
        if fisher.location.key != "Lazy Dock" and fisher.location.key != "Shore":
            fisher.msg("You can't do that without being near a large body of water.")
            return False
        elif fisher.db.fishing == Fishing.CAST:
            fisher.msg("You need to reel the line in first.")
            return False
        else:
            fisher.db.fishing = Fishing.CAST
            fisher.announce_action(routput(random.choice([
                "$You() $conj(cast) << out ^ >> << far ^ >> into the <<water ^ sea>>.",
                "$You() $conj(cast) close to the <<dock ^ shore>>.",
                "$You() $conj(cast) off to the <<right ^ left>> where you <<think you ^ >> see a dark pocket.",
            ])))
            return True

    def do_reel(self, fisher):
        if fisher.db.fishing != Fishing.CAST:
            fisher.msg("You need to |gcast|n before you can reel the line back in.")
        else:
            fisher.db.fishing = Fishing.REEL
            if random.randint(1, 100) < 35:
                self.give_fish(fisher)
            else:
                fisher.announce_action(random.choice(self.failure_msgs))

    def give_fish(self, fisher):
        fish = spawn({
            "typeclass": self.db.make_class or "typeclasses.fishing.Fish",
            "key": "fish",
            "aliases": [Fish.get_name()],
            "desc": Fish.get_desc(),
        })[0]
        fish.location = fisher
        fisher.announce_action(f"$You() caught a fish!")
        fisher.score(Scores.catch_fish)
