#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Inkycal ePaper driving functions
Copyright by aceisace
"""
from importlib import import_module
from PIL import Image

from inkycal.custom import top_level
import glob


class Display:
    """Display class for inkycal

    Creates an instance of the driver for the selected E-Paper model and allows
    rendering images and calibrating the E-Paper display

    args:
      - epaper_model: The name of your E-Paper model.


    """

    def __init__(self, epaper_model):
        """Load the drivers for this epaper model"""

        if 'colour' in epaper_model:
            self.supports_colour = True
        else:
            self.supports_colour = False

        try:
            driver_path = f'inkycal.display.drivers.{epaper_model}'
            driver = import_module(driver_path)
            self._epaper = driver.EPD()
            self.model_name = epaper_model

        except ImportError:
            raise Exception('This module is not supported. Check your spellings?')

        except FileNotFoundError:
            raise Exception('SPI could not be found. Please check if SPI is enabled')

    def render(self, im_black, im_colour=None):
        """Renders an image on the selected E-Paper display.

        Initlializes the E-Paper display, sends image data and executes command
        to update the display.

        Args:
          - im_black: The image for the black-pixels. Anything in this image that is
            black is rendered as black on the display. This is required and ideally
            should be a black-white image.

          - im_colour: For E-Paper displays supporting colour, a separate image,
            ideally black-white is required for the coloured pixels. Anything that is
            black in this image will show up as either red/yellow.

        Rendering an image for black-white E-Paper displays:

        >>> sample_image = PIL.Image.open('path/to/file.png')
        >>> display = Display('my_black_white_display')
        >>> display.render(sample_image)


        Rendering black-white on coloured E-Paper displays:

        >>> sample_image = PIL.Image.open('path/to/file.png')
        >>> display = Display('my_coloured_display')
        >>> display.render(sample_image, sample_image)


        Rendering coloured image where 2 images are available:

        >>> black_image = PIL.Image.open('path/to/file.png') # black pixels
        >>> colour_image = PIL.Image.open('path/to/file.png') # coloured pixels
        >>> display = Display('my_coloured_display')
        >>> display.render(black_image, colour_image)
        """

        epaper = self._epaper

        if not self.supports_colour:
            print('Initialising..', end='')
            epaper.init()
            print('Updating display......', end='')
            epaper.display(epaper.getbuffer(im_black))
            print('Done')

        elif self.supports_colour:
            if not im_colour:
                raise Exception('im_colour is required for coloured epaper displays')
            print('Initialising..', end='')
            epaper.init()
            print('Updating display......', end='')
            epaper.display(epaper.getbuffer(im_black), epaper.getbuffer(im_colour))
            print('Done')

        print('Sending E-Paper to deep sleep...', end='')
        epaper.sleep()
        print('Done')

    def calibrate(self, cycles=3):
        """Calibrates the display to retain crisp colours

        Flushes the selected display several times with it's supported colours,
        removing any previous effects of ghosting.

        Args:
          - cycles: -> int. The number of times to flush the display with it's
            supported colours.

        It's recommended to calibrate the display after every 6 display updates
        for best results. For black-white only displays, calibration is less
        critical, but not calibrating regularly results in grey-ish text.

        Please note that calibration takes a while to complete. 3 cycles may
        take 10 minutes on black-white E-Papers while it takes 20 minutes on coloured
        E-Paper displays.
        """

        epaper = self._epaper
        epaper.init()

        display_size = self.get_display_size(self.model_name)

        white = Image.new('1', display_size, 'white')
        black = Image.new('1', display_size, 'black')

        print('----------Started calibration of ePaper display----------')
        if self.supports_colour:
            for _ in range(cycles):
                print('Calibrating...', end=' ')
                print('black...', end=' ')
                epaper.display(epaper.getbuffer(black), epaper.getbuffer(white))
                print('colour...', end=' ')
                epaper.display(epaper.getbuffer(white), epaper.getbuffer(black))
                print('white...')
                epaper.display(epaper.getbuffer(white), epaper.getbuffer(white))
                print(f'Cycle {_ + 1} of {cycles} complete')

        if not self.supports_colour:
            for _ in range(cycles):
                print('Calibrating...', end=' ')
                print('black...', end=' ')
                epaper.display(epaper.getbuffer(black))
                print('white...')
                epaper.display(epaper.getbuffer(white)),
                print(f'Cycle {_ + 1} of {cycles} complete')

            print('-----------Calibration complete----------')
            epaper.sleep()

    @classmethod
    def get_display_size(cls, model_name):
        """Returns the size of the display as a tuple -> (width, height)

        Looks inside "drivers" folder for the given model name, then returns it's
        size.

        Args:
          - model_name: str -> The name of the E-Paper display to get it's size.

        Returns:
          (width, height) ->tuple, showing the size of the display

        You can use this function directly without creating the Display class:

        >>> Display.get_display_size('model_name')
        """
        if not isinstance(model_name, str):
            print('model_name should be a string')
            return
        else:
            driver_files = top_level + '/inkycal/display/drivers/*.py'
            drivers = glob.glob(driver_files)
            drivers = [i.split('/')[-1].split('.')[0] for i in drivers]
            drivers.remove('__init__')
            drivers.remove('epdconfig')
            if model_name not in drivers:
                print('This model name was not found. Please double check your spellings')
                return
            else:
                with open(top_level + '/inkycal/display/drivers/' + model_name + '.py') as file:
                    for line in file:
                        if 'EPD_WIDTH=' in line.replace(" ", ""):
                            width = int(line.rstrip().replace(" ", "").split('=')[-1])
                        if 'EPD_HEIGHT=' in line.replace(" ", ""):
                            height = int(line.rstrip().replace(" ", "").split('=')[-1])
                return width, height

    @classmethod
    def get_display_names(cls):
        """Prints all supported E-Paper models.

        Fetches all filenames in driver folder and prints them on the console.

        Returns:
          Printed version of all supported Displays.

        Use one of the models to intilialize the Display class in order to gain
        access to the E-Paper.

        You can use this function directly without creating the Display class:

        >>> Display.get_display_names()
        """
        driver_files = top_level + '/inkycal/display/drivers/*.py'
        drivers = glob.glob(driver_files)
        drivers = [i.split('/')[-1].split('.')[0] for i in drivers]
        drivers.remove('__init__')
        drivers.remove('epdconfig')
        print(*drivers, sep='\n')


if __name__ == '__main__':
    print("Running Display class in standalone mode")