//=============================================================================
// RPG Maker MZ - Real World Time  
//=============================================================================

/*:
 * @target MZ
 * @plugindesc Stores the current system time and date in game variables in real-time and optionally displays a customizable HUD.
 * @author exotickiui
 * @url https://exotickiui.itch.io/rpg-maker-mz-real-world-time
 * 
 * @help
 * Version: 1.1
 * 
 * This plugin updates the specified game variables in real-time with the
 * current system time and date. Additionally, a HUD can be displayed on 
 * the map to show the current time, date, and day name. The HUD format, 
 * rows, position, and offsets are fully customizable.
 * 
 * The HUD can be toggled via plugin command.
 * 
 * ============================================================================
 * How to use
 * ============================================================================
 * 
 * === Plugin Parameter ===
 * 
 * == General ==
 * In this section, you can edit the variables that are used to store the 
 * current date and time information.
 * 
 * == HUD ==
 * In this section, you can change the appearance of the HUD.
 * 
 * HUD Format: Changes what is displayed in the HUD
 * HUD Position: Changes the position of the HUD
 * HUD Offset X: The HUD x offset from the window border
 * HUD Offset Y: The HUD y offset from the window border
 * HUD Opacity: Changes the opacity of the HUD. 0 is transparent, 255 is 
 * full opacity. 
 * 
 * == Vocabulary ==
 * In this section you can change the name of the months and days that 
 * are displayed.
 * 
 * === Customize the HUD format ===
 * 
 * The HUD format specifies both what is displayed in the HUD and how many 
 * rows the HUD should have.
 * You can add a new entry in the “HUD format” parameter for each new row. 
 * 
 * Example 1
 * 1. {time}
 * 2. {date}
 * would create a HUD with two rows, displaying the full time in the
 * first row and the full date in the second row.
 * 
 * Example 2
 * 1. {dayName}
 * 2. {hour}:{minutes}
 * 3. {month}.​{day}.{year}
 * would create a HUD with three rows​, displaying the day name on 
 * the first row, the time with only the hour and minutes on the 
 * second row and the date with a number instead of the full month name. 
 * 
 * Available shortcodes:
 * • {time}: Shows the time in the format hour:minutes:seconds
 * • {date}: Shows the date in the format day full month, year
 * 
 * • {dayName}: name of the day
 * • {monthName}: name of the month
 * • {day}: day as a number
 * • {month}: month as a number
 * • {year}: current year
 * • {hour}: current hour
 * • {seconds}: current seconds
 * • {minutes}: current minutes
 * 
 * 
 * ============================================================================
 * Plugin Commands
 * ============================================================================
 *
 * Toggle HUD display: Toggle if the HUD should be displayed or not.
 * 
 * Time format: Change the format to 12h or 24h. Please be aware 
 * that this will also change the option in the options menu.
 * 
 * ============================================================================
 * Version history
 * ============================================================================
 * 
 * 1.0
 * • Release
 * 
 * 1.1
 * • Added an option for 12h formats
 * 
 * @end
 * 
 * =============================================================================
 * Plugin Parameters
 * =============================================================================
 * @param === General ===
 * @default
 *
 * @param hourVariable
 * @parent === General ===
 * @text Hour Variable
 * @type variable
 * @desc The Game Variable where the current hour will be stored.
 * @default 1
 *
 * @param minuteVariable
 * @parent === General ===
 * @text Minute Variable
 * @type variable
 * @desc The Game Variable where the current minute will be stored.
 * @default 2
 *
 * @param secondVariable
 * @parent === General ===
 * @text Second Variable
 * @type variable
 * @desc The Game Variable where the current second will be stored.
 * @default 3
 *
 * @param dayVariable
 * @parent === General ===
 * @text Day Variable
 * @type variable
 * @desc The Game Variable where the current day will be stored.
 * @default 4
 *
 * @param monthVariable
 * @parent === General ===
 * @text Month Variable
 * @type variable
 * @desc The Game Variable where the current month will be stored.
 * @default 5
 *
 * @param yearVariable
 * @parent === General ===
 * @text Year Variable
 * @type variable
 * @desc The Game Variable where the current year will be stored.
 * @default 6
 *
 * @param monthNameVariable
 * @parent === General ===
 * @text Month Name Variable
 * @type variable
 * @desc The Game Variable where the name of the current month will be stored.
 * @default 7
 *
 * @param dayNameVariable
 * @parent === General ===
 * @text Day Name Variable
 * @type variable
 * @desc The Game Variable where the name of the current day will be stored.
 * @default 8
 *
 * @param === HUD ===
 * @default
 *
 * @param hudFormat
 * @parent === HUD ===
 * @text HUD Format
 * @type text[]
 * @desc Customize the text displayed in the HUD. Use placeholders like {time}, {date}, {dayName}.
 * @default ["{time}", "{date}"]
 *
 * @param hudPosition
 * @parent === HUD ===
 * @text HUD Position
 * @type select
 * @option Top Left
 * @value topleft
 * @option Top Right
 * @value topright
 * @option Bottom Left
 * @value bottomleft
 * @option Bottom Right
 * @value bottomright
 * @desc Position of the HUD on the screen.
 * @default topleft
 *
 * @param hudOffsetX
 * @parent === HUD ===
 * @text HUD Offset X
 * @type number
 * @desc Horizontal offset for the HUD position.
 * @default 0
 *
 * @param hudOffsetY
 * @parent === HUD ===
 * @text HUD Offset Y
 * @type number
 * @desc Vertical offset for the HUD position.
 * @default 0
 *
 * @param hudOpacity
 * @parent === HUD ===
 * @text HUD Opacity
 * @type number
 * @max 255
 * @desc Opacity of the HUD
 * @default 200
 * 
 * @param hudInOptions
 * @text Time format in options
 * @parent  === HUD ===
 * @type boolean
 * @on Yes
 * @off No
 * @default true
 * @desc Give the player the option to change the format of the time from 24h to am/pm in the options menu.
 * 
 * @param === Vocabulary ===
 * @default
 *
 * @param Month Names
 * @parent === Vocabulary ===
 * @type text[]
 * @desc Enter month names here.
 * @default ["January","February","March","April","May","June","July","August","September","October","November","December"]
 * 
 * @param Day Names
 * @parent === Vocabulary ===
 * @type text[]
 * @desc Enter Day names here (a week starts at sunday).
 * @default ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]
 * 
 * @param OptionsText
 * @text Options menu text
 * @parent === Vocabulary ===
 * @desc Name of the text in the options menu.
 * @type text
 * @default AM/PM time format
 * @end
 *
 * =============================================================================
 * Plugin Commands
 * =============================================================================
 * 
 * @command toggleHUD
 * @text Toggle HUD display
 * @desc Toggle if the HUD should be displayed or not.
 * 
 * @arg Show HUD
 * @text HUD display options
 * @type boolean
 * @on show
 * @off hide
 * @default true
 * @desc Show or hide the hud
 * 
 * @command hudTimeFormat
 * @text Time format
 * @desc Change the time format to 12h or 24h.
 * 
 * @arg Time format
 * @text 
 * @type boolean
 * @on 12h
 * @off 24h
 * @default true
 * @desc 
 */

(() => {

    //=============================================================================
    // Plugin Parameters
    //=============================================================================

    const pluginName = "Real_World_Time";

    var parameters = PluginManager.parameters(pluginName);
    var hourVariable = Number(parameters["hourVariable"] || 1);
    var minuteVariable = Number(parameters["minuteVariable"] || 2);
    var secondVariable = Number(parameters["secondVariable"] || 3);
    var dayVariable = Number(parameters["dayVariable"] || 4);
    var monthVariable = Number(parameters["monthVariable"] || 5);
    var yearVariable = Number(parameters["yearVariable"] || 6);
    var monthNameVariable = Number(parameters["monthNameVariable"] || 7);
    var dayNameVariable = Number(parameters["dayNameVariable"] || 8);
    var hudFormat = JSON.parse(parameters["hudFormat"] || '["{time}", "{date}"]');
    var hudPosition = String(parameters["hudPosition"] || "topleft");
    var hudOffsetX = Number(parameters["hudOffsetX"] || 0);
    var hudOffsetY = Number(parameters["hudOffsetY"] || 0);
    var hudOpacity = Number(parameters["hudOpacity"] || 195);
    var monthNames = JSON.parse(parameters["Month Names"]) || ["January","February","March","April","May","June","July","August","September","October","November","December"];
    var dayNames = JSON.parse(parameters["Day Names"]) || ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
    var optionsText = String(parameters["OptionsText"]);
    var optionsMenu =  parameters["hudInOptions"] != 'false';
    var showHUD = false;

    //=============================================================================
    // time update
    //=============================================================================

    function kiui_update_time() {
        var now = new Date();
        var hours = now.getHours();
        var minutes = now.getMinutes();
        var seconds = now.getSeconds();
        var day = now.getDate();
        var month = now.getMonth() + 1;
        var year = now.getFullYear();
        var monthName = monthNames[now.getMonth()];
        var dayName = dayNames[now.getDay()];

        if ($gameVariables) {
            $gameVariables.setValue(hourVariable, hours);
            $gameVariables.setValue(minuteVariable, minutes);
            $gameVariables.setValue(secondVariable, seconds);
            $gameVariables.setValue(dayVariable, day);
            $gameVariables.setValue(monthVariable, month);
            $gameVariables.setValue(yearVariable, year);
            $gameVariables.setValue(monthNameVariable, monthName);
            $gameVariables.setValue(dayNameVariable, dayName);
        }
    }

    const _Scene_Map_update = Scene_Map.prototype.update;
    Scene_Map.prototype.update = function() {
        _Scene_Map_update.call(this);
        kiui_update_time()
    };

    //=============================================================================
    // HUD
    //=============================================================================

    const _Scene_Map_createAllWindows = Scene_Map.prototype.createAllWindows;
    Scene_Map.prototype.createAllWindows = function() {
        _Scene_Map_createAllWindows.call(this);
        this._timeHudWindow = new Window_TimeHUD();
        this.addWindow(this._timeHudWindow);
    };

    class Window_TimeHUD extends Window_Base {
        constructor() {
            super(new Rectangle(0, 0, Graphics.width, Graphics.height));
            this.opacity = hudOpacity;
            this.refresh();
        }

        refresh() {
            this.contents.clear();
            if (!showHUD) {
                this.hide();
                return;
            }
            this.show();

            var now = new Date();
            var hours = now.getHours().toString().padStart(2, '0');
            var minutes = now.getMinutes().toString().padStart(2, '0');
            var seconds = now.getSeconds().toString().padStart(2, '0');
            var day = now.getDate();
            var monthName = monthNames[now.getMonth()];
            var year = now.getFullYear();
            var dayName = dayNames[now.getDay()];
            var month = now.getMonth() + 1;

            const period = hours >= 12 ? 'PM' : 'AM';
            if ($gameSystem.isAmPm()) {
                hours = (hours % 12 || 12).toString().padStart(2, '0');
            }

            var timeString = `${hours}:${minutes}:${seconds}`;
            var dateString = `${monthName} ${day}, ${year}`;

            const placeholders = {
                "{time}": timeString,
                "{date}": dateString,
                "{dayName}": dayName,
                "{monthName}": monthName,
                "{hour}": hours,
                "{minutes}": minutes,
                "{seconds}": seconds,
                "{day}": day,
                "{year}": year,
                "{month}": month
            };
            const content = hudFormat.slice(0, hudFormat.length).map(row => {
                if (!$gameSystem.isAmPm()) {
                    for (const placeholder in placeholders) {
                        row = row.replace(placeholder, placeholders[placeholder]);
                    }
                    return row;
                } else {
                    var rowAMPM = false;
                    for (const placeholder in placeholders) {
                        if (row.includes("{time}") || row.includes("{hour}")) rowAMPM = true;
                        row = row.replace(placeholder, placeholders[placeholder]);
                    }
                    if (rowAMPM) {
                        return row + " " + period;
                    } else {
                        return row;
                    }
                }     
            });

            const lineHeight = this.lineHeight();
            const width = Math.max(...content.map(line => this.textWidth(line))) + this.padding * 2;
            const height = content.length * lineHeight + this.padding * 2;

            this.move(this.calculateX(width), this.calculateY(height), width, height);
            this.createContents();

            content.forEach((line, index) => {
                this.drawText(line, 0, index * lineHeight, width, "left");
            });
        }

        calculateX(width) {
            let x = hudOffsetX;
            switch (hudPosition) {
                case "topright":
                case "bottomright":
                    x = Graphics.width - width - hudOffsetX;
                    break;
                case "topleft":
                case "bottomleft":
                default:
                    break;
            }
            return x;
        }

        calculateY(height) {
            let y = hudOffsetY;
            switch (hudPosition) {
                case "bottomleft":
                case "bottomright":
                    y = Graphics.height - height - hudOffsetY;
                    break;
                case "topleft":
                case "topright":
                default:
                    break;
            }
            return y;
        }

        update() {
            super.update();
            this.refresh();
        }
    }

    //=============================================================================
    // Options
    //=============================================================================

    
    const configTimeFormatKey = "timeFormat";
    ConfigManager[configTimeFormatKey] = false;

    const _ConfigManager_makeData = ConfigManager.makeData;
    ConfigManager.makeData = function() {
        const config = _ConfigManager_makeData.call(this);
        config[configTimeFormatKey] = this[configTimeFormatKey];
        return config;
    };

    const _ConfigManager_applyData = ConfigManager.applyData;
    ConfigManager.applyData = function(config) {
        _ConfigManager_applyData.call(this, config);
        this[configTimeFormatKey] = this.readFlag(config, configTimeFormatKey, defaultTimeFormat24h);
    };

    if (optionsMenu) {
        const _Window_Options_addGeneralOptions = Window_Options.prototype.addGeneralOptions;
        Window_Options.prototype.addGeneralOptions = function() {
            _Window_Options_addGeneralOptions.call(this);
            this.addCommand(optionsText, configTimeFormatKey);
        };
    }

    Game_System.prototype.isAmPm = function() {
        return ConfigManager[configTimeFormatKey];
    };
    

    //=============================================================================
    // Plugin Commands
    //=============================================================================

    PluginManager.registerCommand(pluginName, "toggleHUD", args => {
        JSON.parse(args["Show HUD"]) ? showHUD = true :  showHUD = false;
    });

    PluginManager.registerCommand(pluginName, "hudTimeFormat", args => {
        JSON.parse(args["Time format"]) ? ConfigManager[configTimeFormatKey] = true :  ConfigManager[configTimeFormatKey]= false;
    });

})();
