# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4

import os
import sqlite3
from concurrent.futures import ThreadPoolExecutor

import tornado.concurrent
import tornado.gen
import tornado.ioloop
import tornado.web
from tornado.concurrent import run_on_executor
import ox

from settings import static_path
from utils import resize_image, is_svg
import db


import logging
logger = logging.getLogger(__name__)

MAX_WORKERS = 4



class Icons(dict):
    def __init__(self, db):
        self._db = db
        self.create()

    def connect(self):
        conn = sqlite3.connect(self._db, timeout=90)
        return conn

    def create(self):
        conn = self.connect()
        c = conn.cursor()
        c.execute('CREATE TABLE IF NOT EXISTS icon (id varchar(64) unique, data blob)')
        c.execute('CREATE TABLE IF NOT EXISTS setting (key varchar(256) unique, value text)')
        if int(self.get_setting(c, 'version', 0)) < 1:
            self.set_setting(c, 'version', 1)

    def get_setting(self, c, key, default=None):
        c.execute('SELECT value FROM setting WHERE key = ?', (key, ))
        for row in c:
            return row[0]
        return default

    def set_setting(self, c, key, value):
        c.execute('INSERT OR REPLACE INTO setting values (?, ?)', (key, str(value)))

    def default_cover(self):
        with open(os.path.join(static_path, 'png', 'cover.png'), 'rb') as f:
            data = f.read()
        return data

    def __getitem__(self, id, default=None):
        sql = 'SELECT data FROM icon WHERE id=?'
        conn = self.connect()
        c = conn.cursor()
        c.execute(sql, (id, ))
        data = default
        for row in c:
            data = row[0]
            break
        c.close()
        conn.close()
        return data

    def __setitem__(self, id, data):
        sql = 'INSERT OR REPLACE INTO icon values (?, ?)'
        try:
            conn = self.connect()
            c = conn.cursor()
            data = sqlite3.Binary(data)
            c.execute(sql, (id, data))
            conn.commit()
            c.close()
            conn.close()
        except:
            logger.debug('failed to insert icon %s', id)

    def __delitem__(self, id):
        sql = 'DELETE FROM icon WHERE id = ?'
        try:
            conn = self.connect()
            c = conn.cursor()
            c.execute(sql, (id, ))
            conn.commit()
            c.close()
            conn.close()
        except:
            logger.debug('failed to delete icon %s', id)

    def clear(self, prefix):
        try:
            conn = self.connect()
            c = conn.cursor()
            sql = 'DELETE FROM icon WHERE id = ?'
            for size in (64, 128, 256, 512, 1024):
                id = '%s%s' % (prefix, size)
                c.execute(sql, (id, ))
            conn.commit()
            c.close()
            conn.close()
        except:
            logger.debug('failed to clear icon %s', id)

    def vacuum(self, ids):
            conn = self.connect()
            c = conn.cursor()
            sql = 'SELECT id from icon'
            c.execute(sql)
            icons = [row[0] for row in c]
            sql = 'DELETE FROM icon WHERE id = ?'
            for i in icons:
                id = i.split(':')[1]
                if id not in ids:
                    c.execute(sql, (id, ))
            conn.commit()
            sql = 'VACUUM'
            c.execute(sql)
            conn.commit()
            c.close()
            conn.close()

def get_icons_db_path():
    import settings
    import shutil

    metadata = os.path.join(os.path.expanduser(settings.preferences['libraryPath']), 'Metadata')
    ox.makedirs(metadata)
    icons_db_path = os.path.join(metadata, 'icons.db')
    old_icons_db_path = os.path.join(settings.data_path, 'icons.db')
    if not os.path.exists(icons_db_path) and os.path.exists(old_icons_db_path):
        print(icons_db_path, icons_db_path)
        shutil.move(old_icons_db_path, icons_db_path)
    return icons_db_path

def get_icon_sync(id, type_, size):
    if size:
        skey = '%s:%s:%s' % (type_, id, size)
        data = icons[skey]
        if data:
            return bytes(data)
    key = '%s:%s' % (type_, id)
    data = icons[key]
    if is_svg(data):
        return bytes(data)
    if not data:
        type_ = 'preview' if type_ == 'cover' else 'cover'
        key = '%s:%s' % (type_, id)
    if size:
        skey = '%s:%s:%s' % (type_, id, size)
    if size:
        data = icons[skey]
        if data:
            size = None
    if not data:
        data = icons[key]
    if not data:
        skey = '%s:%s:%s' % ('default', 'cover', size)
        if size:
            data = icons[skey]
            if data:
                size = None
        if not data:
            data = icons.default_cover()
        if size:
            try:
                data = resize_image(data, size=size)
            except:
                logger.debug('failed to resize default cover %s %s %s', id, size, skey)
                data = None
            if data:
                icons[skey] = data
        size = None
    if size:
        try:
            data = resize_image(data, size=size)
        except:
            logger.debug('failed to resize %s %s %s', id, size, skey)
            data = None
        if data:
            icons[skey] = data
    if data:
        data = bytes(data)
    else:
        data = ''
    return data

def clear_default_cover_cache():
    icons.clear('default:cover:')


class IconHandler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(max_workers=MAX_WORKERS)

    def initialize(self):
        pass

    @run_on_executor
    def get_icon(self, id, type_, size):
        return get_icon_sync(id, type_, size)

    @tornado.gen.coroutine
    def get(self, id, type_, size=None):

        size = int(size) if size else None

        if type_ not in ('cover', 'preview'):
            self.set_status(404)
            return

        self.set_header('Content-Type', 'image/jpeg')

        response = yield self.get_icon(id, type_, size)
        if not response:
            self.set_status(404)
            return
        if self._finished:
            return
        self.write(response)


icons = Icons(get_icons_db_path())