/**
 *  LogarithmPlotter - Create graphs with logarithm scales.
 *  Copyright (C) 2021  Ad5001
 * 
 *  This program 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.
 * 
 *  This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
 */

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Dialogs 1.3 as D
import eu.ad5001.LogarithmPlotter 1.0
import "../js/objects.js" as Objects
import "../js/objs/common.js" as ObjectsCommons
import "../js/historylib.js" as HistoryLib
import "../js/utils.js" as Utils
import "../js/mathlib.js" as MathLib

D.Dialog {
    id: objEditor
    property string objType: 'Point'
    property int objIndex: 0
    property var obj: Objects.currentObjects[objType][objIndex]
    property QtObject editingRow: QtObject{}
    property var objectLists
    title: `LogarithmPlotter`
    width: 300
    height: 400
    
    Label {
        id: dlgTitle
        anchors.left: parent.left
        anchors.top: parent.top
        verticalAlignment: TextInput.AlignVCenter
        text: `Edit properties of ${objEditor.objType} ${objEditor.obj.name}`
        font.pixelSize: 20
        color: sysPalette.windowText
    }
    
    Column {
        id: dlgProperties
        anchors.top: dlgTitle.bottom
        width: objEditor.width - 20
        spacing: 10
    
        TextSetting {
            id: nameProperty
            height: 30
            label: "Name"
            icon: "icons/settings/custom/label.svg"
            min: 1
            width: dlgProperties.width
            defValue: objEditor.obj.name
            onChanged: function(newValue) {
                var newName = Utils.parseName(newValue)
                if(newName != '' && objEditor.obj.name != newName) {
                    if(Objects.getObjectByName(newName) != null) {
                        console.log(Objects.getObjectByName(newName).name, newName)
                        newName = ObjectsCommons.getNewName(newName)
                    }
                    history.addToHistory(new HistoryLib.NameChanged(
                        objEditor.obj.name, objEditor.objType, newName
                    ))
                    Objects.currentObjects[objEditor.objType][objEditor.objIndex].name = newName
                    objEditor.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex]
                    objectListList.update()
                }
            }
        }
    
        ComboBoxSetting {
            id: labelContentProperty
            height: 30
            width: dlgProperties.width
            label: "Label content"
            model: ["null", "name", "name + value"]
            icon: "icons/settings/custom/label.svg"
            currentIndex: model.indexOf(objEditor.obj.labelContent)
            onActivated: function(newIndex) {
                if(model[newIndex] != objEditor.obj.labelContent) {
                    objEditor.obj.labelContent = model[newIndex]
                    objectListList.update()
                }
            }
        }
        
        // Dynamic properties
        Repeater {
            id: dlgCustomProperties
            
            Item {
                height: customPropComment.height + customPropText.height + customPropCheckBox.height + customPropCombo.height + customPropListDict.height
                width: dlgProperties.width
                property string label: Utils.camelCase2readable(modelData[0])
                
                // Item for comments
                Label {
                    id: customPropComment
                    width: parent.width
                    height: visible ? implicitHeight : 0
                    visible: modelData[0].startsWith('comment')
                    text: visible ? modelData[1].replace(/\{name\}/g, objEditor.obj.name) : ''
                    //color: sysPalette.windowText
                    wrapMode: Text.WordWrap
                }
                
                // Setting for text & number settings as well as domains & expressions
                TextSetting {
                    id: customPropText
                    height: visible ? 30 : 0
                    width: parent.width
                    label: parent.label
                    icon: `icons/settings/custom/${parent.label}.svg`
                    isDouble: modelData[1] == 'number'
                    visible: paramTypeIn(modelData[1], ['Expression', 'Domain', 'string', 'number'])
                    defValue: visible ? {
                        'Expression': () => Utils.simplifyExpression(objEditor.obj[modelData[0]].toEditableString()),
                        'Domain': () => objEditor.obj[modelData[0]].toString(),
                        'string': () => objEditor.obj[modelData[0]],
                        'number': () => objEditor.obj[modelData[0]]
                    }[modelData[1]]() : ""
                    onChanged: function(newValue) {
                        var newValue = {
                            'Expression': () => new MathLib.Expression(newValue),
                            'Domain': () => MathLib.parseDomain(newValue),
                            'string': () => newValue,
                            'number': () => parseFloat(newValue)
                        }[modelData[1]]()
                        // Ensuring old and new values are different to prevent useless adding to history.
                        if(objEditor.obj[modelData[0]] != newValue) {
                            history.addToHistory(new HistoryLib.EditedProperty(
                                objEditor.obj.name, objEditor.objType, modelData[0], 
                                objEditor.obj[modelData[0]], newValue
                            ))
                            objEditor.obj[modelData[0]] = newValue
                            Objects.currentObjects[objEditor.objType][objEditor.objIndex].update()
                            objectListList.update()
                        }
                    }
                }
                
                // Setting for boolean
                CheckBox {
                    id: customPropCheckBox
                    visible: modelData[1] == 'boolean'
                    height: visible ? 20 : 0
                    width: parent.width
                    text: parent.label
                    icon: visible ? `icons/settings/custom/${parent.label}.svg` : ''
                    
                    checked: visible ? objEditor.obj[modelData[0]] : false
                    onClicked: {
                        history.addToHistory(new HistoryLib.EditedProperty(
                            objEditor.obj.name, objEditor.objType, modelData[0], 
                            objEditor.obj[modelData[0]], this.checked
                        ))
                        objEditor.obj[modelData[0]] = this.checked
                        Objects.currentObjects[objEditor.objType][objEditor.objIndex].update()
                        objectListList.update()
                    }
                }
                
                // Setting when selecting data from an enum, or an object of a certain type.
                ComboBoxSetting {
                    id: customPropCombo
                    width: dlgProperties.width
                    height: visible ? 30 : 0
                    label: parent.label
                    icon: visible ? `icons/settings/custom/${parent.label}.svg` : ''
                    // True to select an object of type, false for enums.
                    property bool selectObjMode: paramTypeIn(modelData[1], ['ObjectType'])
                    
                    model: visible ? 
                        (selectObjMode ? Objects.getObjectsName(modelData[1].objType).concat(['+ Create new ' + modelData[1].objType]) : modelData[1].values) 
                        : []
                    visible: paramTypeIn(modelData[1], ['ObjectType', 'Enum'])
                    currentIndex: model.indexOf(selectObjMode ? objEditor.obj[modelData[0]].name : objEditor.obj[modelData[0]])

                    onActivated: function(newIndex) {
                        if(selectObjMode) {
                            // This is only done when what we're selecting are Objects.
                            // Setting object property.
                            var selectedObj = Objects.getObjectByName(model[newIndex], modelData[1].objType)
                            if(selectedObj == null) {
                                // Creating new object.
                                selectedObj = Objects.createNewRegisteredObject(modelData[1].objType)
                                history.addToHistory(new HistoryLib.CreateNewObject(selectedObj.name, modelData[1].objType, selectedObj.export()))
                                model = Objects.getObjectsName(modelData[1].objType).concat(['+ Create new ' + modelData[1].objType])
                                currentIndex = model.indexOf(selectedObj.name)
                            }
                            //Objects.currentObjects[objEditor.objType][objEditor.objIndex].requiredBy = objEditor.obj[modelData[0]].filter((obj) => objEditor.obj.name != obj.name)
                            objEditor.obj.requiredBy = objEditor.obj.requiredBy.filter((obj) => objEditor.obj.name != obj.name)
                            selectedObj.requiredBy.push(Objects.currentObjects[objEditor.objType][objEditor.objIndex])
                            history.addToHistory(new HistoryLib.EditedProperty(
                                objEditor.obj.name, objEditor.objType, modelData[0], 
                                objEditor.obj[modelData[0]], selectedObj
                            ))
                            objEditor.obj[modelData[0]] = selectedObj
                        } else if(model[newIndex] != objEditor.obj[modelData[0]]) { 
                            // Ensuring new property is different to not add useless history entries.
                            history.addToHistory(new HistoryLib.EditedProperty(
                                objEditor.obj.name, objEditor.objType, modelData[0], 
                                objEditor.obj[modelData[0]], model[newIndex]
                            ))
                            objEditor.obj[modelData[0]] = model[newIndex]
                        }
                        // Refreshing
                        Objects.currentObjects[objEditor.objType][objEditor.objIndex].update()
                        objectListList.update()
                    }
                }
                
                // Setting to edit lists or dictionaries (e.g sequences & repartition function values)
                ListSetting {
                    id: customPropListDict
                    width: parent.width
                    height: visible ? implicitHeight : 0
                    
                    visible: paramTypeIn(modelData[1], ['List', 'Dict'])
                    label: parent.label
                    //icon: `icons/settings/custom/${parent.label}.svg`
                    dictionaryMode: paramTypeIn(modelData[1], ['Dict'])
                    keyType: dictionaryMode ? modelData[1].keyType : 'string'
                    valueType: visible ? modelData[1].valueType : 'string'
                    preKeyLabel: visible ? (dictionaryMode ? modelData[1].preKeyLabel : modelData[1].label).replace(/\{name\}/g, objEditor.obj.name) : ''
                    postKeyLabel: visible ? (dictionaryMode ? modelData[1].postKeyLabel : '').replace(/\{name\}/g, objEditor.obj.name) : ''
                    keyRegexp: dictionaryMode ? modelData[1].keyFormat : /^.+$/
                    valueRegexp: visible ? modelData[1].format : /^.+$/
                    forbidAdding: visible ? modelData[1].forbidAdding : false
                    
                    onChanged: {
                        var exported = exportModel()
                        history.addToHistory(new HistoryLib.EditedProperty(
                            objEditor.obj.name, objEditor.objType, modelData[0], 
                            objEditor.obj[modelData[0]], exported
                        ))
                        //Objects.currentObjects[objEditor.objType][objEditor.objIndex][modelData[0]] = exported
                        objEditor.obj[modelData[0]] = exported
                        //Objects.currentObjects[objEditor.objType][objEditor.objIndex].update()
                        objEditor.obj.update()
                        objectListList.update()
                    }
                    
                    Component.onCompleted: {
                        if(visible) importModel(objEditor.obj[modelData[0]])
                    }
                }
            }
        }
    }
    
    function show() {
        dlgCustomProperties.model = [] // Reset
        var objProps = Objects.types[objEditor.objType].properties()
        dlgCustomProperties.model = Object.keys(objProps).map(prop => [prop, objProps[prop]]) // Converted to 2-dimentional array.
        objEditor.open()
    }
}
