/*******************************************************************************
* Copyright (c) 2013 "Filippo Scognamiglio"
* https://github.com/Swordfish90/cool-retro-term
*
* This file is part of cool-retro-term.
*
* cool-retro-term 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 <http://www.gnu.org/licenses/>.
*******************************************************************************/

import QtQuick 2.2
import QtQuick.Controls 1.0

import "utils.js" as Utils

QtObject{
    property string version: "1.0.0"

    // STATIC CONSTANTS ////////////////////////////////////////////////////////

    readonly property real minimumFontScaling: 0.25
    readonly property real maximumFontScaling: 2.50

    // GENERAL SETTINGS ///////////////////////////////////////////////////////

    property int x: 100
    property int y: 100
    property int width: 1024
    property int height: 768

    property bool fullscreen: false
    property bool showMenubar: true

    property real windowOpacity: 1.0
    property real ambientLight: 0.2
    property real contrast: 0.85
    property real brightness: 0.5

    property bool showTerminalSize: true
    property real windowScaling: 1.0

    property real fps: 24
    property bool verbose: false

    onWindowScalingChanged: handleFontChanged();

    // PROFILE SETTINGS ///////////////////////////////////////////////////////

    property bool useCustomCommand: false
    property string customCommand: ""

    property string _backgroundColor: "#000000"
    property string _fontColor: "#ff8100"
    property string saturatedColor: Utils.mix(Utils.strToColor("#FFFFFF"), Utils.strToColor(_fontColor), saturationColor * 0.5)
    property color fontColor: Utils.mix(Utils.strToColor(saturatedColor), Utils.strToColor(_backgroundColor), 0.7 + (contrast * 0.3))
    property color backgroundColor: Utils.mix(Utils.strToColor(_backgroundColor), Utils.strToColor(saturatedColor), 0.7 + (contrast * 0.3))

    property real staticNoise: 0.1
    property real screenCurvature: 0.1
    property real glowingLine: 0.2
    property real burnIn: 0.40
    property real bloom: 0.65

    property real bloomQuality: 0.5
    property real burnInQuality: 0.5

    property real chromaColor: 0.0
    property real saturationColor: 0.0

    property real jitter: 0.18

    property real horizontalSync: 0.08
    property real flickering: 0.1

    property real rbgShift: 0.0

    readonly property int no_rasterization: 0
    readonly property int scanline_rasterization: 1
    readonly property int pixel_rasterization: 2

    property int rasterization: no_rasterization

    // FONTS //////////////////////////////////////////////////////////////////

    property real fontScaling: 1.0
    property real fontWidth: 1.0

    property bool lowResolutionFont: false

    property var fontNames: ["TERMINUS_SCALED", "COMMODORE_PET", "COMMODORE_PET"]
    property var fontlist: fontManager.item.fontlist

    signal terminalFontChanged(string fontSource, int pixelSize, int lineSpacing, real screenScaling, real fontWidth)

    signal initializedSettings()

    property Loader fontManager: Loader{
        states: [
            State { when: rasterization == no_rasterization
                PropertyChanges {target: fontManager; source: "Fonts.qml" } },
            State { when: rasterization == scanline_rasterization
                PropertyChanges {target: fontManager; source: "FontScanlines.qml" } },
            State { when: rasterization == pixel_rasterization;
                PropertyChanges {target: fontManager; source: "FontPixels.qml" } }
        ]

        onLoaded: handleFontChanged()
    }

    onFontScalingChanged: handleFontChanged();
    onFontWidthChanged: handleFontChanged();

    function getIndexByName(name) {
        for (var i = 0; i < fontlist.count; i++) {
            if (name === fontlist.get(i).name)
                return i;
        }
        return 0; // If the font is not available default to 0.
    }

    function incrementScaling(){
        fontScaling = Math.min(fontScaling + 0.05, maximumFontScaling);
        handleFontChanged();
    }

    function decrementScaling(){
        fontScaling = Math.max(fontScaling - 0.05, minimumFontScaling);
        handleFontChanged();
    }

    function handleFontChanged(){
        if (!fontManager.item) return;

        var index = getIndexByName(fontNames[rasterization]);
        if (index === undefined) return;

        fontManager.item.selectedFontIndex = index;
        fontManager.item.scaling = fontScaling * windowScaling;

        var fontSource = fontManager.item.source;
        var pixelSize = fontManager.item.pixelSize;
        var lineSpacing = fontManager.item.lineSpacing;
        var screenScaling = fontManager.item.screenScaling;
        var fontWidth = fontManager.item.defaultFontWidth * appSettings.fontWidth;

        lowResolutionFont = fontManager.item.lowResolutionFont;

        terminalFontChanged(fontSource, pixelSize, lineSpacing, screenScaling, fontWidth);
    }

    // FRAMES /////////////////////////////////////////////////////////////////

    property ListModel framesList: ListModel{
        ListElement{
            name: "NO_FRAME"
            text: "No frame"
            source: ""
            reflections: false
        }
        ListElement{
            name: "SIMPLE_WHITE_FRAME"
            text: "Simple white frame"
            source: "./frames/WhiteSimpleFrame.qml"
            reflections: true
        }
        ListElement{
            name: "ROUGH_BLACK_FRAME"
            text: "Rough black frame"
            source: "./frames/BlackRoughFrame.qml"
            reflections: true
        }
    }

    function getFrameIndexByName(name) {
        for (var i = 0; i < framesList.count; i++) {
            if (name === framesList.get(i).name)
                return i;
        }
        return 0; // If the frame is not available default to 0.
    }

    property string frameSource: "./frames/WhiteSimpleFrame.qml"
    property string frameName: "SIMPLE_WHITE_FRAME"

    property bool _frameReflections: false
    property bool reflectionsAllowed: true
    property bool frameReflections: _frameReflections && reflectionsAllowed

    onFrameNameChanged: {
        var index = getFrameIndexByName(frameName);
        frameSource = framesList.get(index).source;
        reflectionsAllowed = framesList.get(index).reflections;
    }

    // DB STORAGE /////////////////////////////////////////////////////////////

    property Storage storage: Storage{ }

    function stringify(obj) {
        var replacer = function(key, val) {
            return val.toFixed ? Number(val.toFixed(4)) : val;
        }
        return JSON.stringify(obj, replacer, 2);
    }

    function composeSettingsString(){
        var settings = {
            fps: fps,
            x: x,
            y: y,
            width: width,
            height: height,
            windowScaling: windowScaling,
            showTerminalSize: showTerminalSize,
            fontScaling: fontScaling,
            fontNames: fontNames,
            frameReflections: _frameReflections,
            showMenubar: showMenubar,
            bloomQuality: bloomQuality,
            burnInQuality: burnInQuality
        }
        return stringify(settings);
    }

    function composeProfileObject(){
        var settings = {
            backgroundColor: _backgroundColor,
            fontColor: _fontColor,
            flickering: flickering,
            horizontalSync: horizontalSync,
            staticNoise: staticNoise,
            chromaColor: chromaColor,
            saturationColor: saturationColor,
            screenCurvature: screenCurvature,
            glowingLine: glowingLine,
            frameName: frameName,
            burnIn: burnIn,
            bloom: bloom,
            rasterization: rasterization,
            jitter: jitter,
            rbgShift: rbgShift,
            brightness: brightness,
            contrast: contrast,
            ambientLight: ambientLight,
            windowOpacity: windowOpacity,
            fontName: fontNames[rasterization],
            fontWidth: fontWidth,
            useCustomCommand: useCustomCommand,
            customCommand: customCommand
        }
        return settings;
    }

    function composeProfileString() {
        return stringify(composeProfileObject());
    }

    function loadSettings(){
        var settingsString = storage.getSetting("_CURRENT_SETTINGS");
        var profileString = storage.getSetting("_CURRENT_PROFILE");

        if(!settingsString) return;
        if(!profileString) return;

        loadSettingsString(settingsString);
        loadProfileString(profileString);

        if (verbose)
            console.log("Loading settings: " + settingsString + profileString);
    }

    function storeSettings(){
        var settingsString = composeSettingsString();
        var profileString = composeProfileString();

        storage.setSetting("_CURRENT_SETTINGS", settingsString);
        storage.setSetting("_CURRENT_PROFILE", profileString);

        if (verbose) {
            console.log("Storing settings: " + settingsString);
            console.log("Storing profile: " + profileString);
        }
    }

    function loadSettingsString(settingsString){
        var settings = JSON.parse(settingsString);

        showTerminalSize = settings.showTerminalSize !== undefined ? settings.showTerminalSize : showTerminalSize

        fps = settings.fps !== undefined ? settings.fps: fps
        windowScaling = settings.windowScaling !== undefined ? settings.windowScaling : windowScaling

        x = settings.x !== undefined ? settings.x : x
        y = settings.y !== undefined ? settings.y : y
        width = settings.width !== undefined ? settings.width : width
        height = settings.height !== undefined ? settings.height : height

        fontNames = settings.fontNames !== undefined ? settings.fontNames : fontNames
        fontScaling = settings.fontScaling !== undefined ? settings.fontScaling : fontScaling

        _frameReflections = settings.frameReflections !== undefined ? settings.frameReflections : _frameReflections;

        showMenubar = settings.showMenubar !== undefined ? settings.showMenubar : showMenubar;

        bloomQuality = settings.bloomQuality !== undefined ? settings.bloomQuality : bloomQuality;
        burnInQuality = settings.burnInQuality !== undefined ? settings.burnInQuality : burnInQuality;
    }

    function loadProfileString(profileString){
        var settings = JSON.parse(profileString);

        _backgroundColor = settings.backgroundColor !== undefined ? settings.backgroundColor : _backgroundColor;
        _fontColor = settings.fontColor !== undefined ? settings.fontColor : _fontColor;

        horizontalSync = settings.horizontalSync !== undefined ? settings.horizontalSync : horizontalSync
        flickering = settings.flickering !== undefined ? settings.flickering : flickering;
        staticNoise = settings.staticNoise !== undefined ? settings.staticNoise : staticNoise;
        chromaColor = settings.chromaColor !== undefined ? settings.chromaColor : chromaColor;
        saturationColor = settings.saturationColor !== undefined ? settings.saturationColor : saturationColor;
        screenCurvature = settings.screenCurvature !== undefined ? settings.screenCurvature : screenCurvature;
        glowingLine = settings.glowingLine !== undefined ? settings.glowingLine : glowingLine;

        burnIn = settings.burnIn !== undefined ? settings.burnIn : burnIn
        bloom = settings.bloom !== undefined ? settings.bloom : bloom

        frameName = settings.frameName !== undefined ? settings.frameName : frameName;

        rasterization = settings.rasterization !== undefined ? settings.rasterization : rasterization;

        jitter = settings.jitter !== undefined ? settings.jitter : jitter;

        rbgShift = settings.rbgShift !== undefined ? settings.rbgShift : rbgShift;

        ambientLight = settings.ambientLight !== undefined ? settings.ambientLight : ambientLight;
        contrast = settings.contrast !== undefined ? settings.contrast : contrast;
        brightness = settings.brightness !== undefined ? settings.brightness : brightness;
        windowOpacity = settings.windowOpacity !== undefined ? settings.windowOpacity : windowOpacity;

        fontNames[rasterization] = settings.fontName !== undefined ? settings.fontName : fontNames[rasterization];
        fontWidth = settings.fontWidth !== undefined ? settings.fontWidth : fontWidth;

        useCustomCommand = settings.useCustomCommand !== undefined ? settings.useCustomCommand : useCustomCommand
        customCommand = settings.customCommand !== undefined ? settings.customCommand : customCommand

        handleFontChanged();
    }

    function storeCustomProfiles(){
        storage.setSetting("_CUSTOM_PROFILES", composeCustomProfilesString());
    }

    function loadCustomProfiles(){
        var customProfileString = storage.getSetting("_CUSTOM_PROFILES");
        if(customProfileString === undefined) customProfileString = "[]";
        loadCustomProfilesString(customProfileString);
    }

    function loadCustomProfilesString(customProfilesString){
        var customProfiles = JSON.parse(customProfilesString);
        for (var i=0; i<customProfiles.length; i++) {
            var profile = customProfiles[i];

            if (verbose)
                console.log("Loading custom profile: " + stringify(profile));

            profilesList.append(profile);
        }
    }

    function composeCustomProfilesString(){
        var customProfiles = []
        for(var i=0; i<profilesList.count; i++){
            var profile = profilesList.get(i);
            if(profile.builtin) continue;
            customProfiles.push({text: profile.text, obj_string: profile.obj_string, builtin: false})
        }
        return stringify(customProfiles);
    }

    function loadProfile(index){
        var profile = profilesList.get(index);
        loadProfileString(profile.obj_string);
    }

    function appendCustomProfile(name, profileString) {
        profilesList.append({text: name, obj_string: profileString, builtin: false});
    }

    // PROFILES ///////////////////////////////////////////////////////////////

    property ListModel profilesList: ListModel{
        ListElement{
            text: "Default Amber"
            obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.65,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#ff8100","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.16,"jitter":0.18,"burnIn":0.4,"staticNoise":0.1,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}'
            builtin: true
        }
        ListElement{
            text: "Default Green"
            obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#0ccc68","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.16,"jitter":0.18,"burnIn":0.45,"staticNoise":0.1,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}'
            builtin: true
        }
        ListElement{
            text: "Default Scanlines"
            obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"COMMODORE_PET","fontColor":"#00ff5b","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.14,"jitter":0.11,"burnIn":0.4,"staticNoise":0.05,"rasterization":1,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}'
            builtin: true
        }
        ListElement{
            text: "Default Pixelated"
            obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0,"brightness":0.5,"flickering":0.2,"contrast":0.85,"fontName":"COMMODORE_PET","fontColor":"#ffffff","frameName":"ROUGH_BLACK_FRAME","glowingLine":0.2,"horizontalSync":0.2,"jitter":0,"burnIn":0.45,"staticNoise":0.19,"rasterization":2,"screenCurvature":0.05,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}'
            builtin: true
        }
        ListElement{
            text: "Apple ]["
            obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.5,"brightness":0.5,"flickering":0.2,"contrast":0.85,"fontName":"APPLE_II","fontColor":"#2fff91","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.22,"horizontalSync":0.16,"jitter":0.1,"burnIn":0.65,"staticNoise":0.08,"rasterization":1,"screenCurvature":0.18,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}'
            builtin: true
        }
        ListElement{
            text: "Vintage"
            obj_string: '{"ambientLight":0.5,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.9,"contrast":0.80,"fontName":"COMMODORE_PET","fontColor":"#00ff3e","frameName":"ROUGH_BLACK_FRAME","glowingLine":0.3,"horizontalSync":0.42,"jitter":0.4,"burnIn":0.75,"staticNoise":0.2,"rasterization":1,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}'
            builtin: true
        }
        ListElement{
            text: "IBM Dos"
            obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.07,"contrast":0.85,"fontName":"IBM_DOS","fontColor":"#ffffff","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.13,"horizontalSync":0,"jitter":0.16,"burnIn":0.3,"staticNoise":0.03,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":1,"saturationColor":0,"rbgShift":0.35,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}'
            builtin: true
        }
        ListElement{
            text: "IBM 3278"
            obj_string: '{"ambientLight":0.1,"backgroundColor":"#000000","bloom":0.15,"brightness":0.5,"flickering":0,"contrast":0.85,"fontName":"IBM_3278","fontColor":"#0ccc68","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0,"horizontalSync":0,"jitter":0,"burnIn":0.6,"staticNoise":0,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}'
            builtin: true
        }
        ListElement{
            text: "Transparent Green"
            obj_string: '{"ambientLight":0.2,"backgroundColor":"#000000","bloom":0.45,"brightness":0.5,"flickering":0.20,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#0ccc68","frameName":"NO_FRAME","glowingLine":0.16,"horizontalSync":0.1,"jitter":0.20,"burnIn":0.25,"staticNoise":0.20,"rasterization":0,"screenCurvature":0.05,"windowOpacity":0.60,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}'
            builtin: true
        }
    }

    function getProfileIndexByName(name) {
        for (var i = 0; i < profilesList.count; i++) {
            if(profilesList.get(i).text === name)
                return i;
        }
        return -1;
    }

    Component.onCompleted: {
        // Manage the arguments from the QML side.
        var args = Qt.application.arguments;
        if (args.indexOf("--verbose") !== -1) {
            verbose = true;
        }
        if (args.indexOf("--default-settings") === -1) {
            loadSettings();
        }

        loadCustomProfiles();

        var profileArgPosition = args.indexOf("--profile");
        if (profileArgPosition !== -1) {
            var profileIndex = getProfileIndexByName(args[profileArgPosition + 1]);
            if (profileIndex !== -1)
                loadProfile(profileIndex);
            else
                console.log("Warning: selected profile is not valid; ignoring it");
        }

        if (args.indexOf("--fullscreen") !== -1) {
            fullscreen = true;
            showMenubar = false;
        }

        initializedSettings();
    }
    Component.onDestruction: {
        storeSettings();
        storeCustomProfiles();
        //storage.dropSettings(); //DROPS THE SETTINGS!.. REMEMBER TO DISABLE ONCE ENABLED!!
    }

    // VARS ///////////////////////////////////////////////////////////////////

    property Label _sampleLabel: Label {
        text: "100%"
    }
    property real labelWidth: _sampleLabel.width
}