"""
qp/fill/stored_file.qpy
"""
from durus.utils import BytesIO
from os.path import exists
from qp.fill.directory import Directory
from qp.fill.form import Form
from qp.fill.html import div, href, img, htmltag, span
from qp.fill.static import FileStream
from qp.fill.static import FileStream
from qp.fill.widget import FileWidget, CompositeWidget, SingleSelectWidget, TextWidget
from qp.http.request import Upload
from qp.http.response import formatdate
from qp.lib.stored_file import new_file
from qp.lib.util import randbytes
from qp.pub.common import format_date_time
from qp.pub.common import get_user, header, footer, redirect, get_response, not_found
from qp.pub.common import not_found, get_request, get_response
import os, sys
import mimetypes


mimetypes.add_type('text/plain', '.py')

def guess_mime_type(name, fallback=''):
    return mimetypes.guess_type(name.lower())[0] or fallback

thumbnail_types = ['image/png', 'image/jpeg', 'image/gif']


try:
    from PIL import Image
except ImportError:
    Image = None
    print("no PIL")

def thumbnail_response(fp, size=50, cache_time=86400, mime_type="image/png", last_modified=None):
    if Image:
        try:
            image = Image.open(fp)
            image.load()
        except IOError:
            not_found()
        # Scale down the image
        query = get_request().get_query()
        thumbnail_size = size
        if query:
            try:
                thumbnail_size = int(query)
            except ValueError:
                pass
            if thumbnail_size < 2:
                not_found()
        try:
            image.thumbnail((thumbnail_size, thumbnail_size))
        except IOError:
            not_found()
        output = BytesIO()
        try:
            image.save(output, 'PNG', **image.info)
            mime_type = "image/png"
        except IOError:
            not_found()
    else:
        output = None
    response = get_response()
    response.set_header("Cache-Control", "public, max-age=%s" % cache_time)
    response.set_expires(seconds=cache_time)
    if last_modified:
        response.set_header("Last-Modified", last_modified)
    response.set_content_type(mime_type, None)
    if output:
        return output.getvalue()
    else:
        return FileStream(fp)

def thumbnail_response_for_file(path, size=50, cache_time=86400, mime_type="image/png"):
    try:
        stat = os.stat(path)
    except OSError:
        print(sys.exc_info()[1])
        not_found()

    last_modified = formatdate(stat.st_mtime)

    if last_modified == get_request().get_header('If-Modified-Since'):
        get_response().set_status(304)
        return ''
    return thumbnail_response(open(path), size=size, cache_time=cache_time, mime_type=mime_type, last_modified=last_modified)

def get_filename(upload):
    if isinstance(upload, Upload):
        return upload.base_filename
    else:
        return None

def get_mimetype(upload):
    return guess_mime_type(get_filename(upload), 'application/octet-stream')

def get_stored_file(upload, description=None):
    if upload is None:
        return None
    stored_file = new_file(upload)
    stored_file.set_mime_type(get_mimetype(upload))
    stored_file.set_filename(get_filename(upload))
    stored_file.set_owner(get_user())
    stored_file.set_description(description)
    return stored_file

def format_stored_file:xml(stored_file, download_url=None, show_description=True):
    classes = ["stored_file"]
    if stored_file.get_hidden():
        classes.append("hidden_stored_file")
    if show_description:
         description = div(stored_file.get_description(), css_class="stored_file_description")
    else:
        description = None
    div(href(download_url, stored_file.get_filename(), css_class="stored_file_link"),
        div(' (%s, %s) ' % (
            format_file_size(stored_file.get_size()),
            stored_file.get_mime_type() or guess_mime_type(stored_file.get_filename(), 'application/octet-stream')),
            css_class="stored_file_details"),
        div(format_date_time(stored_file.get_date()), css_class="stored_file_date"),
        description,
        classes=classes)

def format_stored_file_icon:xml(stored_file, icon_url=None):
    classes = ['stored_file_icon']
    if stored_file:
        mime_type = stored_file.get_mime_type() or guess_mime_type(stored_file.get_filename(), 'application/octet-stream')
        mime_type_class = mime_type.replace('.', '_').replace('/', '_')
        classes.append(mime_type_class)
        if icon_url:
            classes.append('img_container')
    div(icon_url and img(icon_url), classes=classes)


class StoredFileWidget (CompositeWidget):

    keep = 'Keep'
    replace = 'Replace'
    delete = 'Delete'
    hide = 'Hide'
    unhide = 'Unhide'

    def __init__(self, name, hint=None, title=None, download_url=None, show_description=False, icon_url=None, mime_types=None, path_prefix='', **kwargs):
        CompositeWidget.__init__(self, name, title=title, **kwargs)
        self.download_url = download_url
        self.icon_url = icon_url
        if self.value :
            if self.download_url is None:
                self.download_url = path_prefix + default_download_url(self.value)
            if self.icon_url is None and self.value.get_mime_type() in thumbnail_types:
                self.icon_url = path_prefix + default_icon_url(self.value)
        self.show_description = show_description
        if mime_types is not None:
            assert type(mime_types) is list
        self.mime_types = mime_types
        self.file_widget_id = randbytes(10)
        if self.icon_url:
            self.icon_url += '?%s' % self.file_widget_id # Make sure that the version displayed is current.
        if self.value is not None:
            js = "if (this.value=='%s'){document.getElementById('%s').style.visibility='visible'}else{document.getElementById('%s').style.visibility='hidden'}" % (
                self.replace, self.file_widget_id, self.file_widget_id)
            options = [self.keep, self.replace, self.delete]
            if self.value.get_hidden():
                options.append(self.unhide)
            else:
                options.append(self.hide)
            self.add(SingleSelectWidget, 'action', options=options, value=self.keep, onchange=js)
            self.add(FileWidget, 'new_file', hint=hint)
            if show_description:
                self.add(TextWidget, 'description', rows=2, cols=30, value=self.value.get_description(), title="Description")
        else:
            self.add(FileWidget, 'new_file', hint=hint)
            if show_description:
                self.add(TextWidget, 'description', rows=2, cols=30, title="Description")

    def render_icon:xml(self):
        format_stored_file_icon(self.value, icon_url=self.icon_url)

    def render_stored_file:xml(self):
        format_stored_file(self.value, download_url=self.download_url, show_description=self.show_description)

    def render_content:xml(self):
        if self.show_description:
            description = self.get_widget('description').render()
        else:
            description = None
        if self.value:
            self.render_icon()
            self.render_stored_file()
            div(self.get_widget('action') and self.get_widget('action').render(),
                div(self.get_widget('new_file').render(),
                    description,
                    id=self.file_widget_id, css_class="file_widget_container"),
                                    css_class="stored_file_widgets")
        else:
            div(self.get_widget('new_file').render(),
                description,
                css_class="new_stored_file")

    def _parse(self, request=None):
        if not self.has_error():
            if self.get('action') == self.delete:
                self.value = None
            elif self.get('new_file'):
                if self.mime_types is not None:
                    mime_type = get_mimetype(self.get('new_file'))
                    if mime_type not in self.mime_types:
                        error = 'Wrong type of file: %s. \n' % mime_type
                        if len(self.mime_types) == 1:
                            error += 'Expected %s' % self.mime_types[0]
                        self.set_error(error)
                        return
                self.value = get_stored_file(self.get('new_file'))
                self.value.create_easy_variants() # split pdfs.
                if self.show_description:
                    self.value.set_description(self.get('description'))
                if self.get('action') == self.replace:
                    self.get_widget('action').value = self.keep
            if self.value and self.show_description:
                self.value.set_description(self.get('description'))
            if self.value and self.get('action') == self.hide:
                self.value.set_hidden(True)
                self.get_widget('action') == self.keep
            if self.value and self.get('action') == self.unhide:
                self.value.set_hidden(False)
                self.get_widget('action') == self.keep


class StoredFileListWidget (CompositeWidget):

    def __init__(self, name, title=None, new=1, extra=5, add_more="Add More", value=None, 
        stored_file_widget=StoredFileWidget, **kwargs):
        CompositeWidget.__init__(self, name, title=title, value=value)
        self.new = new
        if new == 0 and not self.value:
            new = 1
        self.extra = extra
        self.add_more = add_more
        if self.value:
            for j, stored_file in enumerate(self.value):
                self.add(stored_file_widget, "old%s" % j, value=stored_file, **kwargs)
        for j in range(0, new):
            self.add(stored_file_widget, "new%s" % j, **kwargs)
        for j in range(0, extra):
            self.add(stored_file_widget, "extra%s" % j, **kwargs)
        self.extra_widgets = self.get_widgets()[-extra:]

    def render_content:xml(self):
        for widget in self.get_widgets():
            if widget not in self.extra_widgets:
                widget.render()
        extra_id = randbytes(10)
        add_more_id = randbytes(10)

        for widget in self.extra_widgets:
            if widget.has_error():
                extra_widget_style = "display:block"
                break
        else:
            extra_widget_style = "display:none"
            js = "genlib.display_block('%s');genlib.display_none('%s')" % (extra_id, add_more_id)
            span(self.add_more, css_class="add_more_stored_files", onclick=js, id=add_more_id)

        htmltag('div', id=extra_id, css_class="extra_stored_file_widgets", style=extra_widget_style)
        for widget in self.extra_widgets:
            widget.render()
        '</div>'

    def _parse(self, request=None):
        CompositeWidget._parse(self, request=request)
        self.value = [w.value for w in self.get_widgets() if w.value is not None]


class StoredFileListDirectory (Directory):

    def __init__(self, stored_file_list, title="Files", title2=None, manage=True, stored_file_widget=None, stored_file_ui=None):
        self.stored_file_list = stored_file_list
        self.title = title
        self.title2 = title2
        self.manage = manage
        self.stored_file_widget = stored_file_widget or StoredFileWidget
        self.stored_file_ui = stored_file_ui or StoredFileUI

    def get_exports(self):
        yield ('', 'index', 'View', None)
        if self.manage:
            if len(self.stored_file_list) > 1:
                yield ('order', 'order', 'Order', None)
            yield ('edit', 'edit', 'Edit', None)
            yield ('move', 'move', None, None)

    def move:xml(self):
        form = Form(method="get")
        form.add_int('from', title="From Position")
        form.add_int('to', title="To Position")
        if form.get('from') and form.get('to'):
            stored_file = self.stored_file_list[form.get('from') - 1]
            self.stored_file_list.remove(stored_file)
            self.stored_file_list.insert(form.get('to') - 1, stored_file)
            redirect('order')
        header('Move')
        form.render()
        footer()

    def index:xml(self):
        header(self.title)
        self.title2
        stored_file_list = [stored_file for stored_file in self.stored_file_list
            if not stored_file.get_hidden()]
        n = len(stored_file_list)
        if n:
            for j, stored_file in enumerate(stored_file_list):
                div(format_stored_file_icon(stored_file, icon_url=default_icon_url(stored_file)),
                    format_stored_file(stored_file, show_description=True, download_url=default_download_url(stored_file)),
                    div(style="clear:both"),
                    css_class="stored_file_index_item")
        else:
            div('There are no files in this list.')
        footer()

    def order:xml(self):
        header(self.title)
        self.title2
        div("Change Order", css_class="stored_file_title3")
        n = len(self.stored_file_list)
        if n:
            for j, stored_file in enumerate(self.stored_file_list):
                if j < n - 1:
                    down = href('move?from=%s&to=%s' % (j + 1, j + 2), '&darr;', css_class="button")
                else:
                    down = ''
                if j > 0:
                    up = href('move?from=%s&to=%s' % (j + 1, j), '&uarr;', css_class="button")
                else:
                    up = ''
                div(div(up, down, css_class="stored_file_controls"),
                    format_stored_file_icon(stored_file, icon_url=default_icon_url(stored_file)),
                    format_stored_file(stored_file, show_description=True, download_url=default_download_url(stored_file)),
                    div(style="clear:both"),
                    css_class="stored_file_index_item")
        else:
            div('There are no files in this list.')
        footer()

    def edit:xml(self):
        form = Form(enctype='multipart/form-data', id="stored_file_list_directory_edit")
        form.add(StoredFileListWidget, 'stored_files', value=self.stored_file_list, show_description=True,
            stored_file_widget=self.stored_file_widget)
        form.add_submit('save', 'Save')
        form.add_submit('cancel', 'Cancel')
        if form.get('cancel'):
            redirect('.')
        if form.get('save') and not form.has_errors():
            self.stored_file_list[:] = form.get('stored_files')

        header('Edit %s' % self.title)
        self.title2
        div("Edit List", css_class="stored_file_title3")
        form.render()
        footer()

    def _q_lookup(self, component):
        for stored_file in self.stored_file_list:
            if component in stored_file.get_ids():
                return self.stored_file_ui(stored_file)
        for stored_file in self.stored_file_list:
            if stored_file.get_filename() == component:
                return self.stored_file_ui(stored_file)

def return_file(path=None, filename=None, mime_type=None, charset=None, disposition=None):
    try:
        fp = open(path, 'rb')
        size = os.stat(path).st_size
    except IOError:
        print("\nCOULD NOT OPEN %s" % path)
        not_found()
    response = get_response()
    #response.set_expires(seconds=self.cache_time)
    if mime_type is None and filename:
        mime_type = guess_mime_type(filename, 'application/octet-stream')
    response.set_content_type(mime_type, charset)
    response.set_header('Cache-Control', 'public')
    if disposition is None:
        if mime_type == 'text/html':
            disposition = 'attachment'
        else:
            disposition = 'inline'
    response.set_header(
        'Content-Disposition',
        '%s; filename="%s"' % (disposition, filename))
    return FileStream(fp, length=size)

def return_stored_file(stored_file):
    if stored_file is None:
        not_found()
    return return_file(
        path=stored_file.get_full_path(),
        filename=stored_file.get_filename(),
        mime_type=stored_file.get_mime_type())

def format_pages:xml(stored_file, stored_file_ui_path='', **kwargs):
    if stored_file.is_pdf_with_png_pages():
        htmltag('div', **kwargs)
        for page in stored_file.list_existing_png_pages():
            image_path = "%s%s" % (stored_file_ui_path, page)
            img(src=image_path, alt=page, href=image_path)
        '</div>'


class StoredFileUI (Directory):

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

    def get_exports(self):
        yield ('', 'index', None, None)
        yield ('thumbnail', 'thumbnail', None, None)
        yield ('view_pages', 'view_pages', None, None)

    def index:xml(self):
        return return_stored_file(self.stored_file)

    def thumbnail(self):
        if self.stored_file.is_pdf_with_png_pages():
            image_path = self.stored_file.get_png_page_path(1)
        elif self.stored_file.get_mime_type() in thumbnail_types:
            image_path = self.stored_file.get_full_path()
        return thumbnail_response_for_file(image_path, size=200)

    def view_pages:xml(self):
        header(self.stored_file.get_filename())
        format_pages(self.stored_file, css_class="view_pages")
        footer()

    def _q_lookup(self, component):
         if '..' in component or '/' in component:
             return None
         path = self.stored_file.get_full_path()
         if "." in component:
             path_with_component = path + component
             filename = self.stored_file.get_filename() + component
         else:
             path_with_component = path + "." + component
             filename = self.stored_file.get_filename() + "." + component
         if exists(path_with_component):
             return return_file(path=path_with_component, filename=filename)


def default_icon_url(stored_file):
    if stored_file and stored_file.get_mime_type() in thumbnail_types:
        return stored_file.get_id() + '/thumbnail'

def default_download_url(stored_file):
    if stored_file:
        return stored_file.get_id() + '/'

def format_file_size(size):
    if size is None:
        return 'file missing'
    elif size > 1024*1024:
        return '%.1f MB' % (size/1024.0/1024)
    elif size > 4096:
        return '%.1f KB' % (size/1024.0)
    else:
        return ('%i bytes') % size
