import Qt5Compat.GraphicalEffects
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts

import gpt4all
import mysettings

ColumnLayout {

property var inputBoxText: null
signal setInputBoxText(text: string)

Item {

Layout.fillWidth: true
Layout.maximumWidth: parent.width
Layout.preferredHeight: gridLayout.height

HoverHandler { id: hoverArea }

GridLayout {
    id: gridLayout
    anchors.left: parent.left
    anchors.right: parent.right
    columns: 2

    Item {
        Layout.row: 0
        Layout.column: 0
        Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
        Layout.preferredWidth: 32
        Layout.preferredHeight: 32
        Image {
            id: logo
            sourceSize: Qt.size(32, 32)
            fillMode: Image.PreserveAspectFit
            mipmap: true
            visible: false
            source: name !== "Response: " ? "qrc:/gpt4all/icons/you.svg" : "qrc:/gpt4all/icons/gpt4all_transparent.svg"
        }

        ColorOverlay {
            id: colorOver
            anchors.fill: logo
            source: logo
            color: theme.conversationHeader
            RotationAnimation {
                id: rotationAnimation
                target: colorOver
                property: "rotation"
                from: 0
                to: 360
                duration: 1000
                loops: Animation.Infinite
                running: isCurrentResponse && currentChat.responseInProgress
            }
        }
    }

    Item {
        Layout.row: 0
        Layout.column: 1
        Layout.fillWidth: true
        Layout.preferredHeight: 38
        RowLayout {
            spacing: 5
            anchors.left: parent.left
            anchors.top: parent.top
            anchors.bottom: parent.bottom

            TextArea {
                text: name === "Response: " ? qsTr("GPT4All") : qsTr("You")
                padding: 0
                font.pixelSize: theme.fontSizeLarger
                font.bold: true
                color: theme.conversationHeader
                enabled: false
                focus: false
                readOnly: true
            }
            Text {
                visible: name === "Response: "
                font.pixelSize: theme.fontSizeLarger
                text: currentModelName()
                color: theme.mutedTextColor
            }
            RowLayout {
                visible: isCurrentResponse && (value === "" && currentChat.responseInProgress)
                Text {
                    color: theme.mutedTextColor
                    font.pixelSize: theme.fontSizeLarger
                    text: {
                        switch (currentChat.responseState) {
                        case Chat.ResponseStopped: return qsTr("response stopped ...");
                        case Chat.LocalDocsRetrieval: return qsTr("retrieving localdocs: %1 ...").arg(currentChat.collectionList.join(", "));
                        case Chat.LocalDocsProcessing: return qsTr("searching localdocs: %1 ...").arg(currentChat.collectionList.join(", "));
                        case Chat.PromptProcessing: return qsTr("processing ...")
                        case Chat.ResponseGeneration: return qsTr("generating response ...");
                        case Chat.GeneratingQuestions: return qsTr("generating questions ...");
                        default: return ""; // handle unexpected values
                        }
                    }
                }
            }
        }
    }

    ColumnLayout {
        Layout.row: 1
        Layout.column: 1
        Layout.fillWidth: true
        spacing: 10
        Flow {
            id: attachedUrlsFlow
            Layout.fillWidth: true
            Layout.bottomMargin: 10
            spacing: 10
            visible: promptAttachments.length !== 0
            Repeater {
                model: promptAttachments

                delegate: Rectangle {
                    width: 350
                    height: 50
                    radius: 5
                    color: theme.attachmentBackground
                    border.color: theme.controlBorder

                    Row {
                        spacing: 5
                        anchors.fill: parent
                        anchors.margins: 5

                        MyFileIcon {
                            iconSize: 40
                            fileName: modelData.file
                        }

                        Text {
                            width: 295
                            height: 40
                            text: modelData.file
                            color: theme.textColor
                            horizontalAlignment: Text.AlignHLeft
                            verticalAlignment: Text.AlignVCenter
                            font.pixelSize: theme.fontSizeMedium
                            font.bold: true
                            wrapMode: Text.WrapAnywhere
                            elide: Qt.ElideRight
                        }
                    }
                }
            }
        }

        TextArea {
            id: myTextArea
            Layout.fillWidth: true
            padding: 0
            color: {
                if (!currentChat.isServer)
                    return theme.textColor
                return theme.white
            }
            wrapMode: Text.WordWrap
            textFormat: TextEdit.PlainText
            focus: false
            readOnly: true
            font.pixelSize: theme.fontSizeLarge
            cursorVisible: isCurrentResponse ? currentChat.responseInProgress : false
            cursorPosition: text.length
            TapHandler {
                id: tapHandler
                onTapped: function(eventPoint, button) {
                    var clickedPos = myTextArea.positionAt(eventPoint.position.x, eventPoint.position.y);
                    var success = textProcessor.tryCopyAtPosition(clickedPos);
                    if (success)
                        copyCodeMessage.open();
                }
            }

            MouseArea {
                id: conversationMouseArea
                anchors.fill: parent
                acceptedButtons: Qt.RightButton

                onClicked: (mouse) => {
                               if (mouse.button === Qt.RightButton) {
                                   conversationContextMenu.x = conversationMouseArea.mouseX
                                   conversationContextMenu.y = conversationMouseArea.mouseY
                                   conversationContextMenu.open()
                               }
                           }
            }

            onLinkActivated: function(link) {
                if (!isCurrentResponse || !currentChat.responseInProgress)
                    Qt.openUrlExternally(link)
            }

            onLinkHovered: function (link) {
                if (!isCurrentResponse || !currentChat.responseInProgress)
                    statusBar.externalHoveredLink = link
            }

            MyMenu {
                id: conversationContextMenu
                MyMenuItem {
                    text: qsTr("Copy")
                    enabled: myTextArea.selectedText !== ""
                    height: enabled ? implicitHeight : 0
                    onTriggered: myTextArea.copy()
                }
                MyMenuItem {
                    text: qsTr("Copy Message")
                    enabled: myTextArea.selectedText === ""
                    height: enabled ? implicitHeight : 0
                    onTriggered: {
                        myTextArea.selectAll()
                        myTextArea.copy()
                        myTextArea.deselect()
                    }
                }
                MyMenuItem {
                    text: textProcessor.shouldProcessText ? qsTr("Disable markdown") : qsTr("Enable markdown")
                    height: enabled ? implicitHeight : 0
                    onTriggered: {
                        textProcessor.shouldProcessText = !textProcessor.shouldProcessText;
                        textProcessor.setValue(value);
                    }
                }
            }

            ChatViewTextProcessor {
                id: textProcessor
            }

            function resetChatViewTextProcessor() {
                textProcessor.fontPixelSize                = myTextArea.font.pixelSize
                textProcessor.codeColors.defaultColor      = theme.codeDefaultColor
                textProcessor.codeColors.keywordColor      = theme.codeKeywordColor
                textProcessor.codeColors.functionColor     = theme.codeFunctionColor
                textProcessor.codeColors.functionCallColor = theme.codeFunctionCallColor
                textProcessor.codeColors.commentColor      = theme.codeCommentColor
                textProcessor.codeColors.stringColor       = theme.codeStringColor
                textProcessor.codeColors.numberColor       = theme.codeNumberColor
                textProcessor.codeColors.headerColor       = theme.codeHeaderColor
                textProcessor.codeColors.backgroundColor   = theme.codeBackgroundColor
                textProcessor.textDocument                 = textDocument
                textProcessor.setValue(value);
            }

            property bool textProcessorReady: false

            Component.onCompleted: {
                resetChatViewTextProcessor();
                textProcessorReady = true;
            }

            Connections {
                target: chatModel
                function onValueChanged(i, value) {
                    if (myTextArea.textProcessorReady && index === i)
                        textProcessor.setValue(value);
                }
            }

            Connections {
                target: MySettings
                function onFontSizeChanged() {
                    myTextArea.resetChatViewTextProcessor();
                }
                function onChatThemeChanged() {
                    myTextArea.resetChatViewTextProcessor();
                }
            }

            Accessible.role: Accessible.Paragraph
            Accessible.name: text
            Accessible.description: name === "Response: " ? "The response by the model" : "The prompt by the user"
        }

        ThumbsDownDialog {
            id: thumbsDownDialog
            x: Math.round((parent.width - width) / 2)
            y: Math.round((parent.height - height) / 2)
            width: 640
            height: 300
            property string text: value
            response: newResponse === undefined || newResponse === "" ? text : newResponse
            onAccepted: {
                var responseHasChanged = response !== text && response !== newResponse
                if (thumbsDownState && !thumbsUpState && !responseHasChanged)
                    return

                chatModel.updateNewResponse(index, response)
                chatModel.updateThumbsUpState(index, false)
                chatModel.updateThumbsDownState(index, true)
                Network.sendConversation(currentChat.id, getConversationJson());
            }
        }
    }

    Item {
        Layout.row: 2
        Layout.column: 1
        Layout.topMargin: 5
        Layout.alignment: Qt.AlignVCenter
        Layout.preferredWidth: childrenRect.width
        Layout.preferredHeight: childrenRect.height
        visible: {
            if (name !== "Response: ")
                return false
            if (consolidatedSources.length === 0)
                return false
            if (!MySettings.localDocsShowReferences)
                return false
            if (isCurrentResponse && currentChat.responseInProgress
                    && currentChat.responseState !== Chat.GeneratingQuestions )
                return false
            return true
        }

        MyButton {
            backgroundColor: theme.sourcesBackground
            backgroundColorHovered: theme.sourcesBackgroundHovered
            contentItem: RowLayout {
                anchors.centerIn: parent

                Item {
                    Layout.preferredWidth: 24
                    Layout.preferredHeight: 24

                    Image {
                        id: sourcesIcon
                        visible: false
                        anchors.fill: parent
                        sourceSize.width: 24
                        sourceSize.height: 24
                        mipmap: true
                        source: "qrc:/gpt4all/icons/db.svg"
                    }

                    ColorOverlay {
                        anchors.fill: sourcesIcon
                        source: sourcesIcon
                        color: theme.textColor
                    }
                }

                Text {
                    text: qsTr("%n Source(s)", "", consolidatedSources.length)
                    padding: 0
                    font.pixelSize: theme.fontSizeLarge
                    font.bold: true
                    color: theme.styledTextColor
                }

                Item {
                    Layout.preferredWidth: caret.width
                    Layout.preferredHeight: caret.height
                    Image {
                        id: caret
                        anchors.centerIn: parent
                        visible: false
                        sourceSize.width: theme.fontSizeLarge
                        sourceSize.height: theme.fontSizeLarge
                        mipmap: true
                        source: {
                            if (sourcesLayout.state === "collapsed")
                                return "qrc:/gpt4all/icons/caret_right.svg";
                            else
                                return "qrc:/gpt4all/icons/caret_down.svg";
                        }
                    }

                    ColorOverlay {
                        anchors.fill: caret
                        source: caret
                        color: theme.textColor
                    }
                }
            }

            onClicked: {
                if (sourcesLayout.state === "collapsed")
                    sourcesLayout.state = "expanded";
                else
                    sourcesLayout.state = "collapsed";
            }
        }
    }

    ColumnLayout {
        id: sourcesLayout
        Layout.row: 3
        Layout.column: 1
        Layout.topMargin: 5
        visible: {
            if (consolidatedSources.length === 0)
                return false
            if (!MySettings.localDocsShowReferences)
                return false
            if (isCurrentResponse && currentChat.responseInProgress
                    && currentChat.responseState !== Chat.GeneratingQuestions )
                return false
            return true
        }
        clip: true
        Layout.fillWidth: true
        Layout.preferredHeight: 0
        state: "collapsed"
        states: [
            State {
                name: "expanded"
                PropertyChanges { target: sourcesLayout; Layout.preferredHeight: flow.height }
            },
            State {
                name: "collapsed"
                PropertyChanges { target: sourcesLayout; Layout.preferredHeight: 0 }
            }
        ]

        transitions: [
            Transition {
                SequentialAnimation {
                    PropertyAnimation {
                        target: sourcesLayout
                        property: "Layout.preferredHeight"
                        duration: 300
                        easing.type: Easing.InOutQuad
                    }
                }
            }
        ]

        Flow {
            id: flow
            Layout.fillWidth: true
            spacing: 10
            visible: consolidatedSources.length !== 0
            Repeater {
                model: consolidatedSources

                delegate: Rectangle {
                    radius: 10
                    color: ma.containsMouse ? theme.sourcesBackgroundHovered : theme.sourcesBackground
                    width: 200
                    height: 75

                    MouseArea {
                        id: ma
                        enabled: modelData.path !== ""
                        anchors.fill: parent
                        hoverEnabled: true
                        onClicked: function() {
                            Qt.openUrlExternally(modelData.fileUri)
                        }
                    }

                    Rectangle {
                        id: debugTooltip
                        anchors.right: parent.right
                        anchors.bottom: parent.bottom
                        width: 24
                        height: 24
                        color: "transparent"
                        ToolTip {
                            parent: debugTooltip
                            visible: debugMouseArea.containsMouse
                            text: modelData.text
                            contentWidth: 900
                            delay: 500
                        }
                        MouseArea {
                            id: debugMouseArea
                            anchors.fill: parent
                            hoverEnabled: true
                        }
                    }

                    ColumnLayout {
                        anchors.left: parent.left
                        anchors.top: parent.top
                        anchors.margins: 10
                        spacing: 0
                        RowLayout {
                            id: title
                            spacing: 5
                            Layout.maximumWidth: 180
                            MyFileIcon {
                                iconSize: 24
                                fileName: modelData.file
                                Layout.preferredWidth: iconSize
                                Layout.preferredHeight: iconSize
                            }
                            Text {
                                Layout.maximumWidth: 156
                                text: modelData.collection !== "" ? modelData.collection : qsTr("LocalDocs")
                                font.pixelSize: theme.fontSizeLarge
                                font.bold: true
                                color: theme.styledTextColor
                                elide: Qt.ElideRight
                            }
                            Rectangle {
                                Layout.fillWidth: true
                                color: "transparent"
                                height: 1
                            }
                        }
                        Text {
                            Layout.fillHeight: true
                            Layout.maximumWidth: 180
                            Layout.maximumHeight: 55 - title.height
                            text: modelData.file
                            color: theme.textColor
                            font.pixelSize: theme.fontSizeSmall
                            elide: Qt.ElideRight
                            wrapMode: Text.WrapAnywhere
                        }
                    }
                }
            }
        }
    }

    ConfirmationDialog {
        id: editPromptDialog
        dialogTitle: qsTr("Edit this message?")
        description: qsTr("All following messages will be permanently erased.")
        onAccepted: {
            const msg = currentChat.popPrompt(index);
            if (msg !== null)
                setInputBoxText(msg);
        }
    }

    ConfirmationDialog {
        id: redoResponseDialog
        dialogTitle: qsTr("Redo this response?")
        description: qsTr("All following messages will be permanently erased.")
        onAccepted: currentChat.regenerateResponse(index)
    }

    RowLayout {
        id: buttonRow
        Layout.row: 4
        Layout.column: 1
        Layout.maximumWidth: parent.width
        Layout.fillWidth: false
        Layout.alignment: Qt.AlignLeft | Qt.AlignTop
        spacing: 3
        visible: !isCurrentResponse || !currentChat.responseInProgress
        enabled: opacity > 0
        opacity: hoverArea.hovered

        Behavior on opacity {
            OpacityAnimator { duration: 30 }
        }

        ChatMessageButton {
            readonly property var editingDisabledReason: {
                if (!currentChat.isModelLoaded)
                    return qsTr("Cannot edit chat without a loaded model.");
                if (currentChat.responseInProgress)
                    return qsTr("Cannot edit chat while the model is generating.");
                return null;
            }
            visible: !currentChat.isServer && model.name === "Prompt: "
            enabled: editingDisabledReason === null
            Layout.maximumWidth: 24
            Layout.maximumHeight: 24
            Layout.alignment: Qt.AlignVCenter
            Layout.fillWidth: false
            name: editingDisabledReason ?? qsTr("Edit")
            source: "qrc:/gpt4all/icons/edit.svg"
            onClicked: {
                if (inputBoxText === "")
                    editPromptDialog.open();
            }
        }

        ChatMessageButton {
            readonly property var editingDisabledReason: {
                if (!currentChat.isModelLoaded)
                    return qsTr("Cannot redo response without a loaded model.");
                if (currentChat.responseInProgress)
                    return qsTr("Cannot redo response while the model is generating.");
                return null;
            }
            visible: !currentChat.isServer && model.name === "Response: "
            enabled: editingDisabledReason === null
            Layout.maximumWidth: 24
            Layout.maximumHeight: 24
            Layout.alignment: Qt.AlignVCenter
            Layout.fillWidth: false
            name: editingDisabledReason ?? qsTr("Redo")
            source: "qrc:/gpt4all/icons/regenerate.svg"
            onClicked: redoResponseDialog.open()
        }

        ChatMessageButton {
            Layout.maximumWidth: 24
            Layout.maximumHeight: 24
            Layout.alignment: Qt.AlignVCenter
            Layout.fillWidth: false
            name: qsTr("Copy")
            source: "qrc:/gpt4all/icons/copy.svg"
            onClicked: {
                myTextArea.selectAll();
                myTextArea.copy();
                myTextArea.deselect();
            }
        }

        Item {
            visible: name === "Response: " && MySettings.networkIsActive
            Layout.alignment: Qt.AlignVCenter
            Layout.preferredWidth: childrenRect.width
            Layout.preferredHeight: childrenRect.height
            Layout.fillWidth: false

            ChatMessageButton {
                id: thumbsUp
                anchors.left: parent.left
                anchors.verticalCenter: parent.verticalCenter
                opacity: thumbsUpState || thumbsUpState == thumbsDownState ? 1.0 : 0.2
                source: "qrc:/gpt4all/icons/thumbs_up.svg"
                name: qsTr("Like response")
                onClicked: {
                    if (thumbsUpState && !thumbsDownState)
                        return

                    chatModel.updateNewResponse(index, "")
                    chatModel.updateThumbsUpState(index, true)
                    chatModel.updateThumbsDownState(index, false)
                    Network.sendConversation(currentChat.id, getConversationJson());
                }
            }

            ChatMessageButton {
                id: thumbsDown
                anchors.top: thumbsUp.top
                anchors.topMargin: buttonRow.spacing
                anchors.left: thumbsUp.right
                anchors.leftMargin: buttonRow.spacing
                checked: thumbsDownState
                opacity: thumbsDownState || thumbsUpState == thumbsDownState ? 1.0 : 0.2
                bgTransform: [
                    Matrix4x4 {
                        matrix: Qt.matrix4x4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
                    },
                    Translate {
                        x: thumbsDown.width
                    }
                ]
                source: "qrc:/gpt4all/icons/thumbs_down.svg"
                name: qsTr("Dislike response")
                onClicked: {
                    thumbsDownDialog.open()
                }
            }
        }
    }
} // GridLayout

} // Item

GridLayout {
    Layout.fillWidth: true
    Layout.maximumWidth: parent.width

    function shouldShowSuggestions() {
        if (!isCurrentResponse)
            return false;
        if (MySettings.suggestionMode === 2) // Off
            return false;
        if (MySettings.suggestionMode === 0 && consolidatedSources.length === 0) // LocalDocs only
            return false;
        return currentChat.responseState === Chat.GeneratingQuestions || currentChat.generatedQuestions.length !== 0;
    }

    Item {
        visible: parent.shouldShowSuggestions()
        Layout.row: 5
        Layout.column: 0
        Layout.topMargin: 20
        Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
        Layout.preferredWidth: 28
        Layout.preferredHeight: 28
        Image {
            id: stack
            sourceSize: Qt.size(28, 28)
            fillMode: Image.PreserveAspectFit
            mipmap: true
            visible: false
            source: "qrc:/gpt4all/icons/stack.svg"
        }

        ColorOverlay {
            anchors.fill: stack
            source: stack
            color: theme.conversationHeader
        }
    }

    Item {
        visible: parent.shouldShowSuggestions()
        Layout.row: 5
        Layout.column: 1
        Layout.topMargin: 20
        Layout.fillWidth: true
        Layout.preferredHeight: 38
        RowLayout {
            spacing: 5
            anchors.left: parent.left
            anchors.top: parent.top
            anchors.bottom: parent.bottom

            TextArea {
                text: qsTr("Suggested follow-ups")
                padding: 0
                font.pixelSize: theme.fontSizeLarger
                font.bold: true
                color: theme.conversationHeader
                enabled: false
                focus: false
                readOnly: true
            }
        }
    }

    ColumnLayout {
        visible: parent.shouldShowSuggestions()
        Layout.row: 6
        Layout.column: 1
        Layout.fillWidth: true
        Layout.minimumHeight: 1
        spacing: 10
        Repeater {
            model: currentChat.generatedQuestions
            TextArea {
                id: followUpText
                Layout.fillWidth: true
                Layout.alignment: Qt.AlignLeft
                rightPadding: 40
                topPadding: 10
                leftPadding: 20
                bottomPadding: 10
                text: modelData
                focus: false
                readOnly: true
                wrapMode: Text.WordWrap
                hoverEnabled: !currentChat.responseInProgress
                color: theme.textColor
                font.pixelSize: theme.fontSizeLarge
                background: Rectangle {
                    color: hovered ? theme.sourcesBackgroundHovered : theme.sourcesBackground
                    radius: 10
                }
                MouseArea {
                    id: maFollowUp
                    anchors.fill: parent
                    enabled: !currentChat.responseInProgress
                    onClicked: function() {
                        var chat = window.currentChat
                        var followup = modelData
                        chat.stopGenerating()
                        chat.newPromptResponsePair(followup)
                    }
                }
                Item {
                    anchors.right: parent.right
                    anchors.verticalCenter: parent.verticalCenter
                    width: 40
                    height: 40
                    visible: !currentChat.responseInProgress
                    Image {
                        id: plusImage
                        anchors.verticalCenter: parent.verticalCenter
                        sourceSize.width: 20
                        sourceSize.height: 20
                        mipmap: true
                        visible: false
                        source: "qrc:/gpt4all/icons/plus.svg"
                    }

                    ColorOverlay {
                        anchors.fill: plusImage
                        source: plusImage
                        color: theme.styledTextColor
                    }
                }
            }
        }

        Rectangle {
            Layout.fillWidth: true
            color: "transparent"
            radius: 10
            Layout.preferredHeight: currentChat.responseInProgress ? 40 : 0
            clip: true
            ColumnLayout {
                id: followUpLayout
                anchors.fill: parent
                Rectangle {
                    id: myRect1
                    Layout.preferredWidth: 0
                    Layout.minimumWidth: 0
                    Layout.maximumWidth: parent.width
                    height: 12
                    color: theme.sourcesBackgroundHovered
                }

                Rectangle {
                    id: myRect2
                    Layout.preferredWidth: 0
                    Layout.minimumWidth: 0
                    Layout.maximumWidth: parent.width
                    height: 12
                    color: theme.sourcesBackgroundHovered
                }

                SequentialAnimation {
                    id: followUpProgressAnimation
                    ParallelAnimation {
                        PropertyAnimation {
                            target: myRect1
                            property: "Layout.preferredWidth"
                            from: 0
                            to: followUpLayout.width
                            duration: 1000
                        }
                        PropertyAnimation {
                            target: myRect2
                            property: "Layout.preferredWidth"
                            from: 0
                            to: followUpLayout.width / 2
                            duration: 1000
                        }
                    }
                    SequentialAnimation {
                        loops: Animation.Infinite
                        ParallelAnimation {
                            PropertyAnimation {
                                target: myRect1
                                property: "opacity"
                                from: 1
                                to: 0.2
                                duration: 1500
                            }
                            PropertyAnimation {
                                target: myRect2
                                property: "opacity"
                                from: 1
                                to: 0.2
                                duration: 1500
                            }
                        }
                        ParallelAnimation {
                            PropertyAnimation {
                                target: myRect1
                                property: "opacity"
                                from: 0.2
                                to: 1
                                duration: 1500
                            }
                            PropertyAnimation {
                                target: myRect2
                                property: "opacity"
                                from: 0.2
                                to: 1
                                duration: 1500
                            }
                        }
                    }
                }

                onVisibleChanged: {
                    if (visible)
                        followUpProgressAnimation.start();
                }
            }

            Behavior on Layout.preferredHeight {
                NumberAnimation {
                    duration: 300
                    easing.type: Easing.InOutQuad
                }
            }
        }
    }

} // GridLayout

} // ColumnLayout
