"""Create an anki deck from a pdf."""

import PyPDF2
from PIL import Image, ImageChops
from math import floor
from hashlib import sha1
from os import listdir
from os.path import basename


def extract_page_images(pdf_path):
    """Extract the each image from a pdf file.

    This only works for pdf files where each page is a separate image file,
    as for example in scanned files or files generated by lecture notes.

    """
    image_list = []
    with open(pdf_path, 'rb') as file:
        pdf = PyPDF2.PdfFileReader(file)
        for p in range(pdf.getNumPages()):
            page = pdf.getPage(p)
            xObject = page['/Resources']['/XObject'].getObject()    # noqa

            for obj in xObject:

                if xObject[obj]['/Subtype'] == '/Image':
                    size = (xObject[obj]['/Width'], xObject[obj]['/Height'])
                    data = xObject[obj].getData()
                    if xObject[obj]['/ColorSpace'] == '/DeviceRGB':
                        mode = "RGB"
                    else:
                        mode = "P"

                    if xObject[obj]['/Filter'] == '/FlateDecode':
                        img = Image.frombytes(mode, size, data)
                        image_list.append(img)
                    elif xObject[obj]['/Filter'] == '/DCTDecode':
                        img = open(obj[1:] + '.jpg', 'wb')
                        img.write(data)
                        img.close()
                    elif xObject[obj]['/Filter'] == '/JPXDecode':
                        img = open(obj[1:] + '.jp2', 'wb')
                        img.write(data)
                        img.close()
    return image_list


def not_white(image):
    """Return True if image is not white."""
    return ImageChops.invert(image).getbbox()


def shift_box_right(box, width_step):
    """Shift box for image cropping one width_step to the right."""
    box_list = list(box)
    box_list[0] += width_step
    box_list[2] += width_step
    return tuple(box_list)


def shift_box_down(box, width_step, height_step):
    """Shift box for image cropping to the start of the next row."""
    box_list = list(box)
    box_list[0] = 0
    box_list[1] += height_step
    box_list[2] = width_step
    box_list[3] += height_step
    return tuple(box_list)


def check_box_from_box(box):
    """Select the upper left 60 by 60 pixel from box."""
    box_list = list(box)
    box_list[2] = box_list[0] + 60
    box_list[3] = box_list[1] + 60
    return tuple(box_list)


def cards_from_image(image, rows=4, columns=3, drop_unchecked=True,
                     drop_white=True):
    """Chop an image into the sides of cards.

    Returns a list of list. Each sublist contains the sides of one card.

    """
    width, height = image.size
    height_step = floor(height / rows)
    width_step = floor(width / columns)
    cards = []
    box = (0, 0, width_step, height_step)
    for r in range(rows):
        check_field = image.crop(check_box_from_box(box))
        if drop_unchecked is False or not_white(check_field):
            temp_card = [None for c in range(columns)]
            for c in range(columns):
                image_slice = image.crop(box)
                if drop_white is False or not_white(image_slice):
                    temp_card[c] = image_slice
                box = shift_box_right(box, width_step)
            cards.append(temp_card)
        box = shift_box_down(box, width_step, height_step)
    return cards


def pdf_to_cards(pdf_path, rows=4, columns=3, drop_unchecked=True,
                 drop_white=True):
    """Transform a pdf to anki cards."""
    image_list = extract_page_images(pdf_path)
    all_cards = []
    for img in image_list:
        all_cards += cards_from_image(
            image=img, rows=rows, columns=columns,
            drop_unchecked=drop_unchecked, drop_white=drop_white)
    return all_cards


def make_anki_deck(pdf_path, csv_path, media_folder, rows=4, columns=3,
                   drop_unchecked=True, drop_white=True, tags=None,
                   overwrite=False):
    """Create an anki deck.

    Save images from pdf into the anki media folder and create a csv that
    imports them into Anki.

    """
    # preparations
    media_folder = media_folder.rstrip('/') + '/'
    base_name = basename(pdf_path).rstrip('.pdf')
    tags.append(base_name)
    tags_string = ' '.join(tags) + '\n'

    all_cards = pdf_to_cards(
        pdf_path, rows, columns, drop_unchecked, drop_white)

    csv_text = ''
    for c, card in enumerate(all_cards):
        line = ''
        unique_id = sha1(card[0].tobytes()).hexdigest()[:20]
        for side, letter in zip(card, list('abcdefghij')):
            name = '_' + base_name + str(c) + letter + '_' + unique_id + '.png'

            # save png
            if side is not None:
                if name not in listdir(media_folder) or overwrite is True:
                    side.save(media_folder + name, format='png')
                elif name in listdir(media_folder):
                    print('{}.png exists and was not overwritten.'.format(
                        name))

            # add line to csv file
            if side is None:
                line += ','
            else:
                line += '<img src={}>,'.format(name)
        line += tags_string
        csv_text += line

    with open(csv_path, 'w') as file:
        file.write(csv_text)
