from __future__ import division
from PIL import Image, ImageDraw, ImageFont, ImageOps

from trello import TrelloClient
import os
import requests
from requests_oauthlib import OAuth1Session
import re

mydir = os.path.dirname(os.path.abspath(__file__))

def textBox(text, font, position, limits, colour, draw_black):
    str = ""
    limit = [0, 0]
    limit[0] = limits[0] - position[0]
    limit[1] = limits[1] - position[1]
    words = text.split(" ")
    i = 0  # index of first word
    sizex, sizey = draw_black.multiline_textsize(
        str, font)  # size of the textbox
    # while more words to be added to string AND string textbox height < height limit
    for word in words:
        sizex, sizey = draw_black.multiline_textsize(str + word, font)
        sizex2, sizey2 = draw_black.multiline_textsize(str, font)
        if sizex < limit[0]:  # if width of gen textbox < width limit
            str += word + " "
        else:
            sizex, sizey = draw_black.multiline_textsize(
                str + "\n" + word, font)
            if(sizey > limit[1]):
                break
            else:
                str = str.rstrip(" ")
                str += "\n" + word + " "
        sizex, sizey = draw_black.multiline_textsize(str, font)
    draw_black.text(position, str, font=font, fill=0)
    return sizex, sizey


def putImage(imagepath, maskpath, position, colour):
    if type(imagepath) is str:
        img = Image.open(imagepath)
    else:
        img = imagepath
    if type(imagepath) is str:
        mask = Image.open(maskpath).convert(mode='1')
    else:
        mask = maskpath
    if colour is "black":
        image_black.paste(img, position, mask)
    if colour is "yellow":
        image_yellow.paste(img, position, mask)
        black_invert = ImageOps.invert(image_black.convert('L'))
        draw_yellow.bitmap((0, 0), black_invert, 255)

def roundRect(draw, topLeft, bottomRight, arcsize, type):
    a = 180
    arcStart = topLeft
    arcEnd = (arcStart[0] + arcsize, arcStart[1] + arcsize)
    lastLineEnd = (arcStart[0], arcStart[1]+arcsize/2)
    lineStart = (arcStart[0]+arcsize/2, arcStart[1])
    if type is 'outline':
        draw.arc((arcStart, arcEnd), a, a+90)
    elif type is 'fill':
        draw.ellipse(xy=(arcStart, arcEnd), fill=0)

    a = (a + 90) % 360
    arcStart = (bottomRight[0]-arcsize, topLeft[1])
    arcEnd = (arcStart[0] + arcsize, arcStart[1] + arcsize)
    lineEnd = (arcStart[0]+arcsize/2, arcStart[1])
    if type is 'outline':
        draw.line((lineStart, lineEnd))
    elif type is 'fill':
        box1corner1 = lineStart
    lineStart = (arcEnd[0], arcEnd[1]-arcsize/2)
    if type is 'outline':
        draw.arc((arcStart, arcEnd), a, a+90)
    elif type is 'fill':
        draw.ellipse(xy=(arcStart, arcEnd), fill=0)

    a = (a + 90) % 360
    arcStart = (bottomRight[0]-arcsize, bottomRight[1]-arcsize)
    arcEnd = (arcStart[0] + arcsize, arcStart[1] + arcsize)
    lineEnd = (arcEnd[0], arcEnd[1]-arcsize/2)
    if type is 'outline':
        draw.line((lineStart, lineEnd))
    lineStart = (arcEnd[0]-arcsize/2, arcEnd[1])
    if type is 'outline':
        draw.arc((arcStart, arcEnd), a, a+90)
    elif type is 'fill':
        draw.ellipse(xy=(arcStart, arcEnd), fill=0)
        box2corner1 = lastLineEnd
        box2corner2 = lineEnd

    a = (a + 90) % 360
    arcStart = (topLeft[0], bottomRight[1]-arcsize)
    arcEnd = (arcStart[0] + arcsize, arcStart[1] + arcsize)
    lineEnd = (arcEnd[0]-arcsize/2, arcEnd[1])
    draw.line((lineStart, lineEnd))

    if type is 'outline':
        draw.arc((arcStart, arcEnd), a, a+90)
    elif type is 'fill':
        box1corner2 = lineStart
        draw.ellipse(xy=(arcStart, arcEnd), fill=0)

        draw.rectangle(xy=(box1corner1, box1corner2), fill=0)
        draw.rectangle(xy=(box2corner1, box2corner2), fill=0)

    lineStart = (arcStart[0], arcStart[1]+arcsize/2)

    draw.line((lineStart, lastLineEnd))




class TextWidget():
    # this example widget will have a set size: 3x1 cells (one column)
    # an instance of this widget class can be created and pasted to canvas
    def __init__(self, cwidth, cheight, xy=(0, 0), dim=(1, 1), stringIn=""):
        self.width = dim[0]
        self.height = dim[1]
        self.wt = cwidth * dim[0]
        self.ht = cheight * dim[1]
        self.cellX = xy[0]
        self.cellY = xy[1]
        self.image_yellow = Image.new(
            '1', (self.wt, self.ht), 255)  # 255: clear the frame
        self.draw_yellow = ImageDraw.Draw(self.image_yellow)
        self.image_black = Image.new(
            '1', (self.wt, self.ht), 255)  # 255: clear the frame
        self.draw_black = ImageDraw.Draw(self.image_black)
        self.mask_black = Image.new('1', (self.wt, self.ht), 255)
        self.draw_mask_black = ImageDraw.Draw(self.mask_black)
        self.font = ImageFont.truetype(os.path.join(mydir, 'resources/fonts/DejaVuSans.ttf'), 16)
        self.stringInput = stringIn
        self.updateWidget()

    def drawText(self, str):
        # define parameters for text placement
        position = (0, 0)
        limit = (self.wt, self.ht)
        # write the string in the widget
        textBox(str, self.font, position, limit, "black", self.draw_black)

    def saveImages(self):
        self.image_black.save(os.path.join(mydir, '../test/imgBlackWidget.bmp'))
        self.image_yellow.save(os.path.join(mydir, '../test/imgYellowWidget.bmp'))

    def updateWidget(self):
        if self.stringInput is not None:
            self.draw_black.rectangle((0, 0, self.wt, self.ht), fill=255)
            self.draw_yellow.rectangle((0, 0, self.wt, self.ht), fill=255)
            self.drawText(self.stringInput)
            self.saveImages()


class ImageWidget():
    # this widget displays an image
    # needs to recieve an image file as input. Then resize and convert to monochrome.
    def __init__(self, cwidth, cheight, xy=(0, 0), dim=(1, 1), bwMode="mono", scaleMode="fill", img=""):
        self.width = dim[0]
        self.height = dim[1]
        self.wt = cwidth * dim[0]
        self.ht = cheight * dim[1]
        self.cellX = xy[0]
        self.cellY = xy[1]
        self.image_yellow = Image.new(
            '1', (self.wt, self.ht), 255)  # 255: clear the frame
        self.draw_yellow = ImageDraw.Draw(self.image_yellow)
        self.image_black = Image.new(
            '1', (self.wt, self.ht), 255)  # 255: clear the frame
        self.draw_black = ImageDraw.Draw(self.image_black)
        self.mask_black = Image.new('1', (self.wt, self.ht), 255)
        self.draw_mask_black = ImageDraw.Draw(self.mask_black)
        self.bwMode = bwMode
        self.scaleMode = scaleMode
        self.image = Image.open(img)
        self.updateWidget()

    def resizeImg(self, img):
        # scale image down until whole image fits inside the cell
        iRatio = img.size[0] / img.size[1]  # image ratio
        cRatio = self.wt / self.ht  # canvas ratio
        if cRatio > iRatio:  # height is limiting dimension
            fixedImg = img.resize((int(self.ht * iRatio), self.ht))
            return fixedImg
        if cRatio < iRatio:  # width is limiting dimension
            fixedImg = img.resize((self.wt, int(self.wt / iRatio)))
            return fixedImg
        fixedImg = img
        return fixedImg

    def fillImg(self, img):
        # enlarge the image and crop to fill the widget canvas
        iRatio = img.size[0] / img.size[1]  # image ratio
        cRatio = self.wt / self.ht  # canvas ratio
        if cRatio > iRatio:
            fixedImg = img.resize((self.wt, int(self.wt / iRatio)))
            offset = 0.5 * (fixedImg.size[1] - self.ht)  # centre by height
            cropped = fixedImg.crop((0, offset, self.wt, offset + self.ht - 1))
            return cropped
        if cRatio < iRatio:
            fixedImg = img.resize((int(self.ht * iRatio), self.ht))
            offset = 0.5 * (fixedImg.size[0] - self.wt)  # centre by width
            cropped = fixedImg.crop((offset, 0, offset + self.wt, self.ht - 1))
            return cropped
        return img.resize((self.wt, self.ht))

    def autoThresholdLimit(self, img):
        gscale = img.convert(mode="L")
        gscale.save(os.path.join(mydir, '../test/greyscale.bmp'))
        extrema = gscale.getextrema()
        limit = extrema[0] + 0.4 * (extrema[1] - extrema[0])
        self.thresLim = limit

    def threshold(self, val):
        if(val > self.thresLim):
            return 1
        else:
            return 0

    def bwImg(self, img):
        self.autoThresholdLimit(img)
        if self.bwMode is "mono":
            return img.convert('L').point(self.threshold, '1')
        if self.bwMode is "dither":
            return img.convert(mode='1')

    def pasteImg(self, img):
        iRatio = img.size[0] / img.size[1]  # image ratio
        cRatio = self.wt / self.ht  # canvas ratio
        if cRatio > iRatio:  # needs to be centred horizontally
            offset = int(0.5 * (self.wt - img.size[0]))
            self.image_black.paste(img, (offset, 0))
        if cRatio < iRatio:  # needs to be centred vertically
            offset = int(0.5 * (self.ht - img.size[1]))
            self.image_black.paste(img, (0, offset))

    def saveImages(self):
        self.image_black.save(os.path.join(mydir, '../test/imgBlackWidget.bmp'))
        self.image_yellow.save(os.path.join(mydir, '../test/imgYellowWidget.bmp'))

    def updateWidget(self):
        if self.image is not None:
            if self.scaleMode is "fill":
                rsImg = self.fillImg(self.image)
            if self.scaleMode is "resize":
                rsImg = self.resizeImg(self.image)
            if self.scaleMode is "none":
                rsImg = self.image
            newImg = self.bwImg(rsImg)
            self.pasteImg(newImg)
            self.saveImages()


class TrelloWidget():
    # displays your trello boards in text form
    # card function: Name, description in one 'cell' or box with rounded corners

    def __init__(self, cwidth, cheight, xy=(0, 0), dim=(1, 1), boardName=None, listName=None):
        self.boardName = boardName
        self.listName = listName

        self.width = dim[0]
        self.height = dim[1]
        self.wt = cwidth * dim[0]
        self.ht = cheight * dim[1]
        self.cellX = xy[0]
        self.cellY = xy[1]
        self.image_yellow = Image.new(
            '1', (self.wt, self.ht), 255)  # 255: clear the frame
        self.draw_yellow = ImageDraw.Draw(self.image_yellow)
        self.image_black = Image.new(
            '1', (self.wt, self.ht), 255)  # 255: clear the frame
        self.draw_black = ImageDraw.Draw(self.image_black)
        self.mask_black = Image.new('1', (self.wt, self.ht), 255)
        self.draw_mask_black = ImageDraw.Draw(self.mask_black)
        self.lastY = 0
        self.cardLastY = 0
        fontpath = os.path.join(mydir, 'resources/fonts/DejaVuSans.ttf')
        self.font1 = ImageFont.truetype(fontpath, 12)
        self.font2 = ImageFont.truetype(fontpath, 13)
        self.font3 = ImageFont.truetype(fontpath, 10)

        self.updateWidget()

    def initTrelloClient(self):
        print("CONNECTING TO TRELLO")
        self.trello_key = 'a3d8f7c04c266e5c9571f6b7747aa353'
        self.trello_secret = 'd4ac9968d997aa3e5a0ebf129a627b03701520de6ff19834c81d6d9c56fe7e9b'
        try:
            with open('tokenfile.secret', 'r') as file:
                token = file.readline().rstrip()
                secret = file.readline().rstrip()
            self.client = TrelloClient(
                api_key=self.trello_key,
                api_secret=self.trello_secret,
                token=token,
                token_secret=secret
            )
            all_boards = self.client.list_boards()
        except Exception as e:
            print(e)
            print("token expired or does not exist\nCreating new token")
            self.access_token, self.resource_owner_key, self.resource_owner_secret = self.create_oauth_token()
            with open('tokenfile.secret', 'w') as file:
                file.write(self.access_token["oauth_token"] +
                           "\n" + self.access_token["oauth_token_secret"])
            self.client = TrelloClient(
                api_key=self.trello_key,
                api_secret=self.trello_secret,
                token=self.access_token["oauth_token"],
                token_secret=self.access_token["oauth_token_secret"]
            )

    def create_oauth_token(self, expiration=None, scope=None, key=None, secret=None, name=None, output=True):
        request_token_url = 'https://trello.com/1/OAuthGetRequestToken'
        authorize_url = 'https://trello.com/1/OAuthAuthorizeToken'
        access_token_url = 'https://trello.com/1/OAuthGetAccessToken'

        expiration = os.environ.get('TRELLO_EXPIRATION', "30days")
        scope = scope or os.environ.get('TRELLO_SCOPE', 'read,write')
        #trello_key = key or os.environ['TRELLO_API_KEY']
        #trello_secret = secret or os.environ['TRELLO_API_SECRET']
        name = name or os.environ.get('TRELLO_NAME', 'py-trello')

        session = OAuth1Session(client_key=self.trello_key,
                                client_secret=self.trello_secret)
        response = session.fetch_request_token(request_token_url)
        resource_owner_key, resource_owner_secret = response.get(
            'oauth_token'), response.get('oauth_token_secret')

        print("Go to the following link in your browser:")
        print("{authorize_url}?oauth_token={oauth_token}&scope={scope}&expiration={expiration}&name={name}".format(
            authorize_url=authorize_url,
            oauth_token=resource_owner_key,
            expiration=expiration,
            scope=scope,
            name=name
        ))

        accepted = "n"
        while accepted.lower() == "n":
            accepted = raw_input('Have you authorized me? (y/n) ')

        #oauth_verifier = ""
        # if input('Have you authorized me? (y/n) ') is "y":
        #    oauth_verifier = input('What is the PIN? ')

        oauth_verifier = raw_input('What is the PIN? ')

        session = OAuth1Session(client_key=self.trello_key, client_secret=self.trello_secret,
                                resource_owner_key=resource_owner_key, resource_owner_secret=resource_owner_secret,
                                verifier=oauth_verifier)
        access_token = session.fetch_access_token(access_token_url)

        return access_token, resource_owner_key, resource_owner_secret

    def updateWidget(self):
        self.initTrelloClient()
        self.image_black = Image.new('1', (self.wt, self.ht), 255)
        self.mask_black = Image.new('1', (self.wt, self.ht), 255)
        card_list = self.cardsFromBoard(self.boardName, self.listName)
        self.printCards(card_list)
        self.image_black.save(os.path.join(mydir, '../test/card_image.bmp'))
        self.mask_black.save(os.path.join(mydir, '../test/mask.bmp'))
        #self.draw_black.rectangle((0, 0, self.wt, self.ht), fill=255) #inclusive/exclusive dimensions?
        #self.draw_yellow.rectangle((0, 0, self.wt, self.ht), fill=255)
        #textBox(content, self.font, (0,0), (self.wt, self.ht), "black", self.draw_black)

    def printCards(self, card_list):
        for cardImg in card_list:
            self.image_black.paste(cardImg[0], (0, self.lastY))
            self.mask_black.paste(cardImg[1], (0, self.lastY))
            self.lastY += cardImg[0].size[1]
        self.lastY = 0

    def makeCard(self, card):
        # calculate card dimensions for an image to draw the card on
        # then draw the card on the widget canvas
        # card dimensions: wt/width, ht/height (cards as large as one cell)
        arcsize = 20 #23
        offset = 10 #15
        padding = 8
        #padding = 5*arcsize/16 + offset
        card_wt = int(self.wt)
        card_ht = int(self.ht)*5
        self.cardLastY = 0 + offset + padding

        card_image = Image.new('1', (card_wt, card_ht), 255)
        card_draw = ImageDraw.Draw(card_image)

        self.drawText(card.name, self.font1, card_draw, card_image, card_wt, offset, padding)
        self.cardLastY += 4
        self.drawText(card.description, self.font3, card_draw, card_image, card_wt, offset, padding)

        card_ht = self.cardLastY+padding

        mask_image = Image.new('1', (card_wt, card_ht+1), 255)
        mask_draw = ImageDraw.Draw(mask_image)

        roundRect(card_draw, (offset, offset), (card_wt-offset, card_ht), arcsize, 'outline')
        roundRect(mask_draw, (offset, offset), (card_wt-offset, card_ht), arcsize, 'fill')

        card_image = card_image.crop((0, 0, card_wt, card_ht+1))
        mask_image = ImageOps.invert(mask_image.convert('L'))

        card_image.save(os.path.join(mydir, '../test/card_image.bmp'))
        self.cardLastY = 0
        return card_image, mask_image

    def drawText(self, text, font, draw, ima, card_wt, offset, padding):
        #self.cardLastY += 4
        sizex, sizey = textBox(text, font, (offset+padding, self.cardLastY),
                               (card_wt-(offset+padding), ima.size[1]), "black", draw)
        # find predicted height of textbox and set lastY to it
        self.cardLastY += sizey

    def cardsFromBoard(self, boardName, listName):
        cards = []
        all_boards = self.client.list_boards()

        for board in all_boards:
            if board.name == boardName:
                slctd_board = board
                slctd_board.list_lists()
                all_lists_in_board = slctd_board.all_lists()

                for lst in all_lists_in_board:
                    if lst.name == listName:
                        card_list = lst.list_cards()
                        for crd in card_list:
                            cards.append(self.makeCard(crd))
        return cards
