#!/usr/bin/env python3

# Simple web browser using the Chromium renderer
# TODO: Mange cookies, add Developer Tools, add Adblock

# Based on
# https://github.com/learnpyqt/15-minute-apps/tree/master/browser_tabbed
# Copyright 2017 Martin Fitzpatrick
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


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

import os
import sys


class AboutDialog(QDialog):
    def __init__(self, *args, **kwargs):
        super(AboutDialog, self).__init__(*args, **kwargs)

        QBtn = QDialogButtonBox.Ok  # No cancel
        self.buttonBox = QDialogButtonBox(QBtn)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

        layout = QVBoxLayout()

        title = QLabel("Simple Browser")
        font = title.font()
        font.setPointSize(20)
        title.setFont(font)

        layout.addWidget(title)

        logo = QLabel()
        logo.setPixmap(QPixmap(os.path.join(os.path.dirname(__file__), "Resources", os.path.dirname(__file__), "Resources", 'images', 'ma-icon-128.png')))
        layout.addWidget(logo)

        layout.addWidget(QLabel("Based on <a href='https://github.com/learnpyqt/15-minute-apps/tree/master/browser_tabbed'>Mozzarella Ashbadger</a>"))
        layout.addWidget(QLabel("Copyright 2017 Martin Fitzpatrick"))
        layout.addWidget(QLabel("Copyright 2021 Simon Peter"))

        for i in range(0, layout.count()):
            layout.itemAt(i).setAlignment(Qt.AlignHCenter)

        layout.addWidget(self.buttonBox)

        self.setLayout(layout)


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.resize(1024, 768)

        self.tabs = QTabWidget()
        self.tabs.setDocumentMode(True)
        #self.tabs.setStretch(0, 1)
        self.tabs.setMovable(True)
        self.tabs.tabBarDoubleClicked.connect(self.tab_open_doubleclick)
        self.tabs.currentChanged.connect(self.current_tab_changed)
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(self.close_current_tab)
        self.current_tab = 0

        self.setCentralWidget(self.tabs)

        self.status = QStatusBar()
        self.setStatusBar(self.status)

        navtb = QToolBar("Navigation")
        navtb.setIconSize(QSize(16, 16))
        self.addToolBar(navtb)

        back_btn = QAction(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'arrow-180.png')), "Back", self)
        back_btn.setStatusTip("Back to previous page")
        back_btn.triggered.connect(lambda: self.tabs.currentWidget().back())
        navtb.addAction(back_btn)

        next_btn = QAction(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'arrow-000.png')), "Forward", self)
        next_btn.setStatusTip("Forward to next page")
        next_btn.triggered.connect(lambda: self.tabs.currentWidget().forward())
        navtb.addAction(next_btn)

        reload_btn = QAction(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'arrow-circle-315.png')), "Reload", self)
        reload_btn.setStatusTip("Reload page")
        reload_btn.setShortcut(QKeySequence("Ctrl+R"))
        reload_btn.triggered.connect(lambda: self.tabs.currentWidget().reload())
        navtb.addAction(reload_btn)

        home_btn = QAction(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'home.png')), "Home", self)
        home_btn.setStatusTip("Go home")
        home_btn.triggered.connect(self.navigate_home)
        navtb.addAction(home_btn)

        navtb.addSeparator()

        self.httpsicon = QLabel()  # Yes, really!
        self.httpsicon.setPixmap(QPixmap(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'lock-nossl.png')))
        navtb.addWidget(self.httpsicon)

        self.urlbar = QLineEdit()
        self.urlbar.returnPressed.connect(self.navigate_to_url)
        navtb.addWidget(self.urlbar)

        stop_btn = QAction(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'stop-hex.png')), "Stop", self)
        stop_btn.setStatusTip("Stop loading current page")
        stop_btn.triggered.connect(lambda: self.tabs.currentWidget().stop())
        navtb.addAction(stop_btn)

        # Uncomment to disable native menubar on Mac
        # self.menuBar().setNativeMenuBar(False)

        file_menu = self.menuBar().addMenu("&File")

        new_tab_action = QAction(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'plus.png')), "New Tab", self)
        new_tab_action.setStatusTip("Open a new tab")
        new_tab_action.triggered.connect(lambda _: self.add_new_tab())
        new_tab_action.setShortcut(QKeySequence("Ctrl+T"))
        navtb.addAction(new_tab_action)

        close_tab_action = QAction(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'ui-tab--minus.png')), "Close Tab", self)
        close_tab_action.setStatusTip("Close the current tab")
        close_tab_action.triggered.connect(lambda _: self.close_current_tab(self.current_tab))
        close_tab_action.setShortcut(QKeySequence("Ctrl+W"))
        

        open_file_action = QAction(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'disk--arrow.png')), "Open File...", self)
        open_file_action.setStatusTip("Open from file")
        open_file_action.triggered.connect(self.open_file)
        open_file_action.setShortcut(QKeySequence("Ctrl+O"))

        open_location_action = QAction("Open Location", self)
        open_location_action.setStatusTip("Select Location Bar")
        open_location_action.triggered.connect(self.open_location)
        open_location_action.setShortcut(QKeySequence("Ctrl+L"))

        save_file_action = QAction(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'disk--pencil.png')), "Save Page As...", self)
        save_file_action.setStatusTip("Save current page to file")
        save_file_action.setShortcut(QKeySequence("Ctrl+S"))
        save_file_action.triggered.connect(self.save_file)

        # print_action = QAction(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'printer.png')), "Print...", self)
        # print_action.setStatusTip("Print current page")
        # print_action.triggered.connect(self.print_page)
        # file_menu.addAction(print_action)

        # file_menu.addSeparator()

        quit_action = QAction("Quit", self)
        quit_action.setStatusTip("Quit this application")
        quit_action.setShortcut(QKeySequence("Ctrl+Q"))
        quit_action.triggered.connect(app.exit)

        #File menu
        file_menu.addAction(new_tab_action)
        file_menu.addAction(close_tab_action)
        file_menu.addAction(reload_btn)
        file_menu.addSeparator()
        file_menu.addAction(open_file_action)
        file_menu.addAction(open_location_action)
        file_menu.addAction(save_file_action)
        file_menu.addSeparator()
        file_menu.addAction(quit_action)

        help_menu = self.menuBar().addMenu("&Help")

        about_action = QAction(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'question.png')), "About Simple Browser", self)
        about_action.setStatusTip("Find out more about Simple Browser")  # Hungry!
        about_action.triggered.connect(self.about)
        help_menu.addAction(about_action)

        navigate_mozarella_action = QAction(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'lifebuoy.png')),
                                            "Documentation", self)
        navigate_mozarella_action.setStatusTip("Go to helloSystem Documentation")
        navigate_mozarella_action.triggered.connect(self.navigate_mozarella)
        help_menu.addAction(navigate_mozarella_action)

        self.add_new_tab(QUrl('https://hellosystem.github.io/docs/'), 'Homepage')

        self.show()

        self.setWindowTitle("Simple Browser")
        self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'ma-icon-64.png')))
        
    def onCookieAdded(self, cookie):
        print("Cookie added:", cookie.toRawForm())

    def add_new_tab(self, qurl=None, label="Blank"):
        if qurl is None:
            qurl = QUrl('')

        webview = QWebEngineView()
        #profile = QWebEngineProfile(None, webview) # "Private"
        # https://doc.qt.io/qt-5/qwebengineprofile.html#isOffTheRecord
        # Off-the-record profile that leaves no record on the computer
        # TriVoxel: "Undoing this so we can have a separate "private" mode"

        profile = QWebEngineProfile(webview)
        webpage = QWebEnginePage(profile, webview)
        webview.setPage(webpage)
        if profile.isOffTheRecord() == True:
            print("Private browsing mode")
        
        # Define a callback to do something whenever a cookie is added
        profile.cookieStore().cookieAdded.connect(self.onCookieAdded)
        
        # Block third-party cookies
        profile.cookieStore().setCookieFilter(lambda _info: True)
        
        webview.setUrl(qurl)
        i = self.tabs.addTab(webview, label)

        self.tabs.setCurrentIndex(i)

        # More difficult! We only want to update the url when it's from the
        # correct tab
        webview.urlChanged.connect(lambda qurl, webview=webview:
                                   self.update_urlbar(qurl, webview))

        webview.loadFinished.connect(lambda _, i=i, webview=webview:
                                     self.tabs.setTabText(i, webview.page().title()))

    def tab_open_doubleclick(self, i):
        if i == -1:  # No tab under the click
            self.add_new_tab()

    def current_tab_changed(self, i):
        print("Tab %i" % i)
        self.current_tab = i
        qurl = self.tabs.currentWidget().url()
        self.update_urlbar(qurl, self.tabs.currentWidget())
        self.update_title(self.tabs.currentWidget())

    def close_current_tab(self, i):
        if self.tabs.count() < 2:
            return

        self.tabs.removeTab(i)

    def update_title(self, webview):
        if webview != self.tabs.currentWidget():
            # If this signal is not from the current tab, ignore
            return

        title = self.tabs.currentWidget().page().title()
        self.setWindowTitle("%s" % title)

    def navigate_mozarella(self):
        self.tabs.currentWidget().setUrl(QUrl("https://www.udemy.com/522076"))

    def about(self):
        dlg = AboutDialog()
        dlg.exec_()

    def open_file(self):
        filename, _ = QFileDialog.getOpenFileName(self, "Open file", "",
                                                  "Hypertext Markup Language (*.htm *.html);;"
                                                  "All files (*.*)")

        if filename:
            with open(filename, 'r') as f:
                html = f.read()

            self.tabs.currentWidget().setHtml(html)
            self.urlbar.setText(filename)

    def open_location(self):
        self.urlbar.setFocus()
        self.urlbar.selectAll()

    def save_file(self):
        filename, _ = QFileDialog.getSaveFileName(self, "Save Page As", "",
                                                  "Hypertext Markup Language (*.htm *html);;"
                                                  "All files (*.*)")

        if filename:
            html = self.tabs.currentWidget().page().toHtml()
            with open(filename, 'w') as f:
                f.write(html.encode('utf8'))

    def print_page(self):
        dlg = QPrintPreviewDialog()
        dlg.paintRequested.connect(self.webview.print_)
        dlg.exec_()

    def navigate_home(self):
        self.tabs.currentWidget().setUrl(QUrl("https://hellosystem.github.io/docs/"))

    def navigate_to_url(self):  # Does not receive the Url
        q = QUrl(self.urlbar.text())
        if q.scheme() == "":
            q.setScheme("http")

        self.tabs.currentWidget().setUrl(q)

    def update_urlbar(self, q, webview=None):

        if webview != self.tabs.currentWidget():
            # If this signal is not from the current tab, ignore
            return

        if q.scheme() == 'https':
            # Secure padlock icon
            self.httpsicon.setPixmap(QPixmap(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'lock-ssl.png')))

        else:
            # Insecure padlock icon
            self.httpsicon.setPixmap(QPixmap(os.path.join(os.path.dirname(__file__), "Resources", 'images', 'lock-nossl.png')))

        self.urlbar.setText(q.toString())
        self.urlbar.setCursorPosition(0)


app = QApplication(sys.argv)
app.setApplicationName("Simple Browser")
app.setOrganizationName("Mozarella")
app.setOrganizationDomain("mozarella.org")

window = MainWindow()

app.exec_()
