#!/usr/bin/env python
#-*- coding: UTF-8 -*-
__author__ = 'helljump'

import os
import logging
from PyQt4 import QtCore
from PyQt4 import QtGui
import startup
from ui.image_manager_ui import Ui_IMWindow
import transaction
from persistent.list import PersistentList
from gallery import ImagesModel
from dbobj import Image
from utils import MyProgressDialog
import sys
from formlayout import fedit
from utils import imgproc
from utils import imgparse
from itertools import cycle
from utils import savepics_su
from lxml.html import fromstring
from grab import Grab
from urlparse import urlparse
import cgi
from ZODB.utils import u64


log = logging.getLogger(__name__)


class DownloadThread(QtCore.QThread):

    process = QtCore.pyqtSignal(int)
    success = QtCore.pyqtSignal(int)
    fail = QtCore.pyqtSignal(unicode)

    def __init__(self, project, parent):
        QtCore.QThread.__init__(self, parent)
        self.project = project
        self.active = True

    def run(self):
        conn = self.project.new_con()
        images = conn.root()['images']
        prefs = conn.root()['prefs']
        proxy_file = prefs.get('proxy_file', None)
        g = Grab(hammer_mode=True)
        if '-d' in sys.argv:
            g.setup(log_dir=startup.DUMPDIR)
        if proxy_file:
            g.load_proxylist(proxy_file, 'text_file', 'http', auto_init=True, auto_change=True)
        docs = conn.root()['documents']
        try:
            done = 0
            for i, doc in enumerate(docs):
                tree = fromstring(doc.text)
                for el in tree.xpath('//img'):
                    try:
                        p = urlparse(el.attrib['src'])
                        if p[0] != 'http' and p[0] != 'https':
                            continue
                        rc = g.go(el.attrib['src'], referer="%s://%s" % (p[0], p[1]))
                        egg = QtGui.QImage.fromData(rc.body)
                        if egg.isNull():
                            continue
                        f = 'PNG' if rc.body[1:4] == 'PNG' else 'JPEG'
                        egg = Image((egg, f))
                        images.append(egg)
                        transaction.commit()
                        doc.text = doc.text.replace(el.attrib['src'], egg.url())
                        done += 1
                    except:
                        log.exception('grab image error')
                        raise
                transaction.commit()
                #print doc.text
                self.process.emit(i)
            self.success.emit(done)
        except:
            transaction.abort()
            exctype, value = sys.exc_info()[:2]
            log.exception('image download thread')
            self.fail.emit(unicode(str(value), 'cp1251', 'replace'))
        finally:
            conn.close()


class UploadThread(QtCore.QThread):

    process = QtCore.pyqtSignal(int)
    success = QtCore.pyqtSignal(int)
    fail = QtCore.pyqtSignal(unicode)

    def __init__(self, project, parent):
        QtCore.QThread.__init__(self, parent)
        self.project = project
        self.active = True

    def run(self):
        conn = self.project.new_con()
        prefs = conn.root()['prefs']
        proxy_file = prefs.get('proxy_file', None)
        docs = conn.root()['documents']
        try:
            done = 0
            upl = savepics_su.Uploader(proxy_file, conn)
            for i, doc in enumerate(docs):
                rc = upl.upload_doc(doc)
                if rc:
                    transaction.commit()
                    done += rc
                self.process.emit(i)
            self.success.emit(done)
        except:
            transaction.abort()
            exctype, value = sys.exc_info()[:2]
            log.exception('image uploader thread')
            self.fail.emit(unicode(value))
        finally:
            conn.close()


class ParseThread(QtCore.QThread):

    success = QtCore.pyqtSignal(unicode)
    fail = QtCore.pyqtSignal(unicode)

    def __init__(self, project, query, amount, parent):
        QtCore.QThread.__init__(self, parent)
        self.project = project
        self.active = True
        self.query = query
        self.amount = amount

    def run(self):
        conn = self.project.new_con()
        images = conn.root()['images']
        prefs = conn.root()['prefs']
        proxy_file = prefs.get('proxy_file', None)
        try:
            for img, link in imgparse.yandex(self.query, proxy_file):
                if not self.active or self.amount == 0:
                    break
                lfn = link.lower()
                if lfn.endswith('.jpg') or lfn.endswith('.jpeg'):
                    f = 'JPEG'
                else:
                    f = 'PNG'
                egg = Image((img, f))
                egg.tag = self.query
                images.append(egg)
                transaction.commit()
                self.success.emit(link)
                self.amount -= 1
            self.success.emit('')
        except:
            transaction.abort()
            exctype, value = sys.exc_info()[:2]
            self.fail.emit(unicode(str(value), 'cp1251', 'replace'))
        finally:
            conn.close()


class ParamsModel(QtGui.QStringListModel):

    update = QtCore.pyqtSignal(unicode)

    def flags(self, index):
        egg = QtCore.Qt.ItemIsEnabled
        if index.row() == 0:
            egg |= QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable
        return egg

    def setData(self, index, value, role):
        rc = super(ParamsModel, self).setData(index, value, role)
        if rc:
            self.update.emit(value.toString())
        return rc


class IManagerWindow(QtGui.QMainWindow, Ui_IMWindow):

    def __init__(self, project=None, parent=None):
        QtGui.QMainWindow.__init__(self, parent)
        self.setupUi(self)
        if startup.CFG.contains("imanager/state"):
            self.restoreState(startup.CFG.value("imanager/state").toByteArray())
        if startup.CFG.contains("imanager/geometry"):
            self.restoreGeometry(startup.CFG.value("imanager/geometry").toByteArray())
        self.project = project
        root = project.root()
        if 'images' not in root:
            root['images'] = PersistentList()
            transaction.commit()
        self.imagesView.setModel(ImagesModel(root['images']))
        self.imagesView.setGridSize(QtCore.QSize(ImagesModel.WIDTH+20, ImagesModel.HEIGHT+40))
        self.imagesView.selectionModel().currentChanged.connect(self.update_info)
        self.current_item = None
        self.pmodel = ParamsModel()
        self.paramsView.setModel(self.pmodel)
        self.pmodel.update.connect(self.pupdate)
        self.toolBar.setVisible(True)

    def pupdate(self, value):
        if self.current_item is None:
            return
        self.current_item.title = unicode(value)
        transaction.commit()

    def closeEvent(self, event):
        startup.CFG.setValue('imanager/state', self.saveState())
        startup.CFG.setValue('imanager/geometry', self.saveGeometry())
        event.accept()

    @QtCore.pyqtSlot()
    def on_actionSave_triggered(self):
        log.debug('save')
        root = self.project.root()
        fdir = QtGui.QFileDialog.getExistingDirectory(self, u'Экспорт изображений')
        if fdir.isNull():
            return
        fdir = unicode(fdir)
        pdlg = MyProgressDialog(u'Экспорт изображений', u'Обработка', u'Отмена', 0, len(root['images']), parent=self)
        pdlg.show()
        try:
            for i, image in enumerate(root['images']):
                finp = image.data.open('r')
                data = finp.read()
                finp.close()
                if data[1:4] == 'PNG':
                    frmt = 'png'
                else:
                    frmt = 'jpg'
                fname = os.path.join(fdir, "%s.%s" % (u64(image._p_oid), frmt))
                with open(fname, 'wb') as fout:
                    fout.write(data)
                pdlg.set_value(i)
                QtGui.qApp.processEvents()
                if pdlg.wasCanceled():
                    break
            pdlg.close()
        except:
            log.exception('image export error')
            pdlg.close()
            exctype, value = sys.exc_info()[:2]
            QtGui.QMessageBox.critical(self, u'Ошибка экспорта', unicode(value))


    @QtCore.pyqtSlot()
    def on_actionAdd_2_triggered(self):
        log.debug('add')
        root = self.project.root()
        fns = QtGui.QFileDialog.getOpenFileNames(self, u'Импорт изображений',
                                                 filter=u'Изображения (*.png *.jpg *.jpeg *.gif)')
        pdlg = MyProgressDialog(u'Импорт изображений', u'Обработка', u'Отмена', 0, len(fns), parent=self)
        pdlg.show()
        try:
            for i, fn in enumerate(fns):
                img = Image(unicode(fn))
                root['images'].append(img)
                transaction.commit()
                pdlg.set_value(i)
                QtGui.qApp.processEvents()
                if pdlg.wasCanceled():
                    break
            pdlg.close()
        except:
            log.exception('image import error')
            pdlg.close()
            exctype, value = sys.exc_info()[:2]
            QtGui.QMessageBox.critical(self, u'Ошибка импорта', unicode(value))
        self.imagesView.model().layoutChanged.emit()

    @QtCore.pyqtSlot()
    def on_actionUpload_triggered(self):
        log.debug('upload')
        rc = QtGui.QMessageBox.question(self, u'Выгрузка изображений', u'Выгрузить изображения на сервер?',
            QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
        if rc == QtGui.QMessageBox.No:
            return
        pdlg = MyProgressDialog(u'Выгрузка изображений', u'Подключение', u'Отмена', parent=self)
        pdlg.show()
        th = UploadThread(self.project, self)

        def fail(msg):
            log.debug('fail')
            pdlg.close()
            QtGui.QMessageBox.critical(self, u'Ошибка выгрузки', msg)

        def success(n):
            log.debug('success')
            pdlg.close()
            QtGui.QMessageBox.information(self, u'Выгрузка', u'Выгружено изображений %i' % n)

        def process(n):
            pdlg.set_value(n)

        def closed():
            log.debug('closed')
            th.active = False
            th.process.disconnect()
            th.success.disconnect()
            th.fail.disconnect()
            self.project.con().sync()

        th.success.connect(success)
        th.process.connect(process)
        th.fail.connect(fail)
        pdlg.closed.connect(closed)
        th.start()

    @QtCore.pyqtSlot()
    def on_actionDownload_triggered(self):
        log.debug('download')
        rc = QtGui.QMessageBox.question(self, u'Загрузки изображений', u'Загрузить внешние изображения в проект?',
            QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
        if rc == QtGui.QMessageBox.No:
            return
        pdlg = MyProgressDialog(u'Загпрузка изображений', u'Подключение', u'Отмена', parent=self)
        pdlg.show()
        th = DownloadThread(self.project, self)

        def fail(msg):
            log.debug('error')
            pdlg.close()
            QtGui.QMessageBox.critical(self, u'Ошибка загрузки', msg)

        def success(n):
            log.debug('success')
            pdlg.close()
            self.imagesView.model().cache.clear()
            self.imagesView.model().layoutChanged.emit()
            QtGui.QMessageBox.information(self, u'Загрузка', u'Загружено изображений %i' % n)

        def process(n):
            pdlg.set_value(n)

        def closed():
            log.debug('closed')
            th.active = False
            th.process.disconnect()
            th.success.disconnect()
            th.fail.disconnect()
            self.project.con().sync()

        th.success.connect(success)
        th.process.connect(process)
        th.fail.connect(fail)
        pdlg.closed.connect(closed)
        th.start()

    @QtCore.pyqtSlot()
    def on_actionInsert_triggered(self):
        log.debug('insert')
        root = self.project.root()
        images = root['images']
        documents = root['documents']
        prefs = root['prefs']
        defval = [True, u'', True, True]
        p = prefs.get('image_insert', defval)
        if len(p) < len(defval):
            p = defval
        data = [
            (u"Использовать повторно", p[0]),
            (u"Выравнивание", [
                p[1],
                ('', u'Без выравнивания'),
                ('left', u'Влево'),
                ('right', u'Вправо'),
                ]),
            (u"Вставлять alt", p[2]),
            (u"Вставлять title", p[3]),
        ]
        params = fedit(data, title=u"Вставка", parent=self, icon=QtGui.QIcon(':/aaaa.png'),
            comment=u'Вставка изображений в документы')
        if not params:
            return False
        #print params
        prefs['image_insert'] = params
        transaction.commit()
        again, alignment, add_alt, add_title = params
        if again:
            images = cycle(images)
        else:
            images = iter(images)
        pdlg = MyProgressDialog(u'Вставка', u'Обработка документов', u'Отмена', 0, len(documents), parent=self)
        pdlg.show()
        updated = 0
        try:
            for i, doc in enumerate(documents):
                if '<img' not in doc.text[:10].lower():
                    img = images.next()
                    a = u' style="float: %s;"' % alignment if alignment else ''
                    if add_title:
                        title = getattr(img, 'title', doc.title)
                        a += u' title="%s"' % cgi.escape(title)
                    if add_alt:
                        alt = getattr(img, 'title', doc.title)
                        a += u' alt="%s"' % cgi.escape(alt)
                    text = "<img src='%s'%s>%s" % (img.url(), a, doc.text)
                    #print text.encode('cp866', 'replace')
                    doc.text = text
                    updated += 1
                pdlg.set_value(i)
                QtGui.qApp.processEvents()
                if pdlg.wasCanceled():
                    transaction.abort()
                    break
        except StopIteration:
            pass
        transaction.commit()
        pdlg.close()
        self.statusBar().showMessage(u'Обновили %i документов' % updated, 3000)

    @QtCore.pyqtSlot()
    def on_actionRemove_triggered(self):
        log.debug('remove')
        rc = QtGui.QMessageBox.question(self, u'Удаление', u'Удалить указанное изображение?',
            QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
        if rc != QtGui.QMessageBox.Yes:
            return
        rows = self.imagesView.selectedIndexes()
        model = self.imagesView.model()
        rows.sort(key=QtCore.QModelIndex.row, reverse=True)
        for ndx in rows:
            model.removeRow(ndx.row())
        transaction.commit()
        self.statusBar().showMessage(u'Удалили %i изображений' % len(rows), 3000)

    @QtCore.pyqtSlot()
    def on_actionSearch_triggered(self):
        log.debug('search')
        prefs = self.project.root()['prefs']
        p = prefs.get('image_search', [u'робот вертер', 10])
        data = [
            (u"Запрос", p[0]),
            (u"Количество изображений", p[1]),
        ]
        params = fedit(data, title=u"Поиск", parent=self, icon=QtGui.QIcon(':/aaaa.png'), comment=u'Искать в Яндексе')
        if not params:
            return False
        prefs['image_search'] = params
        transaction.commit()
        query, amount = params
        if query <= 0:
            return
        pdlg = MyProgressDialog(u'Поиск', u'Подключение', u'Отмена', 0, 0, parent=self)
        pdlg.show()
        th = ParseThread(self.project, query, amount, self)

        def fail(msg):
            log.debug('errormsg!')
            pdlg.close()
            if th.isRunning():
                th.success.disconnect()
                th.fail.disconnect()
            QtGui.QMessageBox.critical(self, u'Ошибка поиска', msg)

        def success(msg):
            pdlg.set_text(msg)
            if msg == '':
                pdlg.close()

        def closed():
            log.debug('pclosed')
            th.active = False
            th.success.disconnect()
            th.fail.disconnect()
            self.project.con().sync()
            self.imagesView.model().cache.clear()
            self.imagesView.model().layoutChanged.emit()
            self.statusBar().showMessage(u'Поиск выполнен', 3000)

        th.success.connect(success)
        th.fail.connect(fail)
        pdlg.closed.connect(closed)
        th.start()

    @QtCore.pyqtSlot()
    def on_actionProcess_triggered(self):
        log.debug('process')
        prefs = self.project.root()['prefs']
        p = prefs.get('image_process', [320, 200, 0, 5, '#ff0000'])
        data = [
            (u"Ширина", p[0]),
            (u"Высота", p[1]),
            (u"Поворот(градусов)", p[2]),
            (u"Размер рамки", p[3]),
            (u"Цвет рамки", p[4]),
        ]
        params = fedit(data, title=u"Обработка", parent=self, icon=QtGui.QIcon(':/aaaa.png'))
        if not params:
            return False
        prefs['image_process'] = params
        transaction.commit()
        w, h, angle, width, color = params
        indexes = self.imagesView.selectedIndexes()
        #images = self.project.root()['images']
        pdlg = MyProgressDialog(u'Обработка изображений', u'Обработка', u'Отмена', 0, len(indexes), parent=self)
        pdlg.show()
        try:
            for i, index in enumerate(indexes):
                image = index.data(role=ImagesModel.ObjectRole).toPyObject()
                blob = image.data
                finp = blob.open('r')
                data = finp.read()
                finp.close()
                pm = QtGui.QPixmap()
                pm.loadFromData(data)
                if angle != 0:
                    pm = imgproc.rotate(pm, angle)
                pm = imgproc.resize(pm, w, h)
                if width > 0:
                    pm = imgproc.border(pm, width, color)
                buff = QtCore.QBuffer()
                pm.save(buff, image.format)
                fout = blob.open('w')
                fout.write(buff.data())
                fout.close()
                image.width = pm.width()
                image.height = pm.height()
                image.size = buff.size()
                transaction.commit()

                pdlg.set_value(i)
                QtGui.qApp.processEvents()
                if pdlg.wasCanceled():
                    break
            pdlg.close()
        except:
            log.exception('image process error')
            pdlg.close()
            exctype, value = sys.exc_info()[:2]
            QtGui.QMessageBox.critical(self, u'Ошибка обработки', unicode(value))
        self.imagesView.model().cache.clear()
        self.imagesView.model().layoutChanged.emit()
        self.statusBar().showMessage(u'Готово', 3000)

    def update_info(self, data):
        view = self.paramsView
        model = view.model()
        img = data.data(role=ImagesModel.ObjectRole).toPyObject()
        self.current_item = img
        if img is None:
            model.setStringList([])
            return
        model.setStringList([
            getattr(img, 'title', u'Без имени'),
            u'Размер: %ix%i' % (img.width, img.height),
            u'Формат: %s' % img.format,
            u'Файл: %i kb' % (img.size / 1024),
            u'Тег: %s' % img.tag,
            ])


if __name__ == '__main__':
    root = startup.CONN.root()
    prj = root["projects"][0]
    prj.open()
    dlg = IManagerWindow(prj)
    startup.SPLASH.finish(dlg)
    dlg.show()
    QtGui.qApp.exec_()
    prj.close()

