from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

from functools import partial
import collections
import requests
import hashlib
import base64
import json
import pyscp
import sys
import os

from skippy.utils.language import Translator
from skippy.utils.preview import Preview
from skippy.utils.logger import log
import skippy.utils.critical
import skippy.utils.elements
import skippy.utils.profile
import skippy.config


class MenusWidget(QWidget):
    def __init__(self, parent=None):
        super(MenusWidget, self).__init__(parent)
        self.parent = parent
        menuBar = parent.menuBar()

        self.action_list = {}

        self.contextMenu = self.addMenu()

        file_menu = self.addMenu(Translator.translate("MENU_BAR_FILE_MENU"), menuBar)
        file_toolbar = self.addToolbar(Translator.translate("MENU_BAR_FILE_TOOLBAR"))

        self.addAction(
            Translator.translate("MENU_BAR_NEW_ACTION_NAME"),
            Translator.translate("MENU_BAR_NEW_ACTION_STATUS_TIP"),
            lambda: parent.tab.new_tab(),
            "new",
            file_menu,
            file_toolbar,
            self.contextMenu,
        )
        self.addAction(
            Translator.translate("MENU_BAR_OPEN_ACTION_NAME"),
            Translator.translate("MENU_BAR_OPEN_ACTION_STATUS_TIP"),
            parent.download,
            "open",
            file_menu,
            file_toolbar,
        )
        self.addAction(
            Translator.translate("MENU_BAR_UPLOAD_ACTION_NAME"),
            Translator.translate("MENU_BAR_UPLOAD_ACTION_STATUS_TIP"),
            lambda: parent.upload(parent.tab.tabs.currentWidget()),
            "upload",
            file_menu,
            file_toolbar,
        )
        self.addAction(
            Translator.translate("MENU_BAR_UPLOAD_AS_ACTION_NAME"),
            Translator.translate("MENU_BAR_UPLOAD_AS_ACTION_STATUS_TIP"),
            lambda: parent.upload_as(parent.tab.tabs.currentWidget()),
            "upload_as",
            file_menu,
            file_toolbar,
        )
        self.addAction(
            Translator.translate("MENU_BAR_CLOSE_ACTION_NAME"),
            Translator.translate("MENU_BAR_CLOSE_ACTION_STATUS_TIP"),
            lambda: parent.tab.remove_tab(parent.tab.tabs.currentIndex),
            "close",
            file_menu,
            file_toolbar,
        )

        self.addSeparator(file_menu, self.contextMenu)

        self.addAction(
            Translator.translate("MENU_BAR_LOAD_SESSION_ACTION_NAME"),
            Translator.translate("MENU_BAR_LOAD_SESSION_ACTION_STATUS_TIP"),
            parent.load_session,
            "load_session",
            file_menu,
        )
        self.addAction(
            Translator.translate("MENU_BAR_SAVE_SESSION_ACTION_NAME"),
            Translator.translate("MENU_BAR_SAVE_SESSION_ACTION_STATUS_TIP"),
            parent.save_session,
            "save_session",
            file_menu,
        )

        self.addSeparator(file_menu)

        self.addAction(
            Translator.translate("MENU_BAR_EXIT_ACTION_NAME"),
            Translator.translate("MENU_BAR_EXIT_ACTION_STATUS_TIP"),
            parent.close,
            "exit",
            file_menu,
        )

        edit_menu = self.addMenu(Translator.translate("MENU_BAR_EDIT_MENU"), menuBar)
        edit_toolbar = self.addToolbar(Translator.translate("MENU_BAR_EDIT_TOOLBAR"))

        self.addAction(
            Translator.translate("MENU_BAR_UNDO_ACTION_NAME"),
            Translator.translate("MENU_BAR_UNDO_ACTION_STATUS_TIP"),
            lambda: parent.tab.tabs.currentWidget().findChild(QPlainTextEdit).undo(),
            "undo",
            edit_menu,
            edit_toolbar,
            self.contextMenu,
        )
        self.addAction(
            Translator.translate("MENU_BAR_REDO_ACTION_NAME"),
            Translator.translate("MENU_BAR_REDO_ACTION_STATUS_TIP"),
            lambda: parent.tab.tabs.currentWidget().findChild(QPlainTextEdit).redo(),
            "redo",
            edit_menu,
            edit_toolbar,
            self.contextMenu,
        )

        self.addSeparator(edit_menu, self.contextMenu)

        self.addAction(
            Translator.translate("MENU_BAR_CUT_ACTION_NAME"),
            Translator.translate("MENU_BAR_CUT_ACTION_STATUS_TIP"),
            lambda: parent.tab.tabs.currentWidget().findChild(QPlainTextEdit).cut(),
            "cut",
            edit_menu,
            edit_toolbar,
            self.contextMenu,
        )
        self.addAction(
            Translator.translate("MENU_BAR_COPY_ACTION_NAME"),
            Translator.translate("MENU_BAR_COPY_ACTION_STATUS_TIP"),
            lambda: parent.tab.tabs.currentWidget().findChild(QPlainTextEdit).copy(),
            "copy",
            edit_menu,
            edit_toolbar,
            self.contextMenu,
        )
        self.addAction(
            Translator.translate("MENU_BAR_PASTE_ACTION_NAME"),
            Translator.translate("MENU_BAR_PASTE_ACTION_STATUS_TIP"),
            lambda: parent.tab.tabs.currentWidget().findChild(QPlainTextEdit).paste(),
            "paste",
            edit_menu,
            edit_toolbar,
            self.contextMenu,
        )
        self.addAction(
            Translator.translate("MENU_BAR_SELECT_ALL_ACTION_NAME"),
            Translator.translate("MENU_BAR_SELECT_ALL_ACTION_STATUS_TIP"),
            lambda: parent.tab.tabs.currentWidget()
            .findChild(QPlainTextEdit)
            .selectAll(),
            "redo",
            edit_menu,
            self.contextMenu,
        )

        self.addSeparator(edit_menu, self.contextMenu)

        self.addAction(
            Translator.translate("MENU_BAR_PREVIEW_ACTION_NAME"),
            Translator.translate("MENU_BAR_PREVIEW_ACTION_STATUS_TIP"),
            lambda: Previewer(parent.tab.tabs.currentWidget().data, parent),
            "preview",
            edit_menu,
            edit_toolbar,
            self.contextMenu,
        )

        element_menu = self.addMenu(
            Translator.translate("MENU_BAR_INSERT_MENU"), edit_menu, self.contextMenu
        )

        for element in skippy.utils.elements.Elements.elements:
            name = element.__alias__ if element.__alias__ else element.__name__
            self.addAction(
                name,
                name,
                partial(ElementGenerator, element, parent),
                None,
                element_menu,
            )

        settings_menu = self.addMenu(
            Translator.translate("MENU_BAR_SETTINGS_MENU"), menuBar
        )

        self.addAction(
            Translator.translate("MENU_BAR_TOGGLE_THEME_ACTION_NAME"),
            Translator.translate("MENU_BAR_TOGGLE_THEME_ACTION_STATUS_TIP"),
            parent.toggle_theme,
            "toggle",
            settings_menu,
        )

        language_menu = self.addMenu(
            Translator.translate("MENU_BAR_LANGUAGES_MENU"), settings_menu
        )

        for lang in Translator.languages():
            name = Translator.getLangName(lang)
            self.addAction(
                name, name, partial(parent.updateTranslate, lang), None, language_menu
            )

        self.addSeparator(settings_menu)

        self.addAction(
            Translator.translate("MENU_BAR_LOGIN_ACTION_NAME"),
            Translator.translate("MENU_BAR_LOGIN_ACTION_STATUS_TIP"),
            parent.login,
            "login",
            settings_menu,
        )
        self.addAction(
            Translator.translate("MENU_BAR_LOGOUT_ACTION_NAME"),
            Translator.translate("MENU_BAR_LOGOUT_ACTION_STATUS_TIP"),
            parent.logout,
            "logout",
            settings_menu,
        )

    def addMenu(self, label="", *widgets):
        menu = QMenu(label, self.parent)
        for widget in widgets:
            widget.addMenu(menu)

        return menu

    def addToolbar(self, label):
        toolbar = QToolBar(label, self.parent)
        toolbar.setIconSize(QSize(20, 20))
        self.parent.addToolBar(toolbar)

        return toolbar

    def addSeparator(self, *menus):
        for menu in menus:
            menu.addSeparator()

    def addAction(self, label, statusTip, func, img, *menus):
        action = QAction(
            label,
            self,
        )

        if img:
            action.setIcon(
                QIcon(
                    os.path.join(
                        skippy.config.ASSETS_FOLDER,
                        self.parent.settings.value("mode", "light"),
                        f"{img}.png",
                    )
                )
            )
            self.action_list[action] = img
        action.setStatusTip(statusTip)
        action.triggered.connect(func)
        for menu in menus:
            menu.addAction(action)

        return action


class LoginStatusWidget(QWidget):
    def __init__(self, parent=None):
        super(LoginStatusWidget, self).__init__(parent)
        self.parent = parent
        self.setGeometry(300, 300, 300, 50)

        self.layout = QVBoxLayout(self)

        self.label = QLabel("", self)
        self.label.setFont(QFont("Arial", 10))

        self.layout.addWidget(self.label)
        self.setLayout(self.layout)

        username = skippy.utils.profile.Profile.load()[0]
        if username != "" and username != None:
            self.label.setText(
                Translator.translate("SIGNED_IN_AS_LABEL").format(username)
            )
        else:
            self.label.setText("")

    def set_text(self, text):
        self.label.setText(Translator.translate("SIGNED_IN_AS_LABEL").format(username))


class ProjectList(QWidget):
    def __init__(self, parent=None):
        super(ProjectList, self).__init__(parent)
        self.parent = parent

        self.layout = QVBoxLayout(self)

        self.tabs = QTabWidget()
        self.tabs.setStyleSheet("QTabBar{font-family: Arial; font-size:10pt;}")
        self.tabs.setTabsClosable(True)
        self.tabs.setMovable(True)
        self.tabs.resize(300, 200)
        self.setAcceptDrops(True)

        self.tabs.tabCloseRequested.connect(self.remove_tab)

        self.layout.addWidget(self.tabs)
        self.setLayout(self.layout)

        self.new_tab()

    @skippy.utils.critical.critical
    def new_tab(self, title="New Tab", source="", tags=[], files={}, parent=None):
        tab = QWidget()
        self.tabs.addTab(tab, title)
        tab.layout = QVBoxLayout(tab)

        if type(files) != dict:
            files = {}

        tab.data = {
            "title": title,
            "source": source,
            "tags": tags,
            "files": files,
            "parent": parent,
        }

        def setData(param, data):
            tab.data[param] = data
            self.parent.session.save()

        title_box = QLineEdit()
        title_box.setText(title)
        title_box.textEdited.connect(
            lambda: self.tabs.setTabText(self.tabs.currentIndex(), title_box.text())
        )
        title_box.textEdited.connect(lambda text: setData("title", text))
        title_box.textEdited.connect(self.parent.update_title)
        title_box.setStyleSheet("QLineEdit{font-family: Arial; font-size:11pt;}")

        editor = CustomPlainTextEdit(self.parent, self)
        editor.setPlainText(source)
        editor.textChanged.connect(lambda: setData("source", editor.toPlainText()))
        editor.setStyleSheet("QPlainTextEdit{font-family: Arial; font-size:11pt;}")

        tags_box = QLineEdit()
        if isinstance(tags, collections.Iterable):
            tags_box.setText(" ".join(tags))
        tags_box.textChanged.connect(lambda text: setData("tags", text.split(" ")))
        tags_box.setStyleSheet("QLineEdit{font-family: Arial; font-size:11pt;}")

        files_button = QPushButton(Translator.translate("FILES_BUTTON"), self)
        files_button.clicked.connect(self.files_dialog)

        tab.layout.addWidget(title_box)
        tab.layout.addWidget(editor)
        tab.layout.addWidget(tags_box)
        tab.layout.addWidget(files_button)
        tab.setLayout(tab.layout)

        log.debug(f"New tab is created with title: {title}")

        self.tabs.setCurrentIndex(self.tabs.count() - 1)

    @skippy.utils.critical.critical
    def remove_tab(self, index):
        self.tabs.removeTab(index)
        if self.tabs.count() == 0:
            self.new_tab()

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
            self.dadDialog = DragAndDropWidget(self)
        else:
            event.ignore()

    def dragLeaveEvent(self, event):
        event.accept()
        self.dadDialog.close()

    def dropEvent(self, event):
        self.dadDialog.close()
        widget = self.tabs.currentWidget()
        files = [u.toLocalFile() for u in event.mimeData().urls()]
        self.thread = QThread()
        self.worker = UploadFiles(files)
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.parent.session.save)
        self.worker.progress.connect(self.upload_file)
        self.thread.start()

    def upload_file(self, file):
        filename = file[0]
        source = file[1]
        widget = self.tabs.currentWidget()
        widget.data["files"][filename] = (
            str(base64.b64encode(source)).replace("b'", "").replace("'", "")
        )

    def files_dialog(self):
        widget = self.tabs.currentWidget()
        fDialog = FilesDialog(widget, self)


class CustomPlainTextEdit(QPlainTextEdit):
    def __init__(self, mainWindow, parent=None):
        super(CustomPlainTextEdit, self).__init__(parent)
        self.mainWindow = mainWindow

    def contextMenuEvent(self, event):
        self.mainWindow.menus.contextMenu.exec_(self.mapToGlobal(event.pos()))


class DragAndDropWidget(QWidget):
    CLOSE = pyqtSignal()

    def __init__(self, parent=None):
        super(DragAndDropWidget, self).__init__(parent)
        self.width, self.height = (
            parent.frameGeometry().width(),
            parent.frameGeometry().height(),
        )

        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)

        self.fillColor = QColor(30, 30, 30, 120)
        self.penColor = QColor("#333333")

        self.popup_fillColor = QColor(240, 240, 240, 255)
        self.popup_penColor = QColor(200, 200, 200, 255)

        self.CLOSE.connect(self.close)

        self.move(0, 0)
        self.resize(self.width, self.height)
        self.show()

    def resizeEvent(self, event):
        popup_width = 300
        popup_height = 120
        ow = int(self.width / 2 - popup_width / 2)
        oh = int(self.height / 2 - popup_height / 2)

    def paintEvent(self, event):
        s = self.size()
        qp = QPainter()
        qp.begin(self)
        qp.setRenderHint(QPainter.Antialiasing, True)
        qp.setPen(self.penColor)
        qp.setBrush(self.fillColor)
        qp.drawRect(0, 0, self.width, self.height)

        qp.setPen(self.popup_penColor)
        qp.setBrush(self.popup_fillColor)
        popup_width = 300
        popup_height = 120
        ow = int(self.width / 2 - popup_width / 2)
        oh = int(self.height / 2 - popup_height / 2)
        qp.drawRoundedRect(ow, oh, popup_width, popup_height, 5, 5)

        font = QFont()
        font.setPixelSize(18)
        font.setBold(True)
        qp.setFont(font)
        qp.setPen(QColor(70, 70, 70))
        tolw, tolh = 80, -5
        qp.drawText(
            ow + int(popup_width / 2) - tolw,
            oh + int(popup_height / 2) - tolh,
            "Drop File.",
        )

        qp.end()

    def _close(self):
        self.CLOSE.emit()


class FilesDialog(QDialog):
    def __init__(self, widget, parent=None):
        super(FilesDialog, self).__init__(parent)
        self.parent = parent
        self.layout = QVBoxLayout(self)

        self.label = QLabel(Translator.translate("FILES_DIALOG_FILES_LIST_LABEL"), self)
        self.label.setFont(QFont("Arial", 10))

        self.fileWidget = FilesWidget(widget, self)

        self.layout.addWidget(self.label, alignment=Qt.AlignCenter)

        if self.fileWidget.layout.count():
            self.layout.addWidget(self.fileWidget, alignment=Qt.AlignCenter)
        else:
            self.empty_files_label = QLabel(
                Translator.translate("FILES_DIALOG_EMPTY_FILES_LIST_LABEL"), self
            )
            self.layout.addWidget(self.empty_files_label, alignment=Qt.AlignCenter)

        self.setLayout(self.layout)

        self.setWindowTitle(f"skippy - {skippy.config.version}")
        self.move(300, 300)
        self.resize(200, 100)
        self.show()


class FilesWidget(QWidget):
    def __init__(self, widget, parent=None):
        super(FilesWidget, self).__init__(parent)
        self.parent = parent

        self.layout = QGridLayout(self)

        files = widget.data["files"]

        pos = []
        for i in range(int(len(files) / 3)):
            for j in range(3):
                pos.append((i, j))
        if len(files) % 3 != 0:
            for i in range(len(files) % 3):
                pos.append((int(len(files) / 3), i))

        for e, p in zip(list(files.keys()), pos):
            self.layout.addWidget(FileWidget(e, widget, self), p[0], p[1])

        self.setLayout(self.layout)


class FileWidget(QWidget):
    del_widget = pyqtSignal(str)

    def __init__(self, title, widget, parent=None):
        super(FileWidget, self).__init__(parent)
        self.parent = parent
        self.widget = widget
        # self.setStyleSheet('background-color: rgb(225,225,225); margin:5px; border:1px solid rgb(0, 0, 0);')

        self.title = title

        self.layout = QGridLayout(self)

        self.lineEdit = QLineEdit(self)
        self.lineEdit.setText(title)
        self.lineEdit.setFixedSize(200, 40)
        self.lineEdit.textEdited.connect(self.set_title)

        self.remove_button = QPushButton("X", self)
        self.remove_button.clicked.connect(self.remove_file)

        self.layout.addWidget(self.lineEdit, 0, 0)
        self.layout.addWidget(self.remove_button, 0, 1)
        self.setLayout(self.layout)

    @skippy.utils.critical.critical
    def set_title(self, text):
        self.widget.data["files"][text] = self.widget.data["files"][self.title]
        log.debug(f"file ")
        del self.widget.data["files"][self.title]
        self.title = text

    @skippy.utils.critical.critical
    def remove_file(self, *args):
        self.widget.data["files"].pop(self.title)


class LoginDialog(QDialog):
    def __init__(self, parent=None):
        super(LoginDialog, self).__init__(parent)
        self.parent = parent

        self.auth = False

        self.layout = QVBoxLayout(self)

        self.label = QLabel(
            Translator.translate("DIALOG_SIGN_IN_TO_WIKIDOT_LABEL"), self
        )
        self.login_box = QLineEdit()
        self.login_box.setPlaceholderText(
            Translator.translate("DIALOG_LOGIN_PLACEHOLDER")
        )
        self.password_box = QLineEdit()
        self.password_box.setEchoMode(QLineEdit.Password)
        self.password_box.setPlaceholderText(
            Translator.translate("DIALOG_PASSWORD_PLACEHOLDER")
        )
        self.button = QPushButton(Translator.translate("DIALOG_OK_BUTTON"), self)

        self.button.clicked.connect(self.login)

        self.layout.addWidget(self.label, alignment=Qt.AlignCenter)
        self.layout.addWidget(self.login_box)
        self.layout.addWidget(self.password_box)
        self.layout.addWidget(self.button, alignment=Qt.AlignRight)

        self.setLayout(self.layout)

        self.setWindowTitle(f"skippy - {skippy.config.version}")
        self.move(300, 300)
        self.resize(200, 100)
        self.show()

    def login(self):
        login = self.login_box.text()
        password = self.password_box.text()
        skippy.utils.profile.Profile.save(login, password)
        self.parent.show()
        self.auth = True
        log.debug(f"Login as {login}")
        self.parent.login_status.set_text(login)
        self.close()

    def closeEvent(self, event):
        if not self.auth:
            sys.exit()


class UploadDialog(QDialog):
    def __init__(self, widget, parent=None):
        super(UploadDialog, self).__init__(parent)
        self.widget = widget
        self.layout = QVBoxLayout(self)

        self.sites = {}

        def addSites(sites):
            if sites[0] in self.sites:
                addSites((sites[0] + "(1)", sites[1]))
            else:
                self.sites[sites[0]] = sites[1]
                self.site_box.addItem(sites[0])

        self.label = QLabel(Translator.translate("DIALOG_ENTER_PAGE_LABEL"), self)
        self.site_box = QComboBox(self)
        self.thread = QThread()
        self.worker = GetSites()
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.thread.quit)
        self.worker.progress.connect(addSites)
        self.page_box = QLineEdit()
        self.page_box.setPlaceholderText(
            Translator.translate("DIALOG_PAGE_BOX_PLACEHOLDER")
        )
        self.comment_box = QLineEdit()
        self.comment_box.setText(Translator.translate("DIALOG_COMMENT_BOX_TEXT"))
        self.comment_box.setPlaceholderText(
            Translator.translate("DIALOG_COMMENT_BOX_PLACEHOLDER")
        )
        self.button = QPushButton(Translator.translate("DIALOG_OK_BUTTON"), self)

        self.button.clicked.connect(self.upload)

        self.layout.addWidget(self.label, alignment=Qt.AlignCenter)
        self.layout.addWidget(self.site_box)
        self.layout.addWidget(self.page_box)
        self.layout.addWidget(self.comment_box)
        self.layout.addWidget(self.button, alignment=Qt.AlignRight)

        self.setLayout(self.layout)

        self.setWindowTitle(f"skippy - {skippy.config.version}")
        self.move(300, 300)
        self.resize(200, 100)

        self.thread.start()
        self.show()

    @skippy.utils.critical.critical
    def upload(self, *args):
        site = str(self.site_box.currentText())
        page = self.page_box.text()
        comment = self.comment_box.text()
        wiki = self.sites[site]
        profile = skippy.utils.profile.Profile.load()
        wiki.auth(profile[0], profile[1])
        p = wiki(page)
        p.edit(
            source=self.widget.data["source"],
            title=self.widget.data["title"],
            comment=comment,
        )
        self.widget.data["parent"] = [
            self.sites[site].site.replace("http://", ""),
            page,
        ]
        p.set_tags(self.widget.data["tags"])

        for file in self.widget.data["files"]:
            try:
                p.upload(file, base64.b64decode(self.widget.data["files"][file]))
                log.debug(f"File {file} is uploaded")
            except RuntimeError as e:
                log.debug(f"RuntimeError({str(e)}): File {file} isn't uploaded")
        for file in p.files:
            if file.name not in self.widget.data["files"]:
                p.remove_file(file.name)
                log.debug(f"File {file.name} is deleted")
        log.debug(
            f"""Upload "{self.widget.data["title"]}" to "{"/".join(self.widget.data["parent"])}" """
        )
        self.close()

    def closeEvent(self, e):
        self.worker.stop()
        e.accept()


class DownloadDialog(QDialog):
    def __init__(self, parent=None):
        super(DownloadDialog, self).__init__(parent)
        self.parent = parent
        self.layout = QVBoxLayout(self)

        self.sites = {}

        def addSites(sites):
            if sites[0] in self.sites:
                addSites((sites[0] + "(1)", sites[1]))
            else:
                self.sites[sites[0]] = sites[1]
                self.site_box.addItem(sites[0])

        self.label = QLabel(Translator.translate("DIALOG_ENTER_PAGE_LABEL"), self)
        self.site_box = QComboBox(self)
        self.thread = QThread()
        self.worker = GetSites()
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.thread.quit)
        self.worker.progress.connect(addSites)
        self.page_box = QLineEdit()
        self.page_box.setPlaceholderText(
            Translator.translate("DIALOG_PAGE_BOX_PLACEHOLDER")
        )
        self.button = QPushButton(Translator.translate("DIALOG_OK_BUTTON"), self)

        self.button.clicked.connect(self.download)

        self.layout.addWidget(self.label, alignment=Qt.AlignCenter)
        self.layout.addWidget(self.site_box)
        self.layout.addWidget(self.page_box)
        self.layout.addWidget(self.button, alignment=Qt.AlignRight)

        self.setLayout(self.layout)

        self.setWindowTitle(f"skippy - {skippy.config.version}")
        self.move(300, 300)
        self.resize(200, 100)

        self.thread.start()
        self.show()

    @skippy.utils.critical.critical
    def download(self, *args):
        site = str(self.site_box.currentText())
        page = self.page_box.text()

        wiki = self.sites[site]
        p = wiki(page)

        url = [self.sites[site].site.replace("http://", ""), page]

        files = {}
        for i in p.files:
            with requests.get(i.url) as res:
                files[i.name] = (
                    str(base64.b64encode(res.content))
                    .replace("b'", "")
                    .replace("'", "")
                )

        self.parent.tab.new_tab(p.title, p.source, list(p.tags), files, url)
        self.parent.session.save()
        log.debug(f"""Download page from "{self.sites[site].site}" """)
        self.close()

    def closeEvent(self, e):
        self.worker.stop()
        e.accept()


class Previewer(QDialog):
    def __init__(self, data, parent=None):
        super(Previewer, self).__init__(parent)
        self.html = Preview(data).process()

        self.layout = QVBoxLayout(self)

        self.webEngineView = QWebEngineView()
        self.webEngineView.load(QUrl.fromLocalFile(self.html))

        self.layout.addWidget(self.webEngineView)

        self.setLayout(self.layout)

        self.setWindowTitle(f"skippy - {skippy.config.version}")
        self.move(parent.x(), parent.y())
        self.resize(parent.width(), parent.height())
        self.setWindowState(parent.windowState())

        self.show()


class ElementGenerator(QDialog):
    def __init__(self, element, parent=None):
        super(ElementGenerator, self).__init__(parent)
        self.element = element()
        self.parent = parent

        self.layout = QVBoxLayout(self)

        self.element_data = QLabel(
            f"{self.element.__alias__} - {self.element.__description__}", self
        )
        self.element_data.setWordWrap(True)

        self.layout.addWidget(self.element_data)
        self.layout.addWidget(QHLine())

        self.scroll = QScrollArea()
        self.widget = QWidget()
        self.vbox = QVBoxLayout()
        self.fields = {}
        for field in self.element.requiredFieldList:
            label = QLabel(field.name if field.name else field.tag, self)

            line = QPlainTextEdit(self)
            line.setFixedHeight(50)
            line.setPlaceholderText(field.description)
            line.textChanged.connect(self.updatePreview)
            self.fields[field.tag] = line

            self.vbox.addWidget(label)
            self.vbox.addWidget(line)

        self.widget.setLayout(self.vbox)

        self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scroll.setWidgetResizable(True)
        self.scroll.setWidget(self.widget)

        self.layout.addWidget(self.scroll)

        self.preview = QPlainTextEdit(self)
        self.preview.setReadOnly(True)

        self.layout.addWidget(self.preview)

        self.button = QPushButton(Translator.translate("DIALOG_GENERATE_BUTTON"), self)
        self.button.clicked.connect(self.process)
        self.layout.addWidget(self.button, alignment=Qt.AlignRight)

        self.setLayout(self.layout)

        self.updatePreview()

        self.setWindowTitle(
            f"{self.element.__alias__} | skippy - {skippy.config.version}"
        )
        self.move(300, 300)
        self.resize(500, 400)

        self.show()

    @skippy.utils.critical.critical
    def updatePreview(self, *args):
        self.preview.setPlainText(self.generate)

    @skippy.utils.critical.critical
    def process(self, *args):
        self.parent.tab.tabs.currentWidget().findChild(QPlainTextEdit).insertPlainText(
            self.generate
        )
        self.close()

    @property
    def generate(self):
        data = {}
        for field in self.fields:
            data[field] = self.fields[field].toPlainText()
        return self.element.generate(**data)


class QHLine(QFrame):
    def __init__(self):
        super(QHLine, self).__init__()
        self.setFrameShape(QFrame.HLine)
        self.setFrameShadow(QFrame.Sunken)


class GetSites(QObject):
    progress = pyqtSignal(tuple)
    finished = pyqtSignal()
    cache = []
    run = True

    def run(self):
        sites = list(pyscp.wikidot.User(skippy.utils.profile.Profile.load()[0]).member)
        for data in self.cache:
            self.progress.emit((data[0], data[1]))
        if len(self.cache) < len(sites):
            for site in sites[len(self.cache) :]:
                if self.run:
                    self.progress.emit((site.title, site))
                    self.cache.append((site.title, site))
                    log.debug(f"Get sites: {site.title}")
        self.finished.emit()

    def stop(self):
        self.run = False


class UploadFiles(QObject):
    progress = pyqtSignal(tuple)
    finished = pyqtSignal()

    def __init__(self, files):
        super(UploadFiles, self).__init__()
        self.files = files

    def run(self):
        log.debug("File loaded started...")
        for file in self.files:
            if os.path.isfile(file):
                filename = os.path.basename(file)
                with open(file, "rb") as f:
                    source = f.read()
                log.debug(f"Load {file}")
                self.progress.emit((filename, source))
        self.finished.emit()
