// Copyright (c) 2023 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.

import QtQml
import QtQuick
import QtQuick.Window
import Qt.labs.platform

import Proton
import Notifications

QtObject {
    id: root

    function isInInterval(num, lower_limit, upper_limit) {
        return lower_limit <= num && num <= upper_limit
    }
    function bound(num, lower_limit, upper_limit) {
        return Math.max(lower_limit, Math.min(upper_limit, num))
    }

    property var title: Backend.appname

    property Notifications _notifications: Notifications {
        id: notifications
        frontendMain: mainWindow
        frontendStatus: statusWindow
        frontendTray: trayIcon
    }

    property MainWindow _mainWindow: MainWindow {
        id: mainWindow
        visible: false

        title: root.title
        notifications: root._notifications

        onVisibleChanged: {
            Backend.dockIconVisible = visible
        }

        Connections {
            target: Backend
            function onDiskCacheUnavailable() {
                mainWindow.showAndRise()
            }
            function onColorSchemeNameChanged(scheme) { root.setColorScheme() }

            function onHideMainWindow() {
                mainWindow.hide();
            }
        }
    }

    property StatusWindow _statusWindow: StatusWindow {
        id: statusWindow
        visible: false

        title: root.title
        notifications: root._notifications

        onShowMainWindow: {
            mainWindow.showAndRise()
        }

        onShowHelp: {
            mainWindow.showHelp()
            mainWindow.showAndRise()
        }

        onShowSettings: {
            mainWindow.showSettings()
            mainWindow.showAndRise()
        }

        onSelectUser: function(userID) {
            mainWindow.selectUser(userID)
            mainWindow.showAndRise()
        }

        onQuit: {
            mainWindow.hide()
            trayIcon.visible = false
            Backend.quit()
        }

        property rect screenRect
        property rect iconRect

        // use binding from function with width and height as arguments so it will be recalculated every time width and height are changed
        property point position: getPosition(width, height)
        x: position.x
        y: position.y

        function getPosition(_width, _height) {
            if (screenRect.width === 0 || screenRect.height === 0) {
                return Qt.point(0, 0)
            }

            var _x = 0
            var _y = 0

            // fit above
            _y = iconRect.top - height
            if (isInInterval(_y, screenRect.top, screenRect.bottom - height)) {
                // position preferably in the horizontal center but bound to the screen rect
                _x = bound(iconRect.left + (iconRect.width - width)/2, screenRect.left, screenRect.right - width)
                return Qt.point(_x, _y)
            }

            // fit below
            _y = iconRect.bottom
            if (isInInterval(_y, screenRect.top, screenRect.bottom - height)) {
                // position preferably in the horizontal center but bound to the screen rect
                _x = bound(iconRect.left + (iconRect.width - width)/2, screenRect.left, screenRect.right - width)
                return Qt.point(_x, _y)
            }

            // fit to the left
            _x = iconRect.left - width
            if (isInInterval(_x, screenRect.left, screenRect.right - width)) {
                // position preferably in the vertical center but bound to the screen rect
                _y = bound(iconRect.top + (iconRect.height - height)/2, screenRect.top, screenRect.bottom - height)
                return Qt.point(_x, _y)
            }

            // fit to the right
            _x = iconRect.right
            if (isInInterval(_x, screenRect.left, screenRect.right - width)) {
                // position preferably in the vertical center but bound to the screen rect
                _y = bound(iconRect.top + (iconRect.height - height)/2, screenRect.top, screenRect.bottom - height)
                return Qt.point(_x, _y)
            }

            // Fallback: position status window right above icon and let window manager decide.
            console.warn("Can't position status window: screenRect =", screenRect, "iconRect =", iconRect)
            _x = bound(iconRect.left + (iconRect.width - width)/2, screenRect.left, screenRect.right - width)
            _y = bound(iconRect.top + (iconRect.height - height)/2, screenRect.top, screenRect.bottom - height)
            return Qt.point(_x, _y)
        }
    }

    property SystemTrayIcon _trayIcon: SystemTrayIcon {
        id: trayIcon
        visible: true
        icon.source: getTrayIconPath()
        icon.mask: true // make sure that systems like macOS will use proper color
        tooltip: `${root.title} v${Backend.version}`
        onActivated: function(reason) {
            function calcStatusWindowPosition() {
                // On some platforms (X11 / Plasma) Qt does not provide icon position and geometry info.
                // In this case we rely on cursor position
                var iconRect = Qt.rect(geometry.x, geometry.y, geometry.width, geometry.height)
                if (geometry.width == 0 && geometry.height == 0) {
                    var mousePos = Backend.getCursorPos()
                    iconRect.x = mousePos.x
                    iconRect.y = mousePos.y
                    iconRect.width = 0
                    iconRect.height = 0
                }

                // Find screen
                var screen
                for (var i in Qt.application.screens) {
                    var _screen = Qt.application.screens[i]
                    if (
                        isInInterval(iconRect.x, _screen.virtualX, _screen.virtualX + _screen.width) &&
                        isInInterval(iconRect.y, _screen.virtualY, _screen.virtualY + _screen.height)
                    ) {
                        screen = _screen
                        break
                    }
                }
                if (!screen) {
                    // Fallback to primary screen
                    screen = Qt.application.screens[0]
                }

                // In case we used mouse to detect icon position - we want to make a fake icon rectangle from a point
                if (iconRect.width == 0 && iconRect.height == 0) {
                    iconRect.x = bound(iconRect.x - 16, screen.virtualX, screen.virtualX + screen.width - 32)
                    iconRect.y = bound(iconRect.y - 16, screen.virtualY, screen.virtualY + screen.height - 32)
                    iconRect.width = 32
                    iconRect.height = 32
                }

                statusWindow.screenRect = Qt.rect(screen.virtualX, screen.virtualY, screen.width, screen.height)
                statusWindow.iconRect = iconRect
            }

            function toggleWindow(win) {
                if (win.visible) {
                    win.close()
                } else {
                    win.showAndRise()
                }
            }


            switch (reason) {
                case SystemTrayIcon.Unknown:
                break;
                case SystemTrayIcon.Context:
                case SystemTrayIcon.Trigger:
                case SystemTrayIcon.DoubleClick:
                case SystemTrayIcon.MiddleClick:
                calcStatusWindowPosition()
                toggleWindow(statusWindow)
                break;
                default:
                break;
            }
        }

        property NotificationFilter _systrayfilter: NotificationFilter {
            source: root._notifications ? root._notifications.all : undefined
        }

        function getTrayIconPath() {
            var color = Backend.goos == "darwin" ? "mono" : "color"

            var level = "norm"
            if (_systrayfilter.topmost) {
                switch (_systrayfilter.topmost.type) {
                    case Notification.NotificationType.Danger:
                    level = "error"
                    break;
                    case Notification.NotificationType.Warning:
                    level = "warn"
                    break;
                    case Notification.NotificationType.Info:
                    level = "update"
                    break;
                }
            }
            return `qrc:/qml/icons/systray-${color}-${level}.png`
        }
    }

    Component.onCompleted: {
        if (!Backend) {
            console.log("Backend not loaded")
        }

        root.setColorScheme()


        if (!Backend.users) {
            console.log("users not loaded")
        }

        var c = Backend.users.count
        var u = Backend.users.get(0)
        // DEBUG
        if (c != 0) {
            console.log("users non zero", c)
            console.log("first user", u )
        }

        if (c === 0) {
            mainWindow.showAndRise()
        }

        if (u) {
            if (c === 1 && (u.state === EUserState.SignedOut)) {
                mainWindow.showAndRise()
            }
        }

        Backend.guiReady()

         if (Backend.showOnStartup || Backend.showSplashScreen) {
            mainWindow.showAndRise()
        }

    }

    function setColorScheme() {
        if (Backend.colorSchemeName == "light") ProtonStyle.currentStyle = ProtonStyle.lightStyle
        if (Backend.colorSchemeName == "dark") ProtonStyle.currentStyle = ProtonStyle.darkStyle
    }
}
