#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Hotel California

In this example we show how to keep people in the same channel.
Following commands are used:

- command: open
- response: **Hotel California** mode activated!

- command: close
- response: **Hotel California** mode deactivated!

- command: hotel
- provides current status of the hotel: open or close

Multiple questions are adressed in this example:

- How to preserve state of the hotel? This is done with ``bot.remember()`` and
  ``bot.recall()`` calls. Under the hood, each bot is equipped with a dedicated
  data store.

- How to know that someone is joining or leaving? Shellbot implements a bare
  event dispatcher. We use it in this example, as shown in the code below. In
  a nutshell, a python object with functions ``on_join()`` and ``on_leave()``
  is created. Then this handler is registered for the events ``join`` and
  ``leave`` generated by the engine.

- How to add participants to a channel? Simple, use the function
  ``bot.add_participant()`` with the e-mail address of the new person. Note
  that some restrictions may apply, depending on the commercial agreement with
  the cloud service provider, e.g., Cisco.


To run this script you have to provide a custom configuration, or set
environment variables instead::

- ``CHANNEL_DEFAULT_PARTICIPANTS`` - Mention at least your e-mail address
- ``CISCO_SPARK_BOT_TOKEN`` - Received from Cisco Spark on bot registration
- ``SERVER_URL`` - Public link used by Cisco Spark to reach your server

The token is specific to your run-time, please visit Cisco Spark for
Developers to get more details:

    https://developer.ciscospark.com/

The other token should be associated to a human being, and not to a bot.
This is required so that the software can receive all events for a chat space.
Without it, the bot may not see who is leaving or joining.

For example, if you run this script under Linux or macOs with support from
ngrok for exposing services to the Internet:

    export CHANNEL_DEFAULT_PARTICIPANTS="<EMAIL>"
    export CISCO_SPARK_BOT_TOKEN="<token id from Cisco Spark for Developers>"
    export SERVER_URL="http://1a107f21.ngrok.io"
    python hotel_california.py


Credit: https://github.com/flint-bot/flint
"""

import os
import time

from shellbot import Engine, Context, Command


class Open(Command):  # do not allow people to leave
    keyword = 'open'
    information_message = u"Open Hotel California"

    def execute(self, bot, arguments=None, **kwargs):
        if bot.channel.is_direct:
            bot.say('This is a private place, not an hotel')
        elif bot.recall('hotel_california.state', 'off') == 'on':
            bot.say('Hotel California mode is already activated!')
        else:
            bot.remember('hotel_california.state', 'on')
            bot.say('Hotel California mode activated!')


class Close(Command):  # allow people to leave
    keyword = 'close'
    information_message = u"Close Hotel California"

    def execute(self, bot, arguments=None, **kwargs):
        if bot.channel.is_direct:
            bot.say('This is a private place, not an hotel')
        elif bot.recall('hotel_california.state', 'off') == 'off':
            bot.say('Hotel California mode is already deactivated!')
        else:
            bot.remember('hotel_california.state', 'off')
            bot.say('Hotel California mode deactivated!')


class Hotel(Command):  # reflect current state of the bot
    keyword = 'hotel'
    information_message = u"Get status of Hotel California"

    def execute(self, bot, arguments=None, **kwargs):
        if bot.channel.is_direct:
            bot.say('This is a private place, not an hotel')
        elif bot.recall('hotel_california.state', 'off') == 'off':
            bot.say('Hotel California will let you escape')
        else:
            bot.say('Hotel California will keep you here forever!')


class Magic(object):  # add stickiness to the hotel

    def __init__(self, engine):
        self.engine = engine

    def on_join(self, received):  # welcome new joiners of the channel

        bot = self.engine.get_bot(received.channel_id)
        addresses = bot.recall('visitors', [])

        if received.actor_address not in addresses:
            addresses.append(received.actor_address)
            bot.remember('visitors', addresses)
            bot.say(u"Welcome to Hotel California, {}".format(
                received.actor_label))

    def on_leave(self, received):  # re-invite leavers when activated

        bot = self.engine.get_bot(received.channel_id)
        addresses = bot.recall('visitors', [])

        if bot.recall('hotel_california.state', 'off') == 'off':
            try:
                addresses.remove(received.actor_address)
            except ValueError:
                pass
            bot.remember('visitors', addresses)
            bot.say('On a dark desert highway, cool wind in my hair...')

        else:
            bot.say('Such a lovely place...')
            time.sleep(5)
            bot.add_participant(received.actor_address)
            bot.say((u"{}, you can check out any time you like, "
                     u"but you can never leave!").format(received.actor_label),
                     content=(u'<@personEmail:{}|{}>, you can **check out '
                              u'any time you like**, '
                              u'but you can **never** leave!').format(
                                received.actor_address, received.actor_label))


if __name__ == '__main__':

    Context.set_logger()

    engine = Engine(type='spark', commands=[Open(), Close(), Hotel()])

    os.environ['BOT_ON_ENTER'] = 'On a dark desert highway, cool wind in my hair...'
    os.environ['CHAT_ROOM_TITLE'] = 'Hotel California'
    engine.configure()  # ensure that all components are ready

    magic = Magic(engine=engine)  # monitor newcomers and leavers
    engine.register('join', magic)
    engine.register('leave', magic)

    engine.bond(reset=True)  # create a group channel for this example
    engine.run()  # until Ctl-C
    engine.dispose()  # delete the initial group channel
