from .template import *
import time
import numpy
from PIL import Image
import io

CHOOSE, ADDARG = range(2)
MESSAGE, WAKE, ALARM, IMAGE, ART = range(3,8)

class Clock(BotFunc):
    """pass on commands to clock-module"""
    def __init__(self, prst, clock_module, art_api):
        super().__init__(prst)
        self.clock = clock_module
        self.api_art = art_api

    def create_handler(self):
        handler = ConversationHandler(
            entry_points=[CommandHandler("clock", self.entry_point)],
            states={
                CHOOSE : [
                    CallbackQueryHandler(self.wake_light, pattern="^wake$"),
                    CallbackQueryHandler(self.alarm_blink, pattern="^alarm$"),
                    CallbackQueryHandler(self.show_message, pattern="^message$"),
                    CallbackQueryHandler(self.show_image, pattern="^image$"),
                    CallbackQueryHandler(self.art_gallery, pattern="^gallery$"),
                ],
                ADDARG : [MessageHandler(Filters.text, callback=self.get_arg1)],
                MESSAGE: [MessageHandler(Filters.text, callback=self.exec_show_message)],
                WAKE : [MessageHandler(Filters.text, callback=self.exec_wake_light)],
                ALARM : [MessageHandler(Filters.text, callback=self.exec_alarm_blink)],
                IMAGE : [MessageHandler(Filters.photo, callback=self.exec_show_image)],
                ART : [MessageHandler(Filters.text, callback=self.exec_art_gallery)],
            },
            fallbacks=[CommandHandler('clock', self.entry_point)],
        )
        return handler

    def entry_point(self, update: Update, context: CallbackContext) -> None:
        keyboard = [
            [InlineKeyboardButton("Make a wake-light", callback_data="wake")],
            [InlineKeyboardButton("Blink as alarm", callback_data="alarm")],
            [InlineKeyboardButton("Show a message", callback_data="message")],
            [InlineKeyboardButton("Show an image", callback_data="image")],
            [InlineKeyboardButton("Art gallery!", callback_data="gallery")],
        ]
        reply_markup = InlineKeyboardMarkup(keyboard)
        update.message.reply_text("What exactly do you want?", reply_markup=reply_markup)
        return CHOOSE


    def wake_light(self, update: Update, context: CallbackContext) -> None:
        query = update.callback_query
        query.answer()

        query.edit_message_text("Ok. How long should the color cycle last? (In seconds)")
        return WAKE

    def alarm_blink(self, update: Update, context: CallbackContext) -> None:
        query = update.callback_query
        query.answer()

        query.edit_message_text("Ok. How long should it blink? (In seconds)")
        self.next_state = {ALARM : "What frequency (Hertz)"}
        return ADDARG

    def show_message(self, update: Update, context: CallbackContext) -> None:
        query = update.callback_query
        query.answer()

        query.edit_message_text("Ok. What message will I show?")
        return MESSAGE

    def show_image(self, update: Update, context: CallbackContext) -> None:
        query = update.callback_query
        query.answer()

        query.edit_message_text("How long (in minutes) should the image be displayed?")
        self.next_state = {IMAGE : "Please send me the photo to display."}
        return ADDARG

    def art_gallery(self, update: Update, context: CallbackContext) -> None:
        query = update.callback_query
        query.answer()

        query.edit_message_text("Ok. How long should we display art? (in hours")
        self.next_state = {ART : "And how many artworks would you like to see during that time?"}
        return ADDARG

    def get_arg1(self, update: Update, context: CallbackContext) -> None:
        a = update.message.text
        self.additional_argument = a
        update.message.reply_text("Furthermore: "+ list(self.next_state.values())[0])
        return list(self.next_state.keys())[0]
    



###### actually running clock actions
    def exec_wake_light(self, update: Update, context: CallbackContext) -> None:
        duration = update.message.text

        def output(duration):
            self.clock.set_brightness(value=1)
            start_color = numpy.array([153, 0, 51])
            end_color = numpy.array([255, 255, 0])
            col_show = numpy.zeros((*self.clock.shape, 3))
            col_show[:,:,...] = start_color

            gradient = end_color - start_color
            # 20 steps should be fine => sleep_time = duration / 20
            for i in range(20):
                ct = i/20 * gradient
                col_show[:,:,...] = [int(x) for x in ct+start_color]
                self.clock.IO.put(col_show)
                time.sleep(int(duration) / 20)

        self.clock.run(output,(duration,))
        return ConversationHandler.END


    def exec_alarm_blink(self, update: Update, context: CallbackContext) -> None:
        duration = self.additional_argument
        frequency = update.message.text

        def output(duration, frequency):
            self.clock.set_brightness(value=1)
            duration =  int(duration)
            frequency = int(frequency)
            n = duration * frequency / 2
            empty = numpy.zeros((*self.clock.shape,3))
            red = empty.copy()
            red[...,0] = 255
            for i in range(int(n)):
                self.clock.IO.put(red)
                time.sleep(1/frequency)
                self.clock.IO.put(empty)
                time.sleep(1/frequency)

        if not(duration == 0 or frequency == 0):
            update.message.reply_text("Now blinking")
            self.clock.run(output,(duration, frequency))
        return ConversationHandler.END
        


    def exec_show_image(self, update: Update, context: CallbackContext) -> None:
        duration = self.additional_argument
        i = update.message.photo
        img = update.message.photo[0]
        bot = img.bot
        id = img.file_id

        file = bot.getFile(id).download_as_bytearray()
        width = self.clock.shape[1]
        height = self.clock.shape[0]

        img = Image.open(io.BytesIO(file))
        im_height = img.height
        im_width = img.width

        scalex = im_width // width
        scaley = im_height // height
        scale = min(scalex, scaley)

        t = img.resize((width, height),box=(0,0,width*scale,height*scale))
        a = numpy.asarray(t)
        
        def output(image, duration):
            self.clock.IO.put(image)
            time.sleep(int(duration) * 60)

        self.clock.run(output,(a, duration))
        return ConversationHandler.END


    def exec_show_message(self, update: Update, context: CallbackContext) -> None:
        message_str = update.message.text
        update.message.reply_text("Now showing: " + message_str)
        self.clock.run(self.clock.text_scroll,(message_str,))
        return ConversationHandler.END


    def exec_art_gallery(self, update: Update, context: CallbackContext) -> None:
        duration = float(self.additional_argument)
        number = int(update.message.text)
        
        def output(number, duration):
            for i in range(number):
                img = self.api_art.get_random_art() # returns an PIL.Image object
                im_height = img.height
                im_width = img.width

                width = self.clock.shape[1]
                height = self.clock.shape[0]
                
                scalex = im_width // width
                scaley = im_height // height
                scale = min(scalex, scaley)

                t = img.resize((width, height),box=(0,0,width*scale,height*scale))
                a = numpy.asarray(t)
                self.clock.IO.put(a)

                time.sleep(duration*3600 / number)


        update.message.reply_text("Ok. Showing art for the next "+ str(duration) + " hours.")
        self.clock.run(output,(number, duration))
        return ConversationHandler.END







# TODO FIx this to work with the new backend