//@ts-check
////////////////////////////////////////////////////////////////////////////////////////
/*:
 * @plugindesc Provides features for novel games
 * @author siguren
 * 
 * @target MZ
 * @orderAfter MOG_TitleSplashScreen
 * 
 * @url https://store.steampowered.com/app/2214302/RPGMZ/
 * 
 * @command Show
 * @text Open Backlog
 * 
 * @command ClearAll
 * @text Clear all log
 * @desc Clear all saved log
 * 
 * 
 * @command GetLogSize
 * @text Obtain the current log size
 * @arg variableId
 * @text Variable to record the outcome
 * @type variable
 * @default 0
 * 
 * @command SetAutoMode
 * @text Auto Mode Settings
 * @desc Turn auto setting ON/OFF from event commands.
 * @arg automode
 * @type boolean
 * @default true
 * 
 * @command GetAutoMode
 * @text Obtain Auto Mode Status
 * @desc Obtain the current Auto Setting Status.
 * @arg swtichId
 * @type switch
 * @default 0
 * 
 * @command HasLastVoice
 * @text Obtain the most recently played voice.
 * @desc Check if voice playback button is enabled.
 * @arg switchId
 * @default 0
 * 
 * 
 * @command GetAutoWaitTime
 * @text Get Auto Wait Time
 * @arg variableId
 * @type variable
 * @default 0
 * 
 * @command SetAutoWaitTime
 * @text Set Auto Wait Time
 * @arg variableId
 * @type variable
 * @default 0
 * 
 * @command SetLogLimitSizeBasic
 * @text Set Log storage capacity
 * @arg variableId
 * @type variable
 * @default 0
 * 
 * @command GetLogLimitSizeBasic
 * @text Obtain Log storage capacity
 * @arg variableId
 * @type variable
 * @default 0
 * 
 * @command SetLogLimitSizeExtends
 * @text Set Log resevation capacity
 * @arg variableId
 * @type variable
 * @default 0
 * 
 * @command GetLogLimitSizeExtends
 * @text Obtain Log reservation capacity
 * @desc The option status will be obtained.
 * @arg variableId
 * @type variable
 * @default 0
 * 
 * @command GetKeepingAutoMode
 * @text Obtain auto-retention setting
 * @type switch
 * @default 0
 * 
 * @command GetMachinakaLines
 * @text Obtain the number of simultaneous displays in NPC convo Mode
 * @arg variableId
 * @type variable
 * @default 0
 * 
 * @command SetMachinakaLines
 * @text Set the number for simultaneous displays in NPC convo Mode
 * @arg variableId
 * @type variable
 * @default 0
 * 
 * 
 * @command ShowConsloeProxy
 * @text Display data on console for log display
 * 
 * @param menuCommand
 * @text Menu Command
 * @type struct<MultiLangString>
 * @desc Add Open Log command to the menu.
 * If the command name is blank, it will not be added.
 * @default {"ja_JP":"","en_US":"","zh_CN":"","ko_KR":"","de_DE":"","fr_FR":"","ru_RU":""}
 * 
 * @param voiceFileRegex
 * @text Voice File Regular Expression
 * @desc  Recognizes file names matched by Regular Expression as voice.
 * It is internally processed with ["^"+"this string "+". +""]
 * @type string
 * @default voice
 * 
 * @param voiceFileList
 * @text Voice File List
 * @type file[]
 * @dir audio/se/
 * @desc Treats the specified file as a voice file.
 * Used for handling files that cannot be handled by Regular Expression. 
 * @default []
 * 
 * @param voice
 * @text Voice Settings
 * @desc Associates [Show Text] and [Play SE]
 * Saves the matched file as voice in the message log.
 * @type struct<VoiceEN>
 * @default {"regex":"voice","files":"[]"}
 * 
 * @param buttonSetting
 * @text General Button Settings
 * @type struct<ButtonSettingEN>
 * @default {"offsetX":"0","offsetY":"0","windowOverlap":"outside","windowStretchY":"0"}
 * 
 * @param backlogButton
 * @text Log Display Button
 * @type struct<Button>
 * @default {"x":"0","y":"0","image":"MessageButton","enabledSwitch":"0","pressedSound":"{\"name\":\"Book1\",\"volume\":\"90\",\"pitch\":\"100\",\"pan\":\"0\"}","hot":"{\"x\":\"0\",\"y\":\"256\",\"width\":\"64\",\"height\":\"64\"}","cold":"{\"x\":\"64\",\"y\":\"256\",\"width\":\"64\",\"height\":\"64\"}"}
 * @parent buttonSetting
 * 
 * @param saveButton
 * @text Save Button
 * @type struct<Button>
 * @default {"x":"64","y":"0","image":"MessageButton","enabledSwitch":"0","pressedSound":"{\"name\":\"Book2\",\"volume\":\"90\",\"pitch\":\"100\",\"pan\":\"0\"}","hot":"{\"x\":\"0\",\"y\":\"128\",\"width\":\"64\",\"height\":\"64\"}","cold":"{\"x\":\"64\",\"y\":\"128\",\"width\":\"64\",\"height\":\"64\"}"}
 * @parent buttonSetting
 * 
 * @param loadButton
 * @text Load Button
 * @type struct<Button>
 * @default {"x":"128","y":"0","image":"MessageButton","enabledSwitch":"0","pressedSound":"{\"name\":\"Book2\",\"volume\":\"90\",\"pitch\":\"100\",\"pan\":\"0\"}","hot":"{\"x\":\"0\",\"y\":\"192\",\"width\":\"64\",\"height\":\"64\"}","cold":"{\"x\":\"64\",\"y\":\"192\",\"width\":\"64\",\"height\":\"64\"}"}
 * @parent buttonSetting
 * 
 * @param autoButton
 * @text Auto Button
 * @desc In auto mode,
 * the button will remain pressed until cancellation.
 * @type struct<Button>
 * @default {"x":"196","y":"0","image":"MessageButton","enabledSwitch":"0","pressedSound":"{\"name\":\"Key\",\"volume\":\"90\",\"pitch\":\"100\",\"pan\":\"0\"}","hot":"{\"x\":\"0\",\"y\":\"0\",\"width\":\"64\",\"height\":\"64\"}","cold":"{\"x\":\"64\",\"y\":\"0\",\"width\":\"64\",\"height\":\"64\"}"}
 * @parent buttonSetting
 * 
 * @param autoGauge
 * @text Auto Gauge
 * @type struct<AutoGauge>
 * @default {"gaugeType":"circle","x":"0","y":"0","width":"10","texture":"","color":"FF0000"}
 * @parent autoButton

 * @param skipButton
 * @text 既読スキップボタン
 * @desc 押された際に指定スイッチをONにします。
 * 既読スキップの処理はManoUZ_Kidokuを使います。
 * @type struct<Button>
 * @default {"x":"320","y":"0","image":"MessageButton","hot":"{\"x\":\"0\",\"y\":\"64\",\"width\":\"64\",\"height\":\"64\"}","cold":"{\"x\":\"64\",\"y\":\"64\",\"width\":\"64\",\"height\":\"64\"}","enabledSwitch":"0","pressedSound":"{\"name\":\"Key\",\"volume\":\"90\",\"pitch\":\"100\",\"pan\":\"0\"}"}
 * @parent buttonSetting
 * 
 * 
 * @param voiceRepeatButton
 * @text Voice Repeat Button
 * @desc Button that replays the most recently played voice.
 * @type struct<Button>
 * @default {"x":"256","y":"0","image":"MessageButton","enabledSwitch":"0","pressedSound":"","hot":"{\"x\":\"0\",\"y\":\"320\",\"width\":\"64\",\"height\":\"64\"}","cold":"{\"x\":\"64\",\"y\":\"320\",\"width\":\"64\",\"height\":\"64\"}"}
 * @parent buttonSetting
 * 
 * @param logWriteSwitch
 * @text Log Writing Enabling Switch
 * @desc Log is written only when the specified switch is ON.
 * If not specified, always writes the log.
 * @type switch
 * @default 0
 * 
 * @param logWriteOnBattle
 * @text Log Writing during Battle
 * @desc The log is not written during battle when OFF.
 * @type boolean
 * @default false
 * 
 * @param logStyle
 * @type struct<LogStyleEN>
 * @text Log Screen Style
 * @desc Log Screen and Options placement settings
 * @default {"windowFileName":"Window","background":"","speakerName":"【%1】","choiceFormat":"【%1】：%2","choiceText":"{\"ja_JP\":\"選択肢\",\"en_US\":\"choices\",\"zh_CN\":\"??\",\"ko_KR\":\"??\",\"de_DE\":\"Auswahl\",\"fr_FR\":\"Les choix\",\"ru_RU\":\"Выбор\"}","voiceIconIndex":"4"}
 * 
 * @param logLimitBasic
 * @text Basic number of logs saved
 * @desc The number of logs removed when cleaning out old logs should be less than or equal to the number specified here.
 * @type number
 * @default 40
 * 
 * @param logLimitExtends
 * @text Number of additional logs saved
 * @desc When the total number of logs stored exceeds the basic + additional limit,the old logs are discarded.
 * @type number
 * @default 20
 * 
 * @param machinakaModeSwitch
 * @text NPC convo Mode Control Switch
 * @desc Enables the function only while the specified switch is ON.
 * @type switch
 * @default 0
 * 
 * @param macinakaModeDefault
 * @text Initial Value for NPC convo Mode
 * @type boolean
 * @default true
 * @parent macinakaMode
 * 
 * @param machinakaModeLines
 * @text Number of lines in NPC convo Mode
 * @type number
 * @default 3
 * 
 * @param choiceFormat
 * @text Choice Display Format
 * @type string
 * @desc [Choices] displayed in %1,Item Strings are displayed in %2
 * @default 【%1】：%2
 * @parent logStyle
 * 
 * @param choiceText
 * @text choices
 * @type struct<MultiLangString>
 * @default {"ja_JP":"選択肢","en_US":"choices","zh_CN":"??","ko_KR":"??","de_DE":"Auswahl","fr_FR":"Les choix","ru_RU":"Выбор"}
 * @desc Display name of the choice.
 * Multilingual Support Availabe.
 * @parent logStyle
 * 
 * @param choiceCancelText
 * @text Text when choice is cancelled.
 * @type struct<MultiLangString>
 * @default {"ja_JP":"キャンセル","en_US":"Cancel","zh_CN":"","ko_KR":"","de_DE":"","fr_FR":"","ru_RU":""}
 * @parent logStyle
 * 
 * @help
 * Provides the following functions
 * ■Saving and displaying message logs
 * ■Quick save during message display/load execution
 * ■Voice recording function
 * 
 * Log the text displayed by [Show Text]
 * 
 * ■Button Assignment
 * When the following are entered during the display of a message, 
 * scene switching is performed.
 * Internally recognized by longPressed, thus holding down button is necessary.
 * ・save:pageup
 * ・load:pagedown
 * ・log:shift
 * 
 * ■Button Placement
 * The relative position of the buttons determines their placement.
 * The button area rectangle can accommodate all buttons.
 * The right side of this rectangle is positioned to align with the right side of the window. 
 * This position will be used to move offsetX and offsetY.
 * 
 * ■Quick Save
 * Open Save Screen to save while message is displayed.
 * Error may occur if saving is performed while
 * triggered events are being executed in parallel.
 * 
 * ■When Quick Save is loaded
 * Rewinds the event execution state to restore the message correctly.
 * When used with plugins that control variables based on text,
 * an error may occur.
 * 
 * ■When Log data is lost
 * Log data is initialized if the plugin version is different.
 * Even in this case, saved data other than logs can be loaded.
 * 
 * ■Voice Recording Function
 * If [Play SE] is executed before a text is displayed, it will be recorded as a voice.
 * 
 * A method of identification is provided so that sound effects are not recorded.
 * SE are placed inside audio/se/, but 
 * the subfolder within audio/se/ determines whether to register.
 * *Subfolders are available in RPG Maker MZ ver1.3.1 or later.
 * The following files are supported.
 * *The following settings are for when the parameter
 *  [Voice Regular Expression] is [voice]. 
 * ・Files in the subfolder [voice] of the folder [audio/se].
 * ・File names that start with the word "voice".
 * ・Exclusively registered files (see Plug-in Parameters: Voice File List)
 * 
 * ■Auto Reading
 * After text has been displayed,next message is automatically displayed after a certain amount of time.
 * Waits until the voice playback is finished(if there is one).
 * Stops voice playback when manually clicked for next message.
 * 
 * Please use included optional plugin to adjust the speed for Auto Reading.
 * 
 * ■How to access Log Screen
 * When following operation is performed while text is displayed, log screen will appear.
 * ・Hold down shift.
 * ・Scroll up.
 * ・Press button on the screen.
 * The following is an alternative way to access Log Screen.
 * If you prefer to open Log Screen on the map,
 * use Common Event to open the plugin command.
 * 
 * ■NPC convo Mode
 * A function for when you move around the map and conversing with multiple events.
 * Overwrites the most recent message
 * when recording messages for the same event.
 * 
 * Switch Control can temporarily deactivate the NPC convo Mode display.
 * During deactivation, NPC convo Mode is hidden when log screen in opened.
 * 
*/

///////////////////////////////////////////////////////////////////////////////////////////////////////


/*:ja
 * @plugindesc ノベルゲーム向けの機能を提供します
 * @author しぐれん
 * 
 * @target MZ
 * @orderAfter MOG_TitleSplashScreen
 * 
 * @url https://siguren400.booth.pm/items/3300534
 * 
 * 
 * @command Show
 * @text バックログを開く
 * 
 * @command ClearAll
 * @text 全てのログをクリアする
 * @desc 保存されているログを全て消します。
 * 
 * 
 * @command GetLogSize
 * @text 現在のログの大きさを取得
 * @arg variableId
 * @text 結果を書き込む変数
 * @type variable
 * @default 0
 * 
 * @command SetAutoMode
 * @text オートモードの設定
 * @desc イベントコマンドからオート設定のON/OFFを切り替えます。
 * @arg automode
 * @type boolean
 * @default true
 * 
 * @command GetAutoMode
 * @text オートモードの状態取得
 * @desc 現在のオート設定の状態を取得します。
 * @arg swtichId
 * @type switch
 * @default 0
 * 
 * @command HasLastVoice
 * @text 最後に再生した音声の有無を取得
 * @desc 音声再生ボタンが有効かどうかを取得します。
 * @arg switchId
 * @default 0
 * 
 * 
 * @command GetAutoWaitTime
 * @text GetAutoWaitTime/オート待ち時間取得
 * @arg variableId
 * @text 結果を書き込む変数
 * @type variable
 * @default 0
 * 
 * @command SetAutoWaitTime
 * @text SetAutoWaitTime/オート待ち時間設定
 * @arg variableId
 * @type variable
 * @default 0
 * 
 * @command SetLogLimitSizeBasic
 * @text ログの保存容量を設定
 * @arg variableId
 * @type variable
 * @default 0
 * 
 * @command GetLogLimitSizeBasic
 * @text ログの保存容量を取得
 * @arg variableId
 * @text 結果を書き込む変数
 * @type variable
 * @default 0
 * 
 * @command SetLogLimitSizeExtends
 * @text ログの予備容量を設定
 * @arg variableId
 * @type variable
 * @default 0
 * 
 * @command GetLogLimitSizeExtends
 * @text ログの予備容量を取得
 * @desc 取得するのはオプションの状態です。
 * @arg variableId
 * @text 結果を書き込む変数
 * @type variable
 * @default 0
 * 
 * @command GetKeepingAutoMode
 * @text オート保持の設定を取得
 * @type switch
 * @default 0
 * 
 * @command GetMachinakaLines
 * @text 街中モードの同時表示数を取得
 * @arg variableId
 * @text 結果を書き込む変数
 * @type variable
 * @default 0
 * 
 * @command SetMachinakaLines
 * @text 街中モードの同時表示数を設定
 * @arg variableId
 * @type variable
 * @default 0
 * 
 * 
 * @command ShowConsloeProxy
 * @text ログ表示用のデータをコンソールに表示
 * 
 * @param menuCommand
 * @text メニューコマンド
 * @type struct<MultiLangString>
 * @desc メニューにログを開くコマンドを追加します。
 * コマンド名が空欄の場合、追加しません。
 * @default {"ja_JP":"","en_US":"","zh_CN":"","ko_KR":"","de_DE":"","fr_FR":"","ru_RU":""}
 * 
 * @param voice
 * @text ボイス設定(廃止予定)
 * @desc 「文章の表示」と「SEの再生」を関連付けます。
 * 合致したファイルを音声として、メッセージログに記録します。
 * @type struct<VoiceJA>
 * @default {"regex":"voice","files":"[]"}
 * 
 * 
 * @param voiceFileList
 * @text ボイスファイルリスト
 * @type file[]
 * @dir audio/se/
 * @desc 指定したファイルをボイスファイルとして扱います。
 * 正規表現で対応できないファイルを対応させる場合に使います。
 * @default []
 * 
 * @param voiceFileRegex
 * @text ボイスファイル正規表現
 * @desc  正規表現で合致したファイル名を音声として扱います。
 * 内部的には「"^"+"この文字列"+".+"」で処理しています。
 * @type string
 * @default voice
 * @parent voiceFileList
 * 
 * @param buttonSetting
 * @text ボタンの全般設定
 * @type struct<ButtonSettingJA>
 * @default {"offsetX":"0","offsetY":"0","windowOverlap":"outside","windowStretchY":"0"}
 * 
 * @param backlogButton
 * @text ログ表示ボタン
 * @type struct<ButtonJA>
 * @default {"x":"0","y":"0","image":"MessageButton","enabledSwitch":"0","pressedSound":"{\"name\":\"Book1\",\"volume\":\"90\",\"pitch\":\"100\",\"pan\":\"0\"}","hot":"{\"x\":\"0\",\"y\":\"256\",\"width\":\"64\",\"height\":\"64\"}","cold":"{\"x\":\"64\",\"y\":\"256\",\"width\":\"64\",\"height\":\"64\"}"}
 * @parent buttonSetting
 * 
 * @param saveButton
 * @text セーブボタン
 * @type struct<ButtonJA>
 * @default {"x":"64","y":"0","image":"MessageButton","enabledSwitch":"0","pressedSound":"{\"name\":\"Book2\",\"volume\":\"90\",\"pitch\":\"100\",\"pan\":\"0\"}","hot":"{\"x\":\"0\",\"y\":\"128\",\"width\":\"64\",\"height\":\"64\"}","cold":"{\"x\":\"64\",\"y\":\"128\",\"width\":\"64\",\"height\":\"64\"}"}
 * @parent buttonSetting
 * 
 * @param loadButton
 * @text ロードボタン
 * @type struct<ButtonJA>
 * @default {"x":"128","y":"0","image":"MessageButton","enabledSwitch":"0","pressedSound":"{\"name\":\"Book2\",\"volume\":\"90\",\"pitch\":\"100\",\"pan\":\"0\"}","hot":"{\"x\":\"0\",\"y\":\"192\",\"width\":\"64\",\"height\":\"64\"}","cold":"{\"x\":\"64\",\"y\":\"192\",\"width\":\"64\",\"height\":\"64\"}"}
 * @parent buttonSetting
 * 
 * @param voiceRepeatButton
 * @text ボイスリプレイボタン
 * @desc 押した場合、最後のボイスをもう一度再生します。
 * @type struct<ButtonJA>
 * @default {"x":"256","y":"0","image":"MessageButton","enabledSwitch":"0","pressedSound":"","hot":"{\"x\":\"0\",\"y\":\"320\",\"width\":\"64\",\"height\":\"64\"}","cold":"{\"x\":\"64\",\"y\":\"320\",\"width\":\"64\",\"height\":\"64\"}"}
 * @parent buttonSetting
 * 
 * @param autoButton
 * @text オートボタン
 * @desc オート状態の場合、
 * 解除されるまでボタンが押されている状態の表示になります。
 * @type struct<ButtonJA>
 * @default {"x":"196","y":"0","image":"MessageButton","enabledSwitch":"0","pressedSound":"{\"name\":\"Key\",\"volume\":\"90\",\"pitch\":\"100\",\"pan\":\"0\"}","hot":"{\"x\":\"0\",\"y\":\"0\",\"width\":\"64\",\"height\":\"64\"}","cold":"{\"x\":\"64\",\"y\":\"0\",\"width\":\"64\",\"height\":\"64\"}"}
 * @parent buttonSetting
 * 
 * @param autoGauge
 * @text オートゲージ
 * @type struct<AutoGauge>
 * @default {"gaugeType":"circle","x":"0","y":"0","width":"10","texture":"","color":"FF0000"}
 * @parent autoButton
 * 
 * @param skipButton
 * @text 既読スキップボタン
 * @desc 押された際に指定スイッチをONにします。
 * 既読スキップの処理はManoUZ_Kidokuを使います。
 * @type struct<ButtonJA>
 * @default {"x":"320","y":"0","image":"MessageButton","hot":"{\"x\":\"0\",\"y\":\"64\",\"width\":\"64\",\"height\":\"64\"}","cold":"{\"x\":\"64\",\"y\":\"64\",\"width\":\"64\",\"height\":\"64\"}","enabledSwitch":"0","pressedSound":"{\"name\":\"Key\",\"volume\":\"90\",\"pitch\":\"100\",\"pan\":\"0\"}"}
 * @parent buttonSetting
 * 
 * @param logWriteSwitch
 * @text ログ書き込み有効化スイッチ
 * @desc 指定したスイッチがONの場合のみ、ログを書き込みます。
 * 未指定の場合、常に書き込みます。
 * @type switch
 * @default 0
 * 
 * @param logWriteOnBattle
 * @text 戦闘中のログ書き込み
 * @desc OFFにすると、戦闘中はログの書き込みを行いません。
 * @type boolean
 * @default false
 * 
 * @param logStyle
 * @type struct<LogStyleJA>
 * @text ログ画面スタイル
 * @desc ログ画面の配置などを設定します。
 * @default {"windowFileName":"Window","background":"","speakerName":"【%1】","choiceFormat":"【%1】：%2","choiceText":"{\"ja_JP\":\"選択肢\",\"en_US\":\"choices\",\"zh_CN\":\"选择\",\"ko_KR\":\"선택\",\"de_DE\":\"Auswahl\",\"fr_FR\":\"Les choix\",\"ru_RU\":\"Выбор\"}","voiceIconIndex":"4"}
 * 
 * @param autoWaitTime
 * @text オート待ち時間
 * @type number
 * @default 360
 * 
 * @param logLimitBasic
 * @text ログ保存数(基本)
 * @desc 古いログを掃除する際に、ここで指定した個数以下にします。
 * @type number
 * @default 40
 * 
 * @param logLimitExtends
 * @text ログ保存数(追加)
 * @desc 保存されたログが基本＋追加を超えた場合、
 * 古いログの破棄を行います。
 * @type number
 * @default 20
 * 
 * @param machinakaModeSwitch
 * @text 街中モード制御スイッチ
 * @desc 指定スイッチがONの間のみ機能を有効化します。
 * @type switch
 * @default 0
 * 
 * @param macinakaModeDefault
 * @text 街中モード初期値
 * @type boolean
 * @default true
 * @parent macinakaMode
 * 
 * @param machinakaModeLines
 * @text 街中モード・表示行数
 * @type number
 * @default 3
 * 
 * @param choiceFormat
 * @text 選択肢の表示形式
 * @type string
 * @desc %1に「選択肢」、%2に項目の文字列が表示されます。
 * @default 【%1】：%2
 * @parent logStyle
 * 
 * @param choiceText
 * @text 選択肢
 * @type struct<MultiLangString>
 * @default {"ja_JP":"選択肢","en_US":"choices","zh_CN":"选择","ko_KR":"선택","de_DE":"Auswahl","fr_FR":"Les choix","ru_RU":"Выбор"}
 * @desc 選択肢の表示名。多言語対応済み。
 * @parent logStyle
 * 
 * @param choiceCancelText
 * @text 選択肢キャンセル時の文章
 * @type struct<MultiLangString>
 * @default {"ja_JP":"キャンセル","en_US":"Cancel","zh_CN":"","ko_KR":"","de_DE":"","fr_FR":"","ru_RU":""}
 * @parent logStyle
 * 
 * @help
 * 以下の機能を提供します
 * ■メッセージログの保存・表示
 * ■メッセージの途中セーブ・ロード実行
 * ■ボイス記録機能
 * 
 * 「文章の表示」によって表示された文章をログとして記録します。
 * 
 * ■ボタンの割り当てについて
 * メッセージの表示中、
 * 以下のシンボルの入力が行われた場合にシーン切り替えを実行します。
 * 内部判定はlongPressedを使用しているので、長押しが必要です。
 * ・セーブ:pageup
 * ・ロード:pagedown
 * ・ログ　:shift
 * 
 * ■ボタン配置について
 * ボタン配置は相対位置で決められています。
 * ボタン領域の四角形は、全てのボタンを収めることができる四角形となります。
 * この四角形の右側がウィンドウの右側と合わさるような位置を基準とします。
 * offsetX,offsetYは、この位置を基準に動かします。
 * 
 * ■途中セーブ機能について
 * メッセージ表示中にセーブ画面を開き、セーブを行えます。
 * トリガーが並列実行のイベントを実行中にセーブを行うと、
 * 不具合が発生することがあります。
 * 
 * ■途中セーブをロードした場合の挙動
 * メッセージを正しく復元するために、イベントの実行状態を巻き戻します。
 * メッセージの文章によって変数などを操作するプラグインと併用すると、
 * 不具合が発生する可能性があります。
 * 
 * ■ログデータが消えている場合
 * プラグインのバージョンが異なる場合、ログデータを初期化します。
 * この場合でもログ以外のセーブデータは読み込み可能です。
 * 
 * ■ボイス記録機能
 * 文章の表示前に「SEの再生」が実行されていると、それをボイスとして記録します。
 * 
 * 効果音が巻き込まれないように、判別方法が用意されています。
 * 効果音はaudio/se/の内部に配置されますが、
 * その中のサブフォルダで登録するかを判定します。
 * ※サブフォルダはツクールMZ ver1.3.1以降の機能です。
 * 対応するのは以下のファイルです。
 * ※以下の設定はパラメータ「ボイスファイル正規表現」が
 * 「voice」の場合の設定方法です。
 * ・フォルダ「audio/se/」内のサブフォルダ「voice」に含まれるファイル
 * ・ファイル名の先頭に「voice」と付くファイル
 * ・専用に登録したファイル(プラグインパラメータ:ボイスファイルリスト参照)
 * 
 * ■メッセージ自動送り
 * 文章の表示が完了した後、一定時間の経過で自動的に次のメッセージを表示します。
 * ボイスがある場合、ボイスの再生完了まで待ちます。
 * 手動でメッセージ送りした場合、ボイスを停止します。
 * 
 * メッセージ自動送りの速度を変更する場合、付属のオプションプラグインを入れてください。
 * 
 * ■スクロールテキスト記録
 * イベントコマンド「文章のスクロール表示」で表示する文章も記録可能です。
 * 記録する際には、以下のタイミングで文章を区切ります。
 * ・空白の行がある場合
 * ・行の終わりが特定の文字(!?.)である場合(全角文字含む)
 * 
 * ■ログ画面の開き方
 * ログ画面はメッセージ表示中に以下の操作を押すと開きます。
 * ・shiftキーを押し続ける
 * ・マウスホイール上
 * ・画面内のボタンを押す
 * これ以外にも、プラグインコマンドで呼び出す方法があります。
 * マップ上で自由に呼び出せるようにしたい場合、
 * コモンイベントを利用してプラグインコマンドを呼び出してください。
 * 
 * ■街中モード
 * マップ内を移動して複数のイベントに話しかけた場合を想定した機能です。
 * 同じイベントのイベントのメッセージを記録する場合、
 * 最新の1回分で上書きします。
 * 
 * 街中モードによる表示はスイッチ制御で一時的に無効化できます。
 * 無効化中はログ画面を開いても街中モードの選択ウィンドウが非表示になります。
 * 
 * 
 * 
 * ■利用規約
 * 1.利用可能なプロジェクト
 * ・ゲームの内容(配布および販売方法・年齢制限の有無など)を問わず、どのようなゲームにでもご利用いただけます。
 * 
 * 2.利用条件
 * ・ゲームの配布(販売を含む)の際に、同梱のテキストなどに本プラグインを利用したことを記載してください。
 * ・複数人のチームによるゲーム作成の場合、参加者の内１名以上が本プラグインを購入している必要があります。
 * ・本プラグインを利用するに際し、利用者は自己の責任に基づいて使用するものとします。
 * ・本プラグインを改変しての利用は可能ですが、その場合は(5)で示すサポートの範囲外とします。
 * 
 * 3.禁止事項
 * ・改変の有無に関わらず本プラグインをゲームの配布(販売を含む)以外の方法で、公開・配布・販売する行為。
 * ・配布(販売含む)されたゲームの中から本プラグイン抜き出して使用する行為。
 * ・このプラグインに記載された利用規約を削除・改変する行為。
 * 
 * 4.免責事項
 * 当方は以下の内容についてその責任を負わないものとします。
 * ・本プラグインの更新によるセーブデータの互換性消失
 * ・セーブデータの破損
 * ・ゲームデータの破損
 * ・他のプラグインとの併用による誤作動・不具合
 * ・その他プラグインの使用によって生じた損害
 * 
 * 5.不具合対応(サポート)
 * ・本プラグインの不具合について可能な限り対応しますが、修正を行うことを保証するものではありません。
 * 
 * 9.利用規約の変更について
 * ・この利用規約は予告なく変更されることがあります。
 * 
*/
 /*~struct~PlaySeEN:
 * @param name
 * @type file
 * @dir audio/se/
 * 
 * @param volume
 * @text Volume
 * @desc Adjust Voice File Volume
 * @default 90
 * @min 0
 * @max 100
 * @type number
 *
 * @param pitch
 * @text Pitch
 * @desc Adjust Voice File Pitch
 * @default 100
 * @type number
 *
 * @param pan
 * @text Pan
 * @desc Pan Voice File
 * @default 0
 * @min -100
 * @max 100
 * @type number
  */
/*~struct~ButtonSettingEN:
 * @param offsetX
 * @desc Move button horizontally.
 * @type number
 * @min -123456
 * @default 0
 * 
 * @param offsetY
 * @desc Move button vertically.
 * @type number
 * @min -123456
 * @default 0
 * 
 * @param windowOverlap
 * @text Window Overlapping
 * @type select
 * @option Inwards(window will be slightly larger)
 * @value inside
 * @option Outwards(choices will move its position accordingly)
 * @value outside
 * @option Fix(displayed only when origin is near the upper left and message is at the bottom)
 * @value absolute
 * @default outside
 * 
 * @param windowStretchY
 * @text Window Stretch(Y)
 * @desc For reserving space for button placement.
 * @type number
 * @min -123456
 * @default 0
 * 
 * @param disableSwitch
 * @text Force Button Hide
 * @desc Disables all buttons while the specified switch is ON.
 * @type switch
 * @default 0
 * */
/*~struct~LogStyleEN:
 * @param windowRect
 * @text Window Placement
 * @type struct<Rect>
 *
 * @param windowFileName
 * @type file
 * @text Window Image
 * @dir img/system
 * @default Window
 * 
 * @param background
 * @text Background Image
 * @type file
 * @dir img/system
 * @desc Sets the image to be used as the log scene background.
 * 
 * @param freamColor
 * @text Frame Color
 * @desc Specifies the color of the separator line to be inserted between sentences.(e.g. #00FFFFFF)
 * @type string
 * @default #00FFFF
 * 
 * @param speakerName
 * @text Name Display Format
 * @desc Converts names to display (%1→name,%2→voice icon)
 * @type string
 * @default 【%1】%2
 * 
 * 
 * @param voiceIconIndex
 * @text Icon Number
 * @desc When viewing logs, icon appears on logs that contain voice.
 * @type number
 * @default 4
 * 
 * 
 * @param lineSpacing
 * @text Line Spacing
 * @type number
 * @default 2
 * 
*/
//通常効果音をボイス扱いするための機能
/*~struct~VoiceEN:
 * @param regex
 * @text Voice File Regular Expression
 * @desc  Recognizes file names matched by Regular Expression as voice.
 * It is internally processed with ["^"+"this string "+". +""]
 * @type string
 * @default voice
 * 
 * @param files
 * @text Voice File List
 * @type file[]
 * @dir audio/se/
 * @desc Treats the specified file as a voice file.
 * Used for handling files that cannot be handled by Regular Expression. 
 * @default []
 * 
 * 
*/

 /*~struct~PlaySeJA:
 * @param name
 * @type file
 * @dir audio/se/
 * 
 * @param volume
 * @text 音量
 * @desc ボイスファイルの音量
 * @default 90
 * @min 0
 * @max 100
 * @type number
 *
 * @param pitch
 * @text ピッチ
 * @desc ボイスファイルのピッチ
 * @default 100
 * @type number
 *
 * @param pan
 * @text 左右バランス
 * @desc ボイスファイルの左右バランス
 * @default 0
 * @min -100
 * @max 100
 * @type number
  */
/*~struct~ButtonJA:
 * 
 * @param x
 * @text X座標(相対位置)
 * @desc 基準位置はボタングループで設定。
 * 状況により基準位置は移動します。
 * @type number
 * @default 0
 * 
 * @param y
 * @text Y座標(相対位置)
 * @desc 基準位置はボタングループで設定。
 * 状況により基準位置は移動します。
 * @type number
 * @default 0
 * 
 * @param image
 * @text ボタン画像
 * @desc 空欄にするとボタンと対応した機能が無効になります。
 * @type file
 * @dir img/system/
 * 
 * @param hot
 * @text 押されている時の画像
 * @type struct<Rect>
 * @desc ボタンがクリックされている間に表示する画像。
 * ボタン画像の一部から、指定範囲を切り出して使います。
 * @default {"x":"0","y":"0","width":"64","height":"64"}
 * 
 * @param cold
 * @text 通常時の画像
 * @type struct<Rect>
 * @desc ボタンがクリックされていない間に表示する画像。
 * ボタン画像の一部から、指定範囲を切り出して使います。
 * @default {"x":"0","y":"0","width":"64","height":"64"}
 * 
 * @param enabledSwitch
 * @text 有効化スイッチ
 * @desc 指定したスイッチがONの場合のみボタンを表示します。
 * ニューゲーム時にONになります。未指定の場合、常に有効化。
 * @type switch
 * @default 0
 * 
 * @param pressedSound
 * @text 押された時の音声
 * @type struct<PlaySeJA>
 * 
 */
/*~struct~Rect:
 * @param x
 * @type number
 * @default 0
 * 
 * @param y
 * @type number
 * @default 0
 * 
 * @param width
 * @type number
 * @default 0
 * 
 * @param height
 * @type number
 * @default 0
*/

/*~struct~AutoGauge:
 * 
 * @param gaugeType
 * @type select
 * @option none
 * @option circle
 * @default circle
 * 
 * @param x
 * @type number
 * @default 0
 * 
 * @param y
 * @type number
 * @default 0
 * 
 * @param width
 * @type number
 * @default 10
 * 
 * @param texture
 * @type file
 * @dir img/system/
 * 
 * @param color
 * @type string
 * @default FF0000
*/
/*~struct~ButtonSettingJA:
 * @param offsetX
 * @desc ボタンを横方向に移動します。
 * @type number
 * @min -123456
 * @default 0
 * 
 * @param offsetY
 * @desc ボタンを縦方向に移動します。
 * @type number
 * @min -123456
 * @default 0
 * 
 * @param windowOverlap
 * @text ウィンドウとの重なり
 * @type select
 * @option 内側(ウィンドウが少し大きくなります)
 * @value inside
 * @option 外側(必要に応じて選択肢が移動します)
 * @value outside
 * @option 絶対指定(ほぼ左上原点・メッセージが下側の場合のみ表示)
 * @value absolute
 * @default outside
 * 
 * @param windowStretchY
 * @text ウィンドウの伸縮(Y)
 * @desc ボタン設置用のスペースを確保するときに使います。
 * @type number
 * @min -123456
 * @default 0
 * 
 * @param disableSwitch
 * @text 強制ボタン非表示
 * @desc 指定したスイッチがONの間、全てのボタンを無効化します。
 * @type switch
 * @default 0
 * */
/*~struct~LogStyleJA:
 * @param windowRect
 * @text ウィンドウ配置
 * @type struct<Rect>
 * 
 *
 * @param windowFileName
 * @type file
 * @text ウィンドウ画像
 * @dir img/system
 * @default Window
 * 
 * @param background
 * @text 背景画像
 * @type file
 * @dir img/system
 * @desc ログシーンの背景に使う画像を設定します。
 * 
 * @param freamColor
 * @text 枠線の色(空欄の場合非表示)
 * @desc 文章の間に入れる区切り線の色を指定します。#00FFFF表記
 * @type string
 * @default #00FFFF
 * 
 * @param speakerName
 * @text 名前表示フォーマット
 * @desc 名前を変換して表示します(%1→名前,%2→ボイスアイコン)
 * @type string
 * @default 【%1】%2
 * 
 * 
 * @param voiceIconIndex
 * @text アイコン番号
 * @desc ログ表示の際に、ボイスがあるログにアイコンを表示します。
 * @type number
 * @default 4
 * 
 * 
 * @param lineSpacing
 * @text 行間スペース
 * @type number
 * @default 2
 * 
*/
//通常効果音をボイス扱いするための機能
/*~struct~VoiceJA:
 * @param regex
 * @text ボイスファイル正規表現
 * @desc  正規表現で合致したファイル名を音声として扱います。
 * 内部的には「"^"+"この文字列"+".+"」で処理しています。
 * @type string
 * @default voice
 * 
 * @param files
 * @text ボイスファイルリスト
 * @type file[]
 * @dir audio/se/
 * @desc 指定したファイルをボイスファイルとして扱います。
 * 正規表現で対応できないファイルを対応させる場合に使います。
 * @default []
 * 
 * 
*/


 /*~struct~MultiLangString:
  * @param ja_JP
    @text 日本語

    @param en_US
    @text English

    @param zh_CN
    @text 中文(簡体)
    @desc 我不知道繁體中文，所以目​​前不支持。
    繁体については私が知らないので非対応です。

    @param ko_KR
    @text 한국

    @param de_DE
    @text Deutsche(ドイツ語)

    @param fr_FR
    @text français(フランス語)

    @param ru_RU
    @text русский(ロシア語)
 */


/**
 * @typedef {Object} ManoUZ_LogOptionParametor
 * @property {number} logLimitBasic
 * @property {number} logLimitExtends
 * @property {number} autoWaitTime
 * @property {boolean} keepingAutoMode
 * @property {number} gaugeColor
 * @property {number} machinakaLines
 */
/**
 * @typedef {Object} UZ_AudioFileParam
 * @property {string} name
 * @property {number} pan
 * @property {number} pitch
 * @property {number} pos
 * @property {number} volume
 */

/**
 * @typedef {object} LogManagerExportConcept
 * @property {(option:ManoUZ_LogOptionParametor)=>void} setOptionValue
 * @property {()=>ManoUZ_LogOptionParametor} cloneOptionValue
 * @property {(audio:UZ_AudioFileParam,exParam:any)=>void} playVoice
 */

/**
 * @type {LogManagerExportConcept}
 */
const ManoUZ_MessageLog=(function(){
    'use strict';

    const PLUGIN_VERSION =20221202;
/**
 * @typedef {object} LogItemStyle
 * @property {number} voiceIcon
 * @property {number} numFaceLines
 * @property {string } speakerNameFormat
 * @property {number} lineSpacing
 */

/**
 * @typedef {object} LogManagerConcept
 * @property {()=>LogItemStyle} getLogItemStyle
 * @property {(baseText:string)=>string} createChoiceItemText
 * @property {(audio:UZ_AudioFileParam,customParam:{})=>void} setLastVoiceParam
 * @property {(option:ManoUZ_LogOptionParametor)=>void} setOptionValue
 * @property {()=>ManoUZ_LogOptionParametor} cloneOptionValue
 * 
 */

/**
 * @typedef {object} MultiLineItemConcept
 * @property {()=>UZ_AudioFileParam} voiceParam
 * @property {()=>boolean} needsVoiceIconRender
 * @property {()=>boolean} needsDrawBackground
 * @property {()=>boolean} isIconLeft
 * @property {()=>boolean} isTopItem
 * @property {()=>number} lineIndex
 * @property {()=>number} numItemLines
 * @property {()=>number} faceIndex
 * @property {()=>string} faceName 
 * @property {()=>string} text
 */

/**
 * 
 * @typedef {object} LogItemNamedConcept
 * @property {()=>string} speakerName
 * @property {()=>string} faceName
 * @property {()=>number} faceIndex
 * @property {()=>string} text
 * @property {()=>UZ_AudioFileParam} voiceParam
 * @property {()=>number} numLines
 * 
 */

/**
 * @typedef {object} LogItemFaceConcept
 * @property {()=>string} chacterName
 */

 class I_Multiline_Porxy{
    text(){
        return "";
    }
    numItemLines(){
        return 1;
    }
    /**
     * @returns {UZ_AudioFileParam}
     */
    voiceParam(){
        return null;
    }
    needsVoiceIconRender(){
        return !!this.voiceParam();
    }
    needsDrawBackground(){
        return true;
    }
    faceName(){
        return null;
    }
    faceIndex(){
        return 0
    }
    isTopItem(){
        return true;
    }
    isIconLeft(){
        return false;
    }
    lineIndex(){
        return 0;
    }
 }

class Multiline_PorxyBase extends I_Multiline_Porxy{
    /**
     * @param {LogItemNamedConcept & I_LogItem} item 
     */
     constructor(item){
        super();
        /**
         * @protected
         */
        this._item=item;
    }
    isTopItem(){
        return false;
    }
    /**
     * 
     * @returns 
     */
    item(){
        return null;
    }
    hasName(){
        return false;
    }
    numItemLines(){
        return this._item.numLines();
    }
    voiceParam(){
        return this._item.voiceParam();
    }
    needsVoiceIconRender(){
        return this.isTopItem()&& this._item.hasVoice();
    }
    needsDrawItem(){
        return false;
    }
    faceName(){
        return this._item.faceName();
    }
    faceIndex(){
        return this._item.faceIndex();
    }
    isIconLeft(){
        return false;
    }
    needsDrawBackground(){
        return false;
    }
}

class MultilineItemProxy_Choice extends I_Multiline_Porxy{
    /**
     * 
     * @param {LogItem_Choice} choice 
     */
    constructor(choice){
        super();
        this._choice=choice;
    }
    text(){
        return this._choice.text();
    }
    needsDrawBackground(){
        return true;
    }

}

class MultilineItemProxy_Name extends Multiline_PorxyBase{
    /**
     * @param {LogItemNamedConcept & I_LogItem} item 
     */
     constructor(item){
        super(item);

    }
    isIconLeft(){
        return true;
    }

    isTopItem(){
        return true;
    }
    text(){
        return this._item.speakerName();
    }
    lineIndex(){
        return 0;
    }
    needsDrawBackground(){
        return true;
    }
}


class MultiLineItemProxy_Message extends Multiline_PorxyBase{
    /**
     * @param {LogItemNamedConcept & I_LogItem} item 
     * @param {number} lineIndex
     */
    constructor(item,lineIndex){
        super(item);
        this._lineIndex =lineIndex;
    }
    lineIndex(){
        return this._lineIndex
    }

    isIconLeft(){
        return false;
    }
    isTopItem(){
        return !this._item.speakerName();
    }
    text(){
        return this._item.text();
    }
    needsDrawItem(){
        return true;
    }
    needsDrawBackground(){
        return this.isTopItem();
    }
}

class MultiLineItemProxy_Empty extends Multiline_PorxyBase{
    /**
     * @param {LogItemNamedConcept & I_LogItem} item 
     * @param {number} lineIndex
     */
     constructor(item,lineIndex){
        super(item);
        this._lineIndex =lineIndex;
    }
    text(){
        return "";
    }
    isTopItem(){
        return false;
    }
    lineIndex(){
        return this._lineIndex;
    }
    needsDrawBackground(){
        return false;
    }
    
}

class I_LogItem{
    onSaveLoaded(){
    }

    /**
     * @returns {ReadonlyArray<MultiLineItemConcept>}
     */
    createProxy(){
        return []
    }

    /**
     * @returns {Readonly<UZ_AudioFileParam>}
     */
    voiceParam(){
        return null;
    }

    //選択肢のためにfalseで解決できるようにする
    hasVoice(){
        return !!this.voiceParam();
    }
    isChoiceMatch(){

    }
    /**
     * @returns {string}
     */
    titleText(){
        return null;
    }
}

class MultiLine_Message extends I_LogItem{
    constructor(){
        super();
        this._text ="";
        this._faceindex=0;
        this._facename =null;
        this._se =null;
        this._numLines=0;
    }
    /**
     * @param {string} spkName 
     * @param {string} facename 
     * @param {number} faceindex 
     */
    startMessage(spkName,facename,faceindex){
        this._faceindex =faceindex;
        this._facename=facename;
        this._text ="";
        this._speakerName=spkName;
    }

    titleText(){
        if(!this._text){
            return null;
        }
        //タイトル行検出の正規表現
        const matchResult= this._text.match(/([^\r\n]\s*\S.*)/);
        if(matchResult){
            return matchResult[0];
        }
        return null;
    }
    /**
     * @param {string} speakerName 
     * @param {string} msg 
     * @param {string} facename
     * @param {number} faceindex
     * @param {UZ_AudioFileParam} se 
     */
    writeText(speakerName,msg,facename,faceindex,se){
        this._speakerName=speakerName;
        this._text =msg;
        this._facename=facename;
        this._faceindex =faceindex;
        this._se=se;
    }
    /**
     * @param {ReadonlyArray<string>} texts 
     */
    writeTextArray(texts){
        if(texts.length >0){
            const msg = texts.join("\n");        
            this.writeText(null,msg,null,0,null);
            this._numLines = texts.length;    
        }
    }
    /**
     * 
     * @param {number} num 
     */
    addLines(num){
        this._numLines +=num;
    }
    text(){
        return this._text;
    }
    textLines(){
        return this._numLines;
    }
    numLines(){
        const nameLine = this.hasName() ? 1:0;
        const faceLine = !!this.faceName() ? 4:0;
        return Math.max(faceLine,nameLine + this.textLines());
    }
    faceName(){
        return this._facename;
    }
    faceIndex(){
        return this._faceindex;
    }
    hasName(){
        return !!this._speakerName;
    }
    speakerName(){
        return this._speakerName;
    }
    voiceParam(){
        return this._se;
    }
    needsVoiceIconRender(){
        return !!this._se;
    }
    createProxy(){
        const hasName=this.hasName();
        const msgTopIndex = hasName ? 1 :0;
        const msg = new MultiLineItemProxy_Message(this,msgTopIndex);
        /**
         * @type {MultiLineItemConcept[]}
         */
        const result =[msg];
        if(hasName){
            const name = new MultilineItemProxy_Name(this);
            result.unshift(name);
        }
        const lines = this.numLines();
        for(let i= msgTopIndex +1 ;  i< lines; ++i){
            const emptyLine = new MultiLineItemProxy_Empty(this,i);
            result.push(emptyLine);
        }

        return result;
    }
}


//新しい方のセーブデータ
class Log_ContentsObject{

    /**
     * @param {number} version 
     */
    constructor(version){
        this._version= version;

        /**
         * @private
         * @type {LogList[]}
         */
        this._machinakaList=[];

        /**
         * @type {LogList}
         */
        this._logList =new LogList();
        /**
         * @private
         */
        this._machinakaFocus=0;
        this.clearLastCodeIndex();


    }
    /**
     * @param {number} yellow 
     * @param {number} red 
     */
    checkLogSize(yellow,red){
        const limit =Math.max(yellow,red);
        if(this._machinakaList.length > limit){
            const start =Math.abs(red - yellow);
            const list =this._machinakaList.slice(start);
            this._machinakaList =list;
        }
    }
    /**
     * @param {I_LogItem} item 
     */
    addItem(item){
        if(this._logList){
            this._logList.add(item);
        }
    }
    version(){
        return this._version;
    }
    lastCodeIndex(){
        return this._lastIndex;
    }
    /**
     * @param {Number} index 
     */
    setLastCodeIndex(index){
        //インタプリタ側のIndex
        this._lastIndex=index;
    }
    clearLastCodeIndex(){
        this.setLastCodeIndex(NaN);
    }
    hasLogList(){
        return this._logList && !this._logList.isEmpty();
    }

    createMachinakaList(){
        const result = this._machinakaList.filter( (log)=>{
            if(this._logList){
                return !this._logList.isLocationMatch(log.mapId(),log.eventId());
            }
            return true;
        });
        // if(this._logList && !this._logList.isEmpty()){
        //     result.push(this._logList);
        // }
        if(this.hasLogList()){
            result.push(this._logList);
        }
        return result;
    }

    /**
     * @returns 
     */
    saveCurrentLog(){
        //ログが存在しないので中断
        if(!this.hasLogList()){
           return; 
        }
        const index = this._machinakaList.findIndex( (log)=>{
            return this._logList.otherLocationMatch(log);
        });
        if(index >=0){
            this._machinakaList[index ]=this._logList;
        }else{
            this._machinakaList.push(this._logList);
        }
        this._logList =null;
    }

    /**
     * 
     * @param {number} mapId 
     * @param {number} eventId 
     */
    makeNewLog(mapId,eventId){
        const log =new LogList();
        log.setLocation(mapId,eventId);
        this._logList =log;
    }

    createProxy(){
        if(this._logList){
            return this._logList.createProxy();
        }
        return [];
    }
    lastVoice(){
        if(this._logList){
            const item = this._logList.lastItem();
            if(item){
                return item.voiceParam();
            }    
        }
        return null;
    }
    calcSize(){

    }
    forceCleanup(){

    }
    createMinimize(){
        const newLog= new Log_ContentsObject(this._version);
        newLog._machinakaList =[];
        return newLog;
    }
    logSize(){
        return 0;
    }
}

class LogItem_Choice extends I_LogItem{
    /**
     * @param {string} text 
     * @param {number} index
     */
    constructor(text,index){
        super();
        this._text = text;
        this._index=index;
    }
    createProxy(){
        return [
            new MultilineItemProxy_Choice(this)
        ];
    }
    text(){
        return this._text;
    }
    titleText(){
        return this._text;
    }
}

class ReadOnlyLogList{

    /**
     * @protected
     * @returns {ReadonlyArray<I_LogItem>}
     */
    list(){
        return [];
    }
    lastItemIndex(){
        return this.logSize() -1;
    }
    /**
     * @returns 
     */
    mapId(){
        return NaN;
    }
    eventId(){
        return NaN;
    }
    logSize(){
        return this.list().length;
    }
    /**
     * 
     * @param {number} mapId 
     * @param {number} eventId 
     */
    isLocationMatch(mapId,eventId){
        return this.mapId() ===mapId && this.eventId() ===eventId;
    }
    /**
     * 
     * @param {ReadOnlyLogList} otherLog 
     */
    otherLocationMatch(otherLog){
        const mapId = this.mapId();
        const eventId = this.eventId();
        if(isNaN(mapId)  || isNaN(eventId)){
            return false;
        }
        return this.mapId() === otherLog.mapId() 
            && this.eventId() ===otherLog.eventId();
    }
    /**
     * 
     * @returns {ReadonlyArray<MultiLineItemConcept>}
     */
    createProxy(){
        /**
         * @type {MultiLineItemConcept[]}
         */
        const result =[];
        const list=this.list();
        for (const iterator of list) {
            const p = iterator.createProxy();
            result.push(...p);
        }
        return result;
    }
}

class LogList extends ReadOnlyLogList{
    constructor(){
        super();
        this.clear();
    }
    list(){
        return this._list;
    }
    clear(){
        /**
         * @type {I_LogItem[]}
         */
        this._list = [];
        this.setTitle("");
        this.setLocation(NaN,NaN);
    }

    createTitle(){
        if(this._title){
            return this._title;
        }
        for (const iterator of this._list) {
            const title = iterator.titleText();
            if(title){
                return title;
            }
        }
        return "notitle";
    }
    /**
     * @param {string} title 
     */
    setTitle(title){
        this._title =title;
    }
    /**
     * @param {number} mapId 
     * @param {number} eventId 
     */
    setLocation(mapId,eventId){
        this._mapId=mapId;
        this._eventId =eventId;
    }
    /**
     * @this {Readonly<LogList>}
     * @returns 
     */
    mapId(){
        return this._mapId;
    }
    /**
     * @this {Readonly<LogList>}
     * @returns {number}
     */
    eventId(){
        return this._eventId;
    }

    isEmpty(){
        return this._list.length <=0 ;
    }

    onSaveLoaded(){
        //メモ:地味に重い可能性があるので、今は無効にしておく
        // for (const iterator of this._list) {
        //     iterator.onSaveLoaded();
        // }
    }

    /**
     * @desc アツマールなどのブラウザ環境で、最小化しての保存で使う。
     * @returns {LogList}
     */
    createMinimize(){
        const item = this.lastItem();
        const result = new LogList();
        result.add(item);
        return result;
    }
    lastItem(){;
        const index=this.lastItemIndex();
        const item=this._list[index];
        return item? item :null;
    }

    /**
     * @param {I_LogItem} item 
     */
    add(item){
        this._list.push(item);
    }
}

class I_LogButtonLayout{
    isValid(){ return false;}
    playClickSound(){}
    x(){ return 0;}
    y(){ return 0;}
    width(){ return 0;}
    height(){return 0;}
    switchId() { return 0;}
    /**
     * @returns {PIXI.Rectangle}
     */
    hotFrame(){ return null;}
    /**
     * @returns {PIXI.Rectangle}
     */
    coldFrame(){ return null;}
    /**
     * @returns {Bitmap}
     */
    bitmap(){ return null;}
}

class I_ButtonBehavior{

    /**
     * @returns {I_LogButtonLayout}
     */
    layout(){ return null;}
    isEnabled(){ return false;}
    onClick(){}
}


/**
 * @typedef {Object} Button_BehavioConcept
 * @property {()=>void} onClick
 * @property {()=>boolean} isEnabled
 * @property {()=>I_LogButtonLayout} layout
 */

/**
 * @typedef {Sprite_Clickable} Sprite_LogButtonConcept
 * @property {()=>boolean} isBeingTouched
 * @property {()=>void} startMessage
 */



/**
 * @typedef {object} TextState
 * @property {number} outputWidth
 * @property {number} outputHeight
 * @property {string} text
 * @property {string} buffer
 * @property {number} x
 * @property {number} y
 * @property {number} index
 * @property {number} width
 * @property {number} height
 * @property {boolean} rtl
 * @property {number} startX
 * @property {number} startY
 * @property {boolean} drawing
 */
/**
 * @typedef {object} EventCommand
 * @property {number} code
 * @property {number} indent
 * @property {Array} parameters
 */

/**
 * @typedef {object} LogSaveContentsConcept
 * @property {()=>number} version
 * @property {(yellow:number,red:number)=>void} checkLogSize
 * @property {()=>LogSaveContentsConcept} createMinimize
 * @property {()=>number} logSize
 * @property {()=>Readonly<UZ_AudioFileParam>} lastVoice
 * @property {()=>number} lastCodeIndex
 * @property {()=>void} clearLastCodeIndex
 * @property {(index:number)=>void} setLastCodeIndex
 * @property {()=>ReadonlyArray<MultiLineItemConcept>} createProxy
 * @property {(item:I_LogItem)=>void} addItem
 * @property {()=>ReadonlyArray<LogList>} createMachinakaList
 */

 class LogManager_Export{
    /**
     * @param {LogManagerConcept} LogManager 
     */
    constructor(LogManager){
        this._logManager=LogManager;
        this.setPlayVoiceFunction(null);
    }

    getStyle(){
        return this._logManager.getLogItemStyle()
    }
    /**
     * @param {(se:UZ_AudioFileParam,exParam:{}?)=>void} func 
     */
    setPlayVoiceFunction(func){
        this._channelFunc=func;
    }
    /**
     * @param {UZ_AudioFileParam} seParam 
     * @param {{}} exParam
     */
    playVoice(seParam,exParam){
        if(this._channelFunc && !!exParam){
            this._channelFunc(seParam,exParam);
        }else{
            AudioManager.playSe(seParam);
        }
    }
    voiceIconIndex(){
        return this._logManager.getLogItemStyle().voiceIcon;
    }
    /**
     * @param {string} baseText 
     * @returns 
     */
    createChoiceItemText(baseText){
        return this._logManager.createChoiceItemText(baseText);
    }
     
    /**
     * @param {UZ_AudioFileParam} audio 
     * @param {{}} exParam
     */
    setLastVoiceParam(audio,exParam){
        this._logManager.setLastVoiceParam(audio,exParam)
    }
    /**
     * @param { ManoUZ_LogOptionParametor} option 
     */
    setOptionValue(option){
        this._logManager.setOptionValue(option);
    }
    cloneOptionValue(){
        return this._logManager.cloneOptionValue();
    }

}


 class Sprite_LogButton extends Sprite_Clickable{
    /**
     * @param {I_ButtonBehavior} behavior
     */
    constructor(behavior){
        super();
        const layout=behavior.layout();
        /**
         * @private
         */
        this._behavior=behavior;
        this.bitmap =layout.bitmap();
        const hot = layout.hotFrame();
        this.setHotFrame(hot.x,hot.y,hot.width,hot.height);
        const cold = layout.coldFrame();
        this.setColdFrame(cold.x,cold.y,cold.width,cold.height);
        this.updateFrame();
        this.x=layout.x();
        this.y=layout.y();
         this._pressed=false;
         this._hovered=false;
    }
    /**
     * @param {I_ButtonBehavior} b 
     */
    setBehavior(b){

    }
    get height(){
        return Math.max(this._hotFrame.height,this._coldFrame.height);
    }
    get width(){
        return Math.max(this._hotFrame.width,this._coldFrame.width);
    }

    /**
     * 
     * @param {number} rate 0~1の値。oppeness/255と同値。
     * @param {number} oppenness 0~255の値
     */
    updateOpenness(rate,oppenness){
        this.alpha =rate;
    }

    update(){
        super.update();
        this.processTouch();
        this.updateFrame();
    }
    /**
     * @private
     * @param {Number} x 
     * @param {Number} y 
     * @param {Number} width 
     * @param {Number} height 
     */
    setHotFrame(x,y,width,height){
        this._hotFrame = new Rectangle(x, y, width, height);
    }
    /**
     * @private
     * @param {Number} x 
     * @param {Number} y 
     * @param {Number} width 
     * @param {Number} height 
     */
    setColdFrame(x,y,width,height){
        this._coldFrame = new Rectangle(x, y, width, height);
    }
    isButtonHot(){
        return this.isPressed()
    }
    updateFrame(){
        const frame = this.isButtonHot() ? this._hotFrame:this._coldFrame;
        if (frame) {
            this.setFrame(frame.x, frame.y, frame.width, frame.height);
        }
    }
    isClickEnabled(){
        if(this.parent){
            if(!this.parent.visible){
                return false;
            }
        }
        return this.visible && this._behavior.isEnabled();
    }
    onClick(){
        if(this.isClickEnabled()){
            this._behavior.onClick();
        }
    }
    onStartMessage(){
        this.visible =this._behavior.isEnabled();
        this.updateFrame();
    }

}


class LogManager_MVMZ_workaround{
    /**
     * @param {Window_Selectable} window_ 
     * @param {Number} index 
     */
    forceSelect(window_,index){
        
    }
    /**
     * @returns {Sprite_LogButton}
     * @param {Button_BehavioConcept} behavior 
     */
    createButtonSprite(behavior){
        return null;
    }
    /**
     * @returns {Sprite_OnOffButton}
     * @param {OnOffButton_BehaviorBase} behavior 
     */
    createOnOffButtonSprite(behavior){
         return null;
    }
    windowMessageInit(rect){


    }
    quickSave(){

    }
    quickLoad(){

    }
}

/**
 * @type {String}
 */
const  PLUGIN_NAME= ('ManoUZ_MessageLog');
function getCurrentScriptName(){
    //@ts-ignore
   const pluginName = decodeURIComponent(document.currentScript.src).match(/([^/]+)\.js$/);
   if(pluginName){ return pluginName[1];}
   return ''; 
}
/**
 * @param {String} officialFileName 
 */
function TestFileNameValid(officialFileName){
    const currentFileName=getCurrentScriptName();
    if(officialFileName ===currentFileName){ return;}
    const message= `Do not rename the plugin file.<br>`+
                    `Current file name: ${currentFileName}<br>`+
                    `Original file name: ${officialFileName}<br>`+
                    `プラグインファイルの名前を変更してはいけません<br>`+
                    `現在のファイル名: ${currentFileName}<br>`+
                    `本来のファイル名: ${officialFileName}`
    throw new Error(message);
}
TestFileNameValid(PLUGIN_NAME);


class MZ_Impriment extends LogManager_MVMZ_workaround{
    /**
     * 
     * @param {Window_Selectable} window_ 
     * @param {Number} index 
     */
    forceSelect(window_,index){
        window_.forceSelect(index);        
    }
    /**
     * @param {Button_BehaviorBase} behavior 
     * @returns 
     */
    createButtonSprite(behavior){
        return new Sprite_LogButton(behavior)
    }
    /**
     * @param {OnOffButton_BehaviorBase} behaviorOnOff 
     * @returns 
     */
    createOnOffButtonSprite(behaviorOnOff){
        return new Sprite_OnOffButton(behaviorOnOff)
    }
    quickSave(){
        const filename =LogManager.option().quickSaveFileName();
        const contents = DataManager.makeSaveContents();
        //@ts-ignore
        return StorageManager.saveObject(filename,contents);
    }
    quickLoad(){
        const filename =LogManager.option().quickSaveFileName();
        //@ts-ignore
        return StorageManager.loadObject(filename).then(contents=>{
            DataManager.createGameObjects();
            DataManager.extractSaveContents(contents);
            DataManager.correctDataErrors();
            return 0;
        });
    }
}


/**
 * @param {Window_Base} window_ 
 * @param {Rectangle} rect 
 * @param {(window:Window_Base,rect:Rectangle)=>void} initFuncton
 */
 function window_initializeMVMZ(window_,rect,initFuncton){
    if(Utils.RPGMAKER_NAME==="MZ"){
        initFuncton.call(window_,rect);
        return
    }
    if(Utils.RPGMAKER_NAME==="MV"){
        initFuncton.call(window_,rect.x,rect.y,rect.width,rect.height);
        return;
    }
    throw( new Error("Unknow RPG MAKER:"+Utils.RPGMAKER_NAME));
}
class SoundEffectParam{

    /**
     * @param {String} name 
     * @param {Number} volume 
     * @param {Number} pitch 
     * @param {Number} pan 
     */
    constructor(name,volume,pitch,pan){
        this._name=name;
        this._volume=volume;
        this._pitch=pitch;
        this._pan=pan;
    }
    /**
     * @param {string} objText 
     * @returns 
     */
    static create(objText){
        if(!objText){
            return null;
        }
        const obj=JSON.parse(objText);
        const name =obj.name;
        const volume =Number(obj.volume);
        const pitch=Number(obj.pitch);
        const pan =Number(obj.pan);
        return new SoundEffectParam(name,volume,pitch,pan);
    }
    play(){
        if(this._name){
            const param= {
                pos:0,
                name:this._name,
                volume:this._volume,
                pitch:this._pitch,
                pan:this._pan,
            }
            AudioManager.playSe(param);    
        }
    }
}
/**
 * @param {string} objText 
 * @returns 
 */
function parseRect(objText){
    const obj = JSON.parse(objText);
    return new Rectangle(
        Number(obj.x),Number(obj.y),Number(obj.width),Number(obj.height)
    );
}
class ButtonLayout extends I_LogButtonLayout{
    /**
     * @param {String} bitmapName 
     * @param {Number} x
     * @param {Number} y
     * @param {PIXI.Rectangle} hotFrame 
     * @param {PIXI.Rectangle} coldFrame
     * @param {Number} switchId
     * @param {SoundEffectParam} se
     */
    constructor(bitmapName,x,y,hotFrame,coldFrame,switchId,se){
        super();
        this._name = bitmapName;
        this._hotFrame = hotFrame;
        this._coldFrame=coldFrame;
        this._x =x;
        this._y =y;
        this._switchId =switchId;
        this._se=se;
    }
    /**
     * @param {String} objText 
     * @returns 
     */
    static create(objText){
        const obj = JSON.parse(objText);
        const se =SoundEffectParam.create(obj.pressedSound);
        const hot = parseRect(obj.hot);
        const cold = parseRect(obj.cold);
        const x = Number(obj.x);
        const y = Number(obj.y);
        const switchId=Number(obj.enabledSwitch||0);
        return new ButtonLayout(obj.image,x,y,hot,cold,switchId,se);
    }
    isValid(){
        return !!this._name;
    }
    switchId(){
        return this._switchId;
    }
    // isSwitchEnabled(){
    //     if(this._switchId >0){
    //         return $gameSwitches.value(this._switchId);
    //     }
    //     return true;
    // }
    // isEnabled(){
    //     return this.isSwitchEnabled();
    // }
    bitmap(){
        return ImageManager.loadSystem(this._name);
    }
    playClickSound(){
        if(this._se){
            this._se.play();
        }
    }
    coldFrame(){
        return this._coldFrame.clone();
    }
    hotFrame(){
        return this._hotFrame.clone();
    }
    x(){
        return this._x;
    }
    y(){
        return this._y;
    }
    width(){
        return Math.max(this._hotFrame.width,this._coldFrame.width);
    }
    height(){
        return Math.max(this._hotFrame.height,this._coldFrame.height);
    } 
}
/**
 *@description ボタンを押された時の挙動を定義するクラス。専用のスプライトに差し込んで使う。
 */
class Button_BehaviorBase extends I_ButtonBehavior{
    /**
     * @param {I_LogButtonLayout} layout 
     * @param {String} inputSymbol 
     */
    constructor(layout,inputSymbol){
        super();
        this._layout=layout;
        this._symbol=inputSymbol;

    }
    isValid(){
        return this._layout && this._layout.isValid();
    }
    layout(){
        return this._layout;
    }

    isEnabled(){
        const switchId = this._layout.switchId();
        if(switchId){
            return $gameSwitches.value(switchId);
        }
        return true;
    }
    symbol(){
        return this._symbol;
    }
    updateInput(){
        if(this._symbol && this.isTriggered()){
            this.onClick();
        }
    }
    isTriggered(){
        return Input.isLongPressed(this._symbol);
    }
    callOkHandler(){

    }
    onClick(){
        this._layout.playClickSound();
        this.callOkHandler();
    }
    /**
     * @returns {Sprite_LogButton}
     */
    createSprite(){
        return this.logManager().getWorkaround().createButtonSprite(this);
    }
    logManager(){
        return LogManager;
    }

    /**
     * @param {SceneClass} scene 
     */
    pushScene(scene){
        this.logManager().pushScene(scene)
    }
}

class LogButtonBehabior extends Button_BehaviorBase{
    callOkHandler(){
        this.pushScene(Scene_MessageLog);
    }
    isTriggered(){
        return super.isTriggered()|| (TouchInput.wheelY <0)
    }
}

class LoadButtonBehabior extends Button_BehaviorBase{

    callOkHandler(){
        this.pushScene(Scene_Load);
    }
}

class SaveButtonBehavior extends Button_BehaviorBase{
    isEnabled(){
        return super.isEnabled()&& this.logManager().canSave();
    }
    callOkHandler(){
        this.pushScene(Scene_Save);
    }
    
}

class QuickSaveButtonBehavior extends Button_BehaviorBase{
    isEnabled(){
        return super.isEnabled()&& this.logManager().canSave();
    }
    callOkHandler(){
        this.logManager().getWorkaround().quickSave();
    }
    onAutoSaveSuccess(){
        

    }
    onAutosaveFailure(){

    }
    
}

class OnOffButton_BehaviorBase extends Button_BehaviorBase{
    createSprite(){
        return this.logManager().getWorkaround().createOnOffButtonSprite(this);
    }
    isButtonOn(){
        return false;
    }
}
 class Sprite_OnOffButton extends Sprite_LogButton{
    /**
     * @param {OnOffButton_BehaviorBase} auto_ 
     */
    constructor(auto_){
        super(auto_);

        this._onOff=auto_;
    }
    onClick(){
        super.onClick()
        console.log(this.isButtonHot());
        this.updateFrame();

    }
    isButtonHot(){
        return this._onOff && this._onOff.isButtonOn();
    }
    
}
/**
 * @typedef {object} AutoGaugeMethod
 * @property {(value:number)=>void} setGaugeValue
 * @property {(()=>void )| null} update
 */

/**
 * @typedef {PIXI.DisplayObject & AutoGaugeMethod} AutoGaugeSprite_Concept
 */
 class Sprite_AutoOnOff extends Sprite_OnOffButton{
    /**
     * @param {AutoButtonBehavior} auto_ 
     */
    constructor(auto_){
        super(auto_);
        this._gauge=null;
    }
    onStartMessage(){
        super.onStartMessage();
        //autokeep対応が必要なので、メッセージ開始時に更新する
        this.updateFrame();
        if(this._gauge){
            this._gauge.setGaugeValue(0);
        }
    }
    /**
     * @param {AutoGaugeSprite_Concept} gauge 
     */
    setAutoGauge(gauge){
        if(this._gauge){
            this.removeChild(this._gauge);
        }
        this._gauge =gauge;
        this.addChild(gauge);
    }
    update(){
        super.update();
        if(this._gauge  ){
            const value =LogManager.autoGaugeRate()
            this._gauge.setGaugeValue(value);
        }
    }
}

class AutoButtonBehavior extends OnOffButton_BehaviorBase{
    /**
     * @param {AutoGaugeBehavior} gauge
     * @param {I_LogButtonLayout} layout 
     * @param {string} inputSymbol
     */
    constructor(gauge,layout,inputSymbol){
        super(layout,inputSymbol);
        this._gauge=gauge;
    }
    callOkHandler(){
        this.logManager().changeAutoMode();
    }
    isButtonOn(){
        return this.logManager().isTextAutoMode();
    }
    /**
     * @this {Readonly<AutoButtonBehavior>}
     * @returns 
     */
    createSprite(){
        /**
         * @type {Sprite_AutoOnOff}
         */
        const autoOnOff= new Sprite_AutoOnOff(this);
        if(this._gauge && this._gauge.isVisible()){
            const gaugeSprite =this._gauge.createSprite( this._layout.width()/2);
            if(gaugeSprite){
                autoOnOff.setAutoGauge(gaugeSprite);    
            }
        }
        return autoOnOff;
    }
}

const KIDOKU_PLUGIN_NAME ="ManoUZ_Kidoku_AlreadyRead";

class SKipButtonBehavior extends OnOffButton_BehaviorBase{
    
    /**
     * @param {I_LogButtonLayout} layout
     * @param {string} inputSymbol
     */
    constructor(layout,inputSymbol){
        super(layout,inputSymbol)
    }
    isValid(){
        return getKidokuSwitch() !==0;
    }
    isButtonOn(){
        return this.logManager().isSkipMode();
    }
    callOkHandler(){
        this.logManager().changeSkipMode();
    }
    

}
/**
 * @param {string} colorText 
 * @returns {number}
 */
function parseColorNumber(colorText){
    const result= /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.exec(colorText);
    if(result){
        const colorNumber =Number(`0x${colorText}`);
        if(!isNaN(colorNumber)){
            return colorNumber;
        }
    }
    return 0;

    // const color =Number("0x"+colorText);
    // if(isNaN(color)){
    //     return 0xffffff;
    // }
    // return color;
}
function parseColorCode(){

}
class AutoGaugeBehavior{
    /**
     * @param {number} x,
     * @param {number} y
     * @param {number} width 
     * @param {string} textureName 
     * @param {number} color 
     * @param {string} type
     */
    constructor(x,y,width,textureName,color,type){
        this._width = width;
        this._textuerName =textureName;
        this._type=type;
        this._color=color;
        this._x=x;
        this._y=y;
    }
    /**
     * 
     * @param {string} objText 
     */
    static create(objText){
        const obj =JSON.parse(objText);
        const x = Number(obj.x||0);
        const y = Number(obj.y||0);
        const gaugeType =String(obj.gaugeType);
        const width =Number(obj.width);
        const color =parseColorNumber(obj.color);
        const textureName =String(obj.texture||"");
        return new AutoGaugeBehavior(x,y,width,textureName,color,gaugeType);
    }
    isVisible(){
        return this._type;
    }
    loadBitmap(){
        return ImageManager.loadSystem(this._textuerName);
    }

    width(){
        return this._width;
    }
    /**
     * @returns {number}
     */
    color(){
        return LogManager.option().getGaugeColor();
    }
    /**
     * @private
     * @returns {AutoGaugeSprite_Concept}
     */
    createSpriteWithType(){
        if(this._type==="none"){
            return null;
        }
        

    }
    /**
     * @param {number} radius 
     * @returns {AutoGaugeSprite_Concept}
     */
    createSprite(radius){
        if(this._type==="none"){
            return null;
        }
        const sprite = new Sprite_AutoGaugeCircle(radius);
        sprite.x = this._x;
        sprite.y = this._y;
        if(this._textuerName){
            const bitmap = this.loadBitmap();
            sprite.setupBitmapStyle(bitmap,this._width,this.color());
        }else{
            sprite.lineStyle(this._width,this.color());
        }
        return sprite;
    }
}

class VoiceRepeatBehavior extends Button_BehaviorBase{
    callOkHandler(){
        this.logManager().repeatLastVoice();
    }
    isEnabled(){
        return this.logManager().hasLastVoice();
    }
    isTriggered(){
        const symbol = this.symbol();
        return symbol && Input.isTriggered(symbol);
    }
}

class Button_Behavior_Setting{
    /**
     * @param {ReadonlyArray<Button_BehaviorBase>} list 
     * @param {Number} offsetX 
     * @param {Number} offsetY
     * @param {String} overlapType
     * @param {Number} disableSwitch
     * @param {Number} windowStretchY
     */
    constructor(list,offsetX,offsetY,overlapType,disableSwitch,windowStretchY){
        this._list=list;
        this._offsetX=offsetX;
        this._offsetY=offsetY;
        this._overlapType=overlapType;
        this._windowStretchY=windowStretchY;
        this._disableSwitch=disableSwitch;
    }
    /**
     * 
     * @param {String} objText 
     * @param {ReadonlyArray<Button_BehaviorBase>} list 
     */
    static create(objText,list){
        if(objText){
            const obj=JSON.parse(objText);

            const offsetX =Number(obj.offsetX);
            const offsetY =Number(obj.offsetY);
            const overlapType =obj.windowOverlap;
            const windowStretchY =Number(obj.windowStretchY);
            const disableSwitch =Number(obj.disableSwitch||0);
    
            return new Button_Behavior_Setting(list,offsetX,offsetY,overlapType,disableSwitch,windowStretchY);    
        }
        //プラグインパラメータの入力間違いで問題があったので、プラグイン側でデフォルト値を持つ
        return new Button_Behavior_Setting(list,0,0,"inside",0,0);
 
    }
    /**
     * @description 全ボタンの非表示
     * @returns 
     */
    isButtonDisabled(){
        return $gameSwitches.value(this._disableSwitch);
    }
    createSpriteList(){
        return this._list.map(b=>{
            return b.createSprite()
        } );
    }

    initSwitchs(){

        for (const iterator of this._list) {
            $gameSwitches.setValue( iterator.layout().switchId(),true );
        }
    }
    updateInput(){
        //ボタン全て非表示の場合は無効化
        if(this.isButtonDisabled()){
            return;
        }

        for (const iterator of this._list) {
            if(SceneManager.isSceneChanging()){
                return;
            }
            iterator.updateInput();
        }
    }
    offsetX(){
        return this._offsetX;
    }
    offsetY(){
        return this._offsetY;
    }
    isInside(){
        return this._overlapType ==="inside";
    }
    isAbsolute(){
        return this._overlapType==="absolute";
    }

    createOutside(){
        return new ButtonGroop_Outside(this);
    }

}

class VoiceContainer{
    /**
     * @param {String} voiceDir
     * @param {ReadonlyArray<String>} voiceList 
     */
    constructor(voiceDir,voiceList){
        this._voiceReg= new RegExp("^"+voiceDir+".+")
        /**
         * @type {Set<String>}
         */
        this._voiceList=new Set(voiceList);
    }
    /**
     * @param {String} objText 
     * @param {string[]} v2FileList
     * @param {string} v2Regex
     */
    static create(objText,v2FileList,v2Regex){
        const obj=JSON.parse(objText);

        const dir=obj.regex;
        const files =JSON.parse(obj.files);
        const container= new VoiceContainer(v2Regex || dir,files);
        container.#addVoice(v2FileList);
        return container
    }
    /**
     * 
     * @param {ReadonlyArray<string>} voiceFileNames 
     */
    #addVoice(voiceFileNames){
        //プラグインパラメータ移行に伴う処置
        for (const iterator of voiceFileNames) {
            this._voiceList.add(iterator);
        }
    }

    /**
     * @param {String} fileName 
     * @returns 
     */
    isVoiceFile(fileName){
        if(this._voiceReg.test(fileName)){
            return true;
        }
        return this._voiceList.has(fileName);
    }
}

class MatchinakaProxy{
    /**
     * @param {LogList} logList 
     */
    constructor(logList){
        this._list=logList;
        this._title = logList.createTitle();
        /**
         * @type {ReadonlyArray<MultiLineItemConcept>}
         */
        this._cache = null;
    }
    makeCache(){
        if(!this._cache){
            this._cache = this._list.createProxy();
        }
    }
    title(){
        return this._title;
    }
    multiLineItems(){
        if(this._cache){
            return this._cache;
        }
        return [];
    }
    isItemEnalbled(){
        return !this._list.isEmpty();
    }

}

class Window_MachinakaSelect extends Window_Selectable{

    /**
     * @param {Rectangle} rect 
     */
    constructor(rect){
        super(rect);
        this.setLogListWindow(null);
        this.setList([]);
    }
    /**
     * @param {ReadonlyArray<LogList>} logListArray 
     */
    setList(logListArray){
        this._list=logListArray.map( (log)=>new MatchinakaProxy(log));
        this.refresh();
    }
    maxItems(){
        return this._list.length;
    }
    /**
     * @param {number} index 
     */
    drawItem(index){
        const item = this.itemAt(index);
        if(item){            
            const rect =this.itemRectWithPadding(index);
            this.drawTextEx(item.title(),rect.x,rect.y,rect.width);
        }
    }
    callUpdateHelp(){
        super.callUpdateHelp();
        if(this._logListWindow){
            const item = this.currentItem();
            if(item){
                item.makeCache();
                this._logListWindow.setList(item.multiLineItems());    
            }else{
                this._logListWindow.setList([]);
            }
        }
    }
    /**
     * @param {{setList:(list:ReadonlyArray<MultiLineItemConcept>)=>void}} logListWindow 
     */
    setLogListWindow(logListWindow){
        this._logListWindow=logListWindow;
    }
    /**
     * @param {number} index 
     * @returns 
     */
    itemAt(index){
        return this._list[index];
    }
    currentItem(){
        return this.itemAt(this.index());
    }
    isCurrentItemEnabled(){
        const item= this.currentItem();
        if(item && item.isItemEnalbled()){
            return true;
        }
        return false;
    }
    selectLastItem(){
        const items = this.maxItems();
        LogManager.getWorkaround().forceSelect(this,items-1);
    }
}

class Window_MultiLine extends Window_Selectable{
    /**
     * @param {Rectangle} rect 
     */
    initialize(rect){
        //@ts-ignore
        window_initializeMVMZ(this,rect,super.initialize);
    }
    /**
     * @param {Number} _index 
     * @returns {Number}
     */
    numItemLines(_index){
        return 1;
    }
    /**
     * @param {Number} _index 
     * @returns 
     */
    lineText(_index){
        return "";
    }
    /**
     * @param {Number} index 
     * @returns 
     */
    topItemIndex(index){
        return index;
    }

    /**
     * @param {number} index 
     * @returns 
     */
    bottomItemIndex(index){
        const top = this.topItemIndex(index);
        const lines = this.numItemLines(index);
        return top + lines -1;
    }
    makeStateText(){
        return `index:${this.index()}\nscroll:${this._scrollY}`;
    }
    updateHelp(){
        this._helpWindow.setText(this.makeStateText());
    }
    /**
     * @param {Number} index 
     * @returns 
     */
    baseRect(index){
        return super.itemRect(index);
    }

    nextTopIndex(){
        const currentIndex = Math.max(0, this.index());
        const top = this.topItemIndex(currentIndex);
        const maxItems = this.maxItems();
        const nextIndex = (top + maxItems-1) % maxItems;
        const nextTop = this.topItemIndex(nextIndex);
        if(nextTop > currentIndex){
            return this.bottomItemIndex(nextTop);
        }
        return nextTop;
    }
    /**
     * @param {boolean} wrap 
     */
    cursorUp(wrap){
        const nextTop = this.nextTopIndex();
        this.smoothSelect(nextTop);
    }
    nextBottomIndex(){
        const currentIndex = Math.max(0, this.index());
        const bottom = this.bottomItemIndex(currentIndex);
        const maxItems = this.maxItems();
        const nextIndex = (bottom + maxItems+1) % maxItems;
        const nextBottom = this.bottomItemIndex(nextIndex);
        if(nextBottom >currentIndex){
            return nextBottom;
        }
        return this.topItemIndex(nextBottom);
    }
    /**
     * 
     * @param {boolean} wrap 
     */
    cursorDown(wrap){
        const newIndex = this.nextBottomIndex();
        this.smoothSelect(newIndex);
    }
    topLineIndex(){
        return this.topIndex();
    }
    bottomLineIndex(){
        const top = this.topIndex();
        const maxRows = this.maxPageRows();
        return top + maxRows;
    }
    /**
     * @param {number} line 
     * @returns 
     */
    calcRectHeight(line){
        const lineHeight = this.lineHeight();
        const rowSpacing = this.rowSpacing()*2;
        return (lineHeight + rowSpacing) * line;
    }

    /**
     * @param {Number} index 
     * @returns 
     */
    itemRect(index){
        const topIndex = this.topItemIndex(index);
        const baseRect = this.baseRect(topIndex);
        const numLines = this.numItemLines(topIndex);
        const extraHeight = this.calcRectHeight(numLines-1);
        baseRect.height += extraHeight;
        return baseRect;
    }
    /**
     * @param {Number} index 
     * @returns 
     */
    itemRectForText(index){
        const baseRect =this.baseRect(index);
        //baseRect.y +=8;
        const padding = this.itemPadding();
        baseRect.x += padding;
        baseRect.width -= padding *2;
        //baseRect.height -=8
        return baseRect;
    }
    // /**
    //  * 
    //  * @param {number} topItemIndex 
    //  */
    // backgroundRect(topItemIndex){


    // }
    // /**
    //  * @param {Number} index 
    //  * @returns 
    //  */
    needsDrawBackground(index){
        const top = this.topItemIndex(index);
        return top ===index;
    }
    /**
     * @param {Number} index 
     */
    drawItemBackground(index){
        if(this.needsDrawBackground(index)){
            super.drawItemBackground(index);
        }
    }
    /**
     * @param {Number} index 
     */
    drawItem(index){
        const text = this.lineText(index);
        if(text){
            const rect = this.itemRectForText(index);
            this.drawTextEx(text,rect.x,rect.y,rect.width);
        }
    }
}

/**
 * @template {MultiLineItemConcept} ItemType
 */
class Window_MultiLine_ProxyType extends Window_MultiLine{
    /**
     * @param {ReadonlyArray<ItemType>} list 
     */
    setList(list){
        this._list=list;
    }
    clear(){
        this.setList([]);
    }
    maxItems(){
        return this._list.length;
    }
    /**
     * @returns {ItemType}
     * @param {number} index 
     */
    itemAt(index){
        return this._list[index];
    }
    currentItem(){
        return this.itemAt(this.index());
    }
    /**
     * @param {Number} index 
     * @returns 
     */
    topItemIndex(index){
        const item = this.itemAt(index);
        if(item){
            return index -item.lineIndex();
        }
        return index;
    }
    /**
     * @param {number} index 
     * @returns {boolean}
     */
    needsDrawBackground(index){
        const item = this.itemAt(index);
        if(item){
            return item.needsDrawBackground();
        }
        return false;
    }
    /**
     * @param {number} index 
     * @returns 
     */
    lineText(index){
        const item =this.itemAt(index);
        if(item){
            return item.text();
        }
        return "";
    }
    /**
     * 
     * @param {number} index 
     * @returns 
     */
    numItemLines(index){
        const item = this.itemAt(index);
        if(item){
            return item.numItemLines();
        }
        return 1;
    }
    //メモ:rowSpacingを変更すると、表示がかなり狂う
    //おそらく変更できない
    rowSpacing(){
        return 0;
    }
    lineSpacing(){
        return 4;
    }
    lineHeight(){
        return super.lineHeight()+this.lineSpacing();
    }
    itemHeight(){
        return this.lineHeight();
    }


}

/**
 * @extends {Window_MultiLine_ProxyType<MultiLineItemConcept>}
 */
class Window_MessageLog extends Window_MultiLine_ProxyType{
    initialize(rect){
        this._style = LogManager.readOnryData().logStyle();
        super.initialize(rect);
        this.makeItemList();
    }
    loadWindowskin(){
        const style = LogManager.readOnryData().logStyle();
        const skin = style.windowSkinBitmap();
        if(skin){
            this.windowskin=skin;
        }else{
            super.loadWindowskin();
        }

    }
    lineSpacing(){
        return this._style.itemStyle().lineSpacing;
    }
    makeItemList(){
        const list = LogManager.createProxy();
        this.setList(list);
    }
    /**
     * @param {ReadonlyArray<MultiLineItemConcept>} list 
     */
    setList(list){
        super.setList(list);
        for (const iterator of list) {
            if(iterator){
                ImageManager.loadFace(iterator.faceName());
            }
        }
        this.refresh();
    }


    playBuzzerSound(){}
    playOkSound(){}

    selectlLastItem(){
        const index=this.maxItems()-1;
        LogManager.getWorkaround().forceSelect(this,index);
    }
    faceWidth(){
        return ImageManager.faceWidth;
    }
    faceHeight(){
        return ImageManager.faceHeight;
    }
    /**
     * @param {Number} index 
     * @returns 
     */
    faceRect(index){
        const item =this.itemAt(index);
        const indexOffset= item ? item.lineIndex() :0;
        const rect = this.baseRect(index-indexOffset);
        const faceHeight = this.faceHeight();
        const lineSpacing = this.lineSpacing();
        const offsetY = Math.min(lineSpacing*2,4);

        rect.width = this.faceWidth();
        rect.height = faceHeight;

        rect.x += this.colSpacing();
        rect.y +=offsetY;
        return rect;
    }

    /**
     * @private
     * @param {number} index 
     * @returns {boolean}
     */
    needsDrawFace(index){
        const item = this.itemAt(index);
        if(!item.faceName()){
            return false;
        }
        if(item.isTopItem()){
            return true;
        }
        const topIndex=this.topIndex();
        if(index===topIndex){
            return true;
        }
        return false;

    }    
    callDrawFace(index){
        if(this.needsDrawFace(index)){
            const rect = this.faceRect(index);
            const item = this.itemAt(index);
            this.drawFace(item.faceName(),item.faceIndex(),rect.x,rect.y,rect.width,rect.height);    
        }
    }


    iconWidth(){
        return ImageManager.iconWidth;
    }
    /**
     * 
     * @param {MultiLineItemConcept} item 
     * @param {Rectangle} rect 
     */
    callDrawIcon(item,rect){
        const x = item.isIconLeft() ? rect.x:(rect.x +rect.width - this.iconWidth());
        this.drawIcon(this._style.voiceIcon(),x,rect.y);
    

    }
    /**
     * @param {Number} index 
     */
     drawItem(index){
        //this.drawItemFrame(index);
        this.callDrawFace(index);
        const item = this.itemAt(index);
        if(!item){return}
        const rect = this.itemRectForText(index);
        // if( this.topIndex() ===index || item.needsDrawBackground()  ){
        //     this.callDrawBackground(index);
        // }
        if(item.needsVoiceIconRender()){
            this.callDrawIcon(item,rect);
            if(item.isIconLeft()){
                const iconWidth =this.iconWidth();
                this.drawTextEx(item.text(),rect.x + iconWidth,rect.y,rect.width - iconWidth);
                return;
            }
        }
        //this.drawVoiceIcon(index);
        super.drawItem(index);
    }
    /**
     * @param {number} index 
     * @returns 
     */
     itemRectForText(index){
        const rect = super.itemRectForText(index);
        const item = this.itemAt(index);
        if(!!item.faceName()){
            const fw = this.faceWidth();
            rect.width -=fw;
            rect.x += fw+ this.colSpacing();
        }
        return rect;
    }
}
class MulitLangString{
    /**
     * @param {String} ja_ 
     * @param {String} en_
     * @param {String} zh_ 
     * @param {String} ko_ 
     * @param {String} de_ 
     * @param {String} ru_ 
     */
    constructor(ja_,en_,zh_,ko_,de_,ru_){
        this._table={
            ja:ja_,
            en:en_,
            zh:zh_,
            ko:ko_,
            de:de_,
            ru:ru_,
        }

        this.refreshLang("en_US");
        MultiLangManager.add(this);
    }
    /**
     * @param {string} objText 
     * @returns 
     */
    static create(objText){
        if(objText){
            const obj =JSON.parse(objText);
            return new MulitLangString(
                obj.ja_JP,obj.en_US,obj.zh_CN,obj.ko_KR,obj.de_DE,obj.ru_RU
            );    
        }
        return new MulitLangString("","","","","","");
    }
    text(){
        return this._currnet;
    }
    /**
     * @param {String} locale 
     * @returns 
     */
    getCurrentText(locale){
        if(locale.match(/^ja/)){
            return this._table.ja;
        }
        if(locale.match(/^en/)){
            return this._table.en;
        }
        if(locale.match(/^de/)){
            return this._table.de;
        }
        if(locale.match(/^zh/)){
            return this._table.zh;
        }
        return this._table.en;
    }
    /**
     * @param {string} locale 
     */
    refreshLang(locale){
        this._currnet=this.getCurrentText(locale);
    }

}

class MultiLangManager_T{
    constructor(){
        /**
         * @type {MulitLangString[]}
         */
        this._list=[];
        //this.setLocal("en_US");
    }

    currentLocal(){
        return $dataSystem.locale;
    }
    /**
     * @param {MulitLangString} mText 
     */
    add(mText){
        this._list.push(mText);
    }
    refresh(){
        const local=this.currentLocal();
        for (const iterator of this._list) {
            iterator.refreshLang(local);
        }

    }
    /**
     * 
     * @param {String} locale 
     */
    setLocal(locale){
        return;
 //       $dataSystem.locale=locale;
//        this._currentLocal=local;
    }
}
const MultiLangManager = new MultiLangManager_T();


class AutoSpeed{
    /**
     * @param {Number} speed 
     * @param {MulitLangString} mText 
     */
    constructor(speed,mText){
        this._speed=speed;
        this._text=mText;
    }
    text(){
        return this._text.text();
    }
}

class AutoReadonry{
    /**
     * @param {ReadonlyArray<AutoSpeed>} autoSpeedList 
     * @param {*} configText 
     */
    constructor(autoSpeedList,configText){
        this._autoSpeedList =autoSpeedList;
    }
    time(){
        return 320;
    }

}


class AutoTemporary {

    constructor(){
        this.clear();
        this._enabled=false;
    }
    /**
     * @param {boolean} value 
     */
    setAutoMode(value){
        this._enabled=value;
    }
    getAutoMode(){
        return this._enabled;
    }
    flipAutoMode(){
        this.setAutoMode(!this._enabled);
    }
    clear(){
        this.setLastVoiceBuffer(null)
        this.resetTimer();
    }
    resetTimer(){
        this._currentTime=0;
    }
    timerValue(){
        return this._currentTime;
    }
    isAutoMode(){
        return this._enabled;
    }
    /**
     * @param {number} t 
     */
    updateAutoTime(t){
        if(this._enabled){
            this._currentTime +=t;
        }
    }

    onStartMessage(){
        this.resetTimer();
    }
    stopVoice(){
        if(this._seBuffer&& this._seBuffer.isPlaying()){
            this._seBuffer.stop();
            this._seBuffer=null;
        }
    }

    onTerminateMessage(){
        this.stopVoice();
        this.resetTimer();
    }
    onVoiceStop(){
        this._seBuffer=null;
    }

    /**
     * @param {WebAudio} seBuffer 
     */
    setLastVoiceBuffer(seBuffer){
        this._seBuffer = seBuffer;
        if(seBuffer){
            seBuffer.addStopListener(this.onVoiceStop.bind(this) );
        }
    }

    isVoiceStoped(){
        return !this._seBuffer;
    }
    /**
     * 
     * @param {number} maxTime 
     */
    timeRate(maxTime){
        const rate = this._currentTime /maxTime;
        return Math.min(1,rate);
    }
    /**
     * 
     * @param {Number} autoTimeMax タイマーがこの値を超えたら、次に進む
     * @returns 
     */
    needsNextMessage(autoTimeMax){
        if(this.isVoiceStoped()){
            const rate= this.timeRate(autoTimeMax);
            if(rate >=1){
                return true;
            }
        }
        return false;
    }

}

class ChoiceStyle{
    /**
     * @param {string} format 
     * @param {MulitLangString} text 
     * @param {MulitLangString} cancelText
     */
    constructor(format,text,cancelText){
        this._format=format;
        this._choiceText = text;
        this._cancelText=cancelText;
    }
    /**
     * @param {String} baseText 
     * @returns {String}
     */
    createChoiceItemText(baseText){
        const choiceText =this._choiceText.text();
        //@ts-ignore
        return this._format.format(choiceText,baseText);
    }
    cancelText(){
        return this._cancelText.text();
    }
}


class LogStyle{
    /**
     * @param {Readonly<LogItemStyle>} logItemStyle
     * @param {String} windowFileName 
     * @param {Readonly<Rectangle>} windowRect
     * @param {String} backgroundFileName
     * @param {String} frameRectColor
     * @param {Readonly<Rectangle>} machinakaRect
     * @param {ChoiceStyle} choice
     */
    constructor(logItemStyle,windowFileName,windowRect,backgroundFileName ,frameRectColor,machinakaRect,choice){
        this._itemstyle=logItemStyle;
        this._windowFileName=windowFileName || null;
        this._background =backgroundFileName||null;
        this._frameColor=frameRectColor||null;
        this._windowRect=windowRect;
        this._machinakaRect =machinakaRect;
        this._choiceStyle =choice;
    }
    /**
     * @param {string} objText 
     * @param {{
     *    choice:ChoiceStyle
     * }} ex
     * @returns 
     */
    static create(objText,ex){
        const obj =JSON.parse(objText);


        const windowFileName = obj.windowFileName;
        const speakerName = obj.speakerName;
        const back = (obj.background)
        const voiceIcon =Number(obj.voiceIconIndex);
        //const faceLines =Number(obj.faceLines||4);
        const machinakaRect = obj.machinakaRect ? parseRect(obj.obj.machinakaRect) :null;
        const windowRect =obj.windowRect? parseRect(obj.windowRect):null;
        const frameColor= (obj.freamColor)||null;
        const lineSpacing =Number(obj.lineSpacing||2)



        /**
         * @type {LogItemStyle}
         */
        const ex3 ={
            voiceIcon:voiceIcon,
            numFaceLines:4,
            lineSpacing:lineSpacing,
            speakerNameFormat:speakerName,
        };
        return new LogStyle(ex3,windowFileName,windowRect,back,frameColor,machinakaRect,ex.choice);
    }
    voiceIcon(){
        return this._itemstyle.voiceIcon;
    }
    itemStyle(){
        return this._itemstyle;
    }
    logWindowRect(){
        if(this._windowRect){
            return this._windowRect.clone();
        }
        return null;
    }
    backgroundBitmap(){
        if(this._background){
            return ImageManager.loadSystem(this._background);
        }
        return null;
    }
    windowSkinBitmap(){
        if(this._windowFileName){
            return ImageManager.loadSystem(this._windowFileName);
        }
        return null;
    }
    itemPadding(){
        return 8;
    }
    speakerNameFormat(){
        return this._itemstyle.speakerNameFormat;
    }

    /**
     * @param {String} baseText 
     * @returns {String}
     */
    createChoiceItemText(baseText){
        return this._choiceStyle.createChoiceItemText(baseText);
    }
    cancelText(){
        const baseText=this._choiceStyle.cancelText();
        return this.createChoiceItemText(baseText);
    }
    itemFrameThickness(){
        return 4;
    }

    itemFrameColor(){
        return this._frameColor
    }
}
/**
 * @typedef {object} MenuCommandParam
 * @property {MulitLangString} commandName
 * @property {string} symbol
 */

class LogSwtichs{
    /**
     * 
     * @param {number} logWrite 
     * @param {number} matinakaMode 
     * @param {number} skipMode
     */
    constructor(logWrite,matinakaMode,skipMode){
        this._logWrite=logWrite;
        this._macinakaMode=matinakaMode;
        this._skipMode =skipMode;

    }
    initSwitchs(){
        for (const iterator of [this._logWrite,this._macinakaMode]) {
            $gameSwitches.setValue(iterator,true);
        }
    }
    isMacinakaMode(){
        return this.readSwitch(this._macinakaMode);
    }
    isLogWriteEnabled(){
        return this.readSwitch(this._logWrite);
    }

    isSkipMode(){
        return $gameSwitches.value(this._skipMode);
    }
    changeSkipMode(){
        const value = this.isSkipMode();
        $gameSwitches.setValue(this._skipMode,!value);
    }
    stopSkipMode(){
        $gameSwitches.setValue(this._skipMode,false);
    }

    /**
     * @param {number} switchId 
     */
    readSwitch(switchId){
        if(switchId <=0){
            return true;
        }
        return $gameSwitches.value(switchId);

    }
}


class MachinakMode{
    /**
     * @param {number} switchId 
     * @param {boolean} defaultState 
     */
    constructor(switchId,defaultState){
        this._swicthId=switchId;
        this._defaultState=defaultState;
    }
    initSwitch(){
        $gameSwitches.setValue(this._swicthId,this._defaultState);
    }
    isEnabled(){
        if(this._swicthId > 0){
            return $gameSwitches.value(this._swicthId);
        }
        return this._defaultState;
    }
}

class LogReadOnlryData{

    /**
     * @param {VoiceContainer} voice
     * @param {Readonly<LogStyle>} logStyle
     * @param {AutoReadonry} auto_
     * @param {LogSwtichs} logSwitch
     * @param {Boolean} logWriteOnBattle
     * @param {ManoUZ_LogOptionParametor} defaultOption
     * @param {MenuCommandParam} menuCommand
     * @param {MachinakMode} macinakaMode
     */
    constructor(voice,logStyle,auto_,logSwitch,logWriteOnBattle,defaultOption,menuCommand,macinakaMode){
        this._logSwitch=logSwitch;
        this._voice=voice;
        this._logWriteOnBattle=logWriteOnBattle;
        this._logStyle =logStyle;
        this._auto=auto_;
        this._defaultOption =defaultOption;
        this._menuCommand =menuCommand;
        this._machinakaMode=macinakaMode;
    }
    waittimeOnGaugeMax(){
        return 20;
    }

    switchs(){
        return this._logSwitch;
    }
    initSwitchs(){

        //this._behaviorList.initSwitchs();
        this._logSwitch.initSwitchs();
        this._machinakaMode.initSwitch();
    }
    logStyle(){
        return this._logStyle;
    }

    /**
     * @param {String} baseText 
     * @returns 
     */
    createChoiceItemText(baseText){
        return this._logStyle.createChoiceItemText(baseText);
    }
    autoTime(){
        return this._auto.time();
    }
    // buttonSpace(){
    //     return this._behaviorList.space();
    // }
    isLogWriteEnabled(){
        if(!this._logWriteOnBattle){
            if($gameParty.inBattle()){
                return false;
            }
        }
        return this._logSwitch.isLogWriteEnabled();
    }

    isMachinakaModeEnabled(){
        return this._machinakaMode.isEnabled();
    }

    canSave(){
        return true;
    }
    /**
     * @param {rm.types.AudioParameters} audio 
     * @returns 
     */
    isVoiceFile(audio){
        if(audio){
            return this._voice.isVoiceFile(audio.name);
        }
        return false;
    }
    /**
     * @param {ManoUZ_LogOptionParametor} option 
     * @returns {ManoUZ_LogOptionParametor}
     */
    optionNormalize(option){
        /**
         * @type {ManoUZ_LogOptionParametor}
         */
        const result={
            keepingAutoMode:(!!option.keepingAutoMode),
            autoWaitTime:vaildateNumber(option.autoWaitTime,this._defaultOption.autoWaitTime),
            logLimitBasic: vaildateNumber(option.logLimitBasic,this._defaultOption.logLimitBasic),
            logLimitExtends:vaildateNumber(option.logLimitExtends,this._defaultOption.logLimitExtends),
            gaugeColor:vaildateNumber(option.gaugeColor,this._defaultOption.gaugeColor),
            machinakaLines:vaildateNumber(option.machinakaLines,this._defaultOption.machinakaLines),
        };
        return result;
    }
    cloneDefaultOption(){
        return this.optionNormalize(this._defaultOption);
    }
    menuCommand(){
        return this._menuCommand;
    }

}
/**
 * @param {Number} value 
 * @param {Number} default_ 
 */
function vaildateNumber(value,default_){
    //isNaN(undefined)=>true
    //Number.isNaN(undefined)=>false
    //なので、このisNaNが正しい。
    return isNaN(value) ? default_:Number(value);
}
//メッセージを保存するための一時領域
//開く・閉じるの処理が必要なのでstreamとした
class MessageStream{
    constructor(){
        this.clear();
    }
    clear(){
        this._textState =null;
        this._logItem =null;
        this._se =null;
    }
    /**
     * @param {number} num
     */
    addLines(num){
        if(this._logItem){
            this._logItem.addLines(num);
        }
    }

    /**
     * @param {string} spkName 
     * @param {string} text 
     * @param {string} facename 
     * @param {number} faceindex 
     */
    setText(spkName,text,facename,faceindex){
        if(this._logItem){
            this._logItem.writeText(spkName,text,facename,faceindex,this._se);
        }
    }
    getLogItem(){
        return this._logItem;

    }
    /**
     * @param {UZ_AudioFileParam} se 
     */
    setLastVoice(se){
        if(!se){
            this._se=null;
            return;
        }
        /**
         * @type {UZ_AudioFileParam}
         */
        const copy ={
            name:se.name,
            pan:se.pan,
            pitch:se.pitch,
            pos:(se.pos||0),
            volume:se.volume,
        };
        this._se=copy;
    }
    hasVoice(){
        return !!this._se;
    }
    clearTextItem(){

    }
    isOpen(){
        return !!this._logItem;
    }
    close(){
        this.clear();
    }
    open(){
        this._logItem = new  MultiLine_Message();
    }
}


class LogTemporaly{
    constructor(){
        this.setBattleMode(false);
        this.setShowFast(false);
        this.setParallelMode(false);
        this.setTextTerminate(false);
        this.setScrollIndex(NaN)
    }
    /**
     * @param {Number} index 
     */
    setScrollIndex(index){
        this._scrollIndex = index;
    }
    /**
     * @param {Boolean} value 
     */
    setTextTerminate(value){
        this._textTerminate = !!value;
    }
    /**
     * @param {Boolean} value 
     */
    setParallelMode(value){
        this._parallelMode=value;
        this._parallelInputLock=true;
    }
    canSave(){
        return !this._parallelMode;
    }
    /**
     * @param {boolean} value 
     */
    setShowFast(value){
        this._needsShowFast=!!value;
    }
    needsShowFast(){
        return this._needsShowFast;
    }
    /**
     * 
     * @param {Boolean} value 
     */
    setBattleMode(value){
        this._battleMode=value;
    }
    isBattleMode(){
        return this._battleMode;
    }

}

class OptionCasset{
    /**
     * 
     * @param {ManoUZ_LogOptionParametor} option 
     */
    constructor(option){
        this.setOptionValue(option);
    }
    logSizeRed() {
        return this._value.logLimitExtends;
    }
    logSizeYellow() {
        return this._value.logLimitBasic;
    }
    quickSaveFileName() {
        return this.defaultQuickSaveFileName();
    }
    defaultQuickSaveFileName(){
        return "logquicksave";
    }
    /**
     * @description データの不足が無いように気を付けること
     * @param {ManoUZ_LogOptionParametor} value 
     */
    setOptionValue(value){
        this._value=value;
    }

    /**
     * @param {number} size 
     */
    setLogLimitBasic(size){
        if(!isNaN(size)){
            this._value.logLimitExtends = Math.max(10,size);
        }
    }
    getLogLimitSizeBasic(){
        return this._value.logLimitBasic;
    }
    /**
     * @param {number} size 
     */
    setLogLimitExtends(size){
        if(!isNaN(size)){
            this._value.logLimitExtends = Math.max(4,size);
        }
    }
    getLogLimitSizeExtends(){
        return this._value.logLimitExtends;
    }
    getAutoWaitTime(){
        return this._value.autoWaitTime;
    }
    /**
     * @param {number} time 
     */
    setAutoWaitTime(time){
        if(!Number.isNaN(time)){
            this._value.autoWaitTime = Math.max(10,time);
        }
    }
    isKeepingAutoMode(){
        return !!this._value.keepingAutoMode;
    }
    getGaugeColor() {
        return this._value.gaugeColor;
    }
    machinakaLines(){
        return this._value.machinakaLines;
    }
    /**
     * @returns {ManoUZ_LogOptionParametor} 
     */
    cloneValue(){
        return {
            keepingAutoMode:this._value.keepingAutoMode,
            logLimitBasic : this._value.logLimitBasic,
            logLimitExtends : this._value.logLimitExtends,
            autoWaitTime:this._value.autoWaitTime,
            gaugeColor:this._value.gaugeColor,
            machinakaLines:this._value.machinakaLines,
        }
    }
}

class AudioErrorXXX{
    constructor(){
        /**
         * @type {Set<string>}
         */
        this._errorFiles =new Set();
    }
    /**
     * 
     * @param {string} filename 
     */
    isVoiceFile(filename){

    }
    
}

/**
 * 
 * @param {number} srcMapId 
 * @returns 
 */
function getRootMapId(srcMapId){
    let mapId=srcMapId;
    for(let i=0;i < 100; ++i){
        const nextMap =$dataMapInfos[mapId];
        if(!nextMap){
            return mapId;
        }
        mapId =nextMap.parentId;
    }
    return 0;
}


class MaptreeMemory{
    constructor(){
        this.setRootMap(0);
    }
    /**
     * @param {number} mapId 
     */
    setRootMap(mapId){
        this._rootMapId=mapId;
    }

    onMapTransfer(){



    }
}

/**
 * 
 * @returns {Game_Interpreter}
 */
function getRunningInterpriter(){
    let inter =$gameMap._interpreter;
    for(let i=0; i< 1010;++i){
        if(!inter._childInterpreter){
            return inter;
        }
        inter = inter._childInterpreter;
    }
    return null;
}

class LogManager_T {
    pluginVersion(){
        return PLUGIN_VERSION;
    }
    /**
     * @param {LogManager_MVMZ_workaround} mvmz 
     * @param {LogReadOnlryData} readOnryData
     * @param {Button_Behavior_Setting} buttonBehaviorList
     */
    constructor(mvmz,readOnryData ,buttonBehaviorList){
        this.setWorkaround(mvmz);
        /**
         * @private
         */
        this._contents =null;
        /**
         * @private
         */
        this._autoTemporary = new AutoTemporary();
        this.resetTemporaly();
        /**
         * @private
         */
         this._readOnlyData = readOnryData;
        /**
         * @private
         */
         this._option = new OptionCasset(readOnryData.cloneDefaultOption());
         this._buttons = buttonBehaviorList

    }


    /**
     * @param {WebAudio} webAudio 
     */
    onAudioLoadError(webAudio){

    }
    /**
     * @param {ManoUZ_LogOptionParametor} option 
     */
    setOptionValue(option){
        if(option){
            //外部からくるデータなので、きれいにする
            const normalized =this._readOnlyData.optionNormalize(option);
            console.log(normalized);
            this._option.setOptionValue(normalized);
        }else{
            this._option.setOptionValue( this._readOnlyData.cloneDefaultOption() );
        }
    }
    /**
     * @returns {ManoUZ_LogOptionParametor}
     */
    cloneOptionValue(){
        const value =  this._option.cloneValue();
        return this._readOnlyData.optionNormalize(value);
    }
    
    option(){
        return this._option;
    }
    openOptionScene(){
//        this._option.
    }
    /**
     * @returns 
     */
    getLogItemStyle(){
        return this.readOnryData().logStyle().itemStyle();
    }
    /**
     * 
     * @param {LogManager_MVMZ_workaround} mvmz 
     */
    setWorkaround(mvmz){
        this._workaround=mvmz;
    }
    /**
     * @returns 
     */
    getWorkaround(){
        return this._workaround;
    }

    /**
     * @returns {ReadonlyArray<LogList>}
     */
    createMachinakaList(){
        return this.getContents().createMachinakaList();
    }
    /**
     * @param {string} title 
     */
    setMachinakaTitle(title){
        const contents = this.getContents();
        //現在書き込み中のログにタイトルを埋め込む

    }
    /**
     * 
     * @param {Game_Event} mapEvent 
     */
    nameIsMeetsTitle(mapEvent){
        const name =mapEvent.event().name;
        //名前が空欄
        if(!name){
            return false;
        }
        //自動生成された名前が付いている
        if(name.match(/^EV\d{1,3}/)){
            return false;
        }
        return true;
    }
    createEmptySaveContents(){
        return new Log_ContentsObject(this.pluginVersion());
    }
    clearLog(){
        this.setContents(this.createEmptySaveContents());
    }
    clearOldData(){
        if(this._contents){
            const option = this.option();
            this._contents.checkLogSize(option.logSizeYellow(),option.logSizeRed())    
        }
    }
    /**
     * @param {Log_ContentsObject} c 
     */
    setContents(c){
        this._contents =c;
        this.makeSaveContents();
    }
    getLogSize(){
        return this._contents.logSize()
    }
    /**
     * 
     * @returns {LogSaveContentsConcept}
     */
    getContents(){
        return this._contents;
    }
    makeSaveContents(){
        if(!this._contents){
            this._contents =this.createEmptySaveContents();
        }
    }
    /**
     * @private
     * @param {Log_ContentsObject} msgLog 
     */
    tryReadingSaveContents(msgLog){
        //セーブデータが無い場合はエラー無しとして成功扱い
        //あとでデータを新規作成する
        if(!msgLog){
            return true;
        }
        try {
            //バージョンチェック＋型チェック
            //型が間違っているなら、例外に飛ぶ
            const version =msgLog.version();
            if(version !==this.pluginVersion()){
                //セーブデータのバージョンが違う
                //何が起こるか不明なので失敗にする
                return false;
            }
            this.setContents(msgLog);
        } catch (error) {
            console.error(error);
            console.error(msgLog);
            //読み込み中にエラーが出たのでfalse
            //ここでreturnするのはガード節の意味合いもある
            return false;
        }
        //実行位置に巻き戻し
        //こうしないと、読み込み時にメッセージがおかしくなる
        const lastCode = msgLog.lastCodeIndex();
        const inter = getRunningInterpriter();
        if(!isNaN(lastCode) && inter && inter.isRunning()){
            inter.jumpTo(lastCode);
        }
        return true;

    }
    readSaveContents(contents){
        this.resetTemporaly();
        //先に古いデータを消す 例外安全の発想で先に消すようにしている
        this._contents =null;
        /**
         * @type {Log_ContentsObject}
         */
        const msgLog= contents.messageLog;
        //エラーの起こりうる読み込みを行う
        const success =this.tryReadingSaveContents(msgLog);
        //読み込み失敗や、データが無い場合ここで初期化する
        this.makeSaveContents();
        if(!success){
            //失敗した場合に何かしたい時
        }
    }
    /**
     * @private
     */
    resetTemporaly(){
        //WebAudioBufferを確実に消すためにnullを埋めておく
        this._autoTemporary.setLastVoiceBuffer(null);
        this._temporaly = new LogTemporaly();
        this._messageStream=new MessageStream();
    }
    onNewGame(){
        this._buttons.initSwitchs();
        this._readOnlyData.initSwitchs();
        this.resetTemporaly();
        this.clearLog();
    }
    onBoot(){
    }
    /**
     * @param {String} labelName 
     * @param {number} depth //どのインタプリタでジャンプ先を指定したかを記録するため
     */
    onLabel(labelName,depth){
        //保存済みのラベルと￥一致した場合

        //保存してあるラベルを削除する
    }
    /**
     * @returns 
     */
    readOnryData(){
        return this._readOnlyData;
    }
    machinakaLines(){
        return( this._option.machinakaLines());
    }

    /**
     * 
     * @returns {ButtonGroop_Base}
     */
    createLogButtonSprites(){
        const b =this._buttons;
        if(b.isInside()){
            return new ButtonGroop_Inside(b);
        }
        if(b.isAbsolute()){
            return new ButtonGroop_Absolute(b);
        }
        return new ButtonGroop_Outside(b);
    }
    /**
     * @returns 
     */
    speakerNameFormat(){
        return this._readOnlyData.logStyle().speakerNameFormat();
        //  speakerNameFormat()
    }
    /**
     * @param {String} baseText 
     * @returns 
     */
    createChoiceItemText(baseText){
        return this._readOnlyData.createChoiceItemText(baseText);
    }
    /**
     * @param {Boolean} value 
     */
    setParallelMode(value){
        this._temporaly.setParallelMode(value)
    }
    canSave(){
        return $gameSystem.isSaveEnabled()&& this._temporaly.canSave() &&
             this._readOnlyData.canSave() ;
    }
    
    writeSaveContents(contents){
        contents.messageLog = this.getSaveContents();
    }

    /**
     * @param {SceneClass} scene 
     */
    pushScene(scene){
        //ツクール標準のシーンには戻った場合の処理を書くことは避けたい
        //よって、ここで遷移時の対応を行う
        SceneManager.push(scene);
        this._temporaly.setShowFast(true);
//        this._autoXXX.
        //this._temporaly.setAutoMode(false);
    }
    needsShowFast(){
        return this._temporaly.needsShowFast() ;
    }
    isInputEnabled(){
        if($gameMessage.isItemChoice()||$gameMessage.isNumberInput()){
            return false;
        }
        return true;
    }
    updateInput(){
        if(this.isInputEnabled()){
            this._buttons.updateInput();
            //this._readOnlyData.updateInput();
        }
    }
    GetAutoSpeed(){
        return 1;
    }
    startAutoTimer(){

    }
    endAutoTimer(){
        this._autoTemporary.clear();
    }
    autoGaugeRate(){
        return this._autoTemporary.timeRate(this._option.getAutoWaitTime());
    }
    updateAutoTimer(){
        this._autoTemporary.updateAutoTime(1);
        // if(this._temporaly.isTextAutoMode()){
        //     this._temporaly.updateAutoTime(this.autoSpeed());
        // }
    }
    isVoiceStoped(){
        return this._autoTemporary.isVoiceStoped();
    }
    /**
     * @description テキスト表示の終了が必要かを返す。オートモード用。
     */
    needsTextTerminate(){
        
        const timeMax = this._option.getAutoWaitTime();
        const waitingTime =this._readOnlyData.waittimeOnGaugeMax();
        return this._autoTemporary.needsNextMessage(timeMax + waitingTime);
        // if(this.isVoiceStoped()){
        //     return this._autoXXX.timerValue() > this._readOnlyData.autoTime();
        // }
        // return false;

        // return this.isVoiceStoped()
        //     && this._temporaly.autoTimerValue() > this._readOnlyData.autoTime();
    }
    /**
     * @description linebreak対応用関数
     * @param {number} lines 
     */
    addLines(lines){
        this._messageStream.addLines(lines);
    }
    onEnedOfText(){
        const log = this._messageStream.getLogItem();
        if(log){
            this.addItem(log);
        }
        this._messageStream.close();
    }
    /**
     * @param {string} spkName 
     * @param {string} text
     */
    startMessage(spkName,text){
        const facename =$gameMessage.faceName();
        const faceindex =$gameMessage.faceIndex();
        this._messageStream.setText(spkName,text,facename,faceindex);

        this._autoTemporary.onStartMessage();
        this._temporaly.setShowFast(false);
    }

    /**
     * @private
     * @param {ReadonlyArray<string>} texts 
     */
    addScrollItem(texts){
        const msgItem = new MultiLine_Message();
        msgItem.writeTextArray(texts);
        this._contents.addItem(msgItem);
    }

    /**
     * @param {string} lineText 
     */
    needsScrollFlash(lineText){
        if(!lineText){
            return true;
        }
        const mresult= lineText.match(/[\)\?\!\.。！？）]$/);
        return !!mresult;
    }
    /**
     * @param {ReadonlyArray<string>} texts 
     */
    startScrollTextMessage(texts){
        /**
         * @type {string[]}
         */
        const buffer =[];
        for (const iterator of texts) {
            if(iterator ){
                buffer.push(iterator);
            }
            if(buffer.length >1 && this.needsScrollFlash(iterator)){
                this.addScrollItem(buffer);
                buffer.length=0;
            }
        }
        this.addScrollItem(buffer);
    }
    
    onTerminateMessage(){
        this._autoTemporary.onTerminateMessage();
    }
    /**
     * @param {Number} index 
     */
    setLastCodeIndex(index){
        //戦闘テスト時にぬるぽするので対策
        const contents = this.getContents();
        if(contents.lastCodeIndex()===index){
            this.repeatLastVoice();
            this._messageStream.close();
            //this._temporaly.closeStream();
        }else{
            contents.setLastCodeIndex(index);
            if(this.isLogWriteEnabled()){
                this._messageStream.open();
            }
        }
    }

    createProxy(){
        return this.getContents().createProxy();
    }
    /**
     * 
     * @param {boolean} value 
     */
    setBattleMode(value){
        this._temporaly.setBattleMode(value);
    }
    isLogButtonEnabled(){
        return  !this._temporaly.isBattleMode();
    }
    /**
     * @public
     * @returns 
     */
    isLogWriteEnabled(){
        return this._readOnlyData.isLogWriteEnabled() ;//&& this._temporaly.isLogWriteEnabled()
    }
    /**
     * @private
     * @returns 
     */
    isLogSaveEnabled(){
        //ダウンロードしてのプレイの場合には有効化
        if(Utils.isNwjs() ){
            return true
        }
        //vscodeでの実行は多分これ
        if(Utils.isLocal()){
            return true;
        }
        //上記以外だと、アツマールの可能性があるので、セーブを最小化
        return false;
    }

    /**
     * @private
     * @returns 
     */
    getSaveContents(){
        if(!this._contents){
            return null;
        }
        if(this.isLogSaveEnabled()){
            return this._contents;
        }
        return this._contents.createMinimize();
    }

    repeatLastVoice(){
        if(this.isVoiceStoped()){
            const lastVoice =this.getContents().lastVoice();
            if(lastVoice){
                this.stopAutoAndSkip();
                ManoUZ_MessageLog.playVoice(lastVoice,{});
            }
        }
    }
    hasLastVoice(){
        if(this._messageStream.hasVoice()){
            return true;
        }

        const contents =this.getContents();
        const lastVoice = contents.lastVoice();
        return !!lastVoice;
    }

    stopBGS(){
        this._temporaly
    }

    /**
     * 
     * @param {number} index 
     * @param {Window_ChoiceList} choiceWindow 
     */
    onChoiceOk(index,choiceWindow){
        //キャンセル(分岐)の場合
        if(index ===-2){
            const cancelText= this.readOnryData().logStyle().cancelText();
            const item = new LogItem_Choice(cancelText,index);
            this.addItem(item);
            return;
        }
        if(0 <=index  &&  index < choiceWindow.maxItems()){
            const baseName = choiceWindow.commandName(index);
            const convertedText =choiceWindow.convertEscapeCharacters(baseName);
            const text= this.readOnryData().logStyle().createChoiceItemText(convertedText);
            const item = new LogItem_Choice(text,index);
            this.addItem(item);
        }
    }
    /**
     * @private
     * @param {I_LogItem} item 
     */
    addItem(item){
        //上の方で事前にガードしているが、念のために
        if(!this.isLogWriteEnabled()){
            return;
        }
        const contents = this.getContents();
        contents.addItem(item);
    }

    isEventRunning(){
       return !isNaN( this._contents.lastCodeIndex())
    }
    onMapEventEnd(){
        this._contents.clearLastCodeIndex();
        this._contents.saveCurrentLog();
        this.clearOldData();
        this._messageStream.clear();
        this.refreshAutoKeeping();
        //this._temporaly.clearLastVoice();
    }
    /**
     * @param {Game_Event} mapEvent 
     * @param {number} mapId
     */
    onMapEventStart(mapEvent,mapId){
        if(this._contents){
            this._contents.makeNewLog(mapId,mapEvent.eventId());
        }
        this.stopAutoAndSkip();
    }
    
    /**
     * @param {UZ_AudioFileParam} audio 
     * @param {{}} exParam
     */
    setLastVoiceParam(audio,exParam){
        const isVoice=this._readOnlyData.isVoiceFile(audio)
        if(isVoice){
            this._messageStream.setLastVoice(audio);
        }
        return isVoice;
    }
    /**
     * @param {WebAudio} buffer 
     */
    setLastVoiceBuffer(buffer){
        this._autoTemporary.setLastVoiceBuffer(buffer);
    }
    /**
     * @param {boolean} value 
     */
    setAutoMode(value){
        this._autoTemporary.setAutoMode(!!value);
    }
    getAutoMode(){
        return this._autoTemporary.getAutoMode();
    }
    changeAutoMode(){
        this._autoTemporary.flipAutoMode();
    }
    needsAutoModeStop(){
        if(this._option.isKeepingAutoMode()) {
            return false;
        }

        if($gameMessage.isBusy()){
            return false;
        }
        return !$gameMap.isEventRunning();

        // const eventRunning =$gameMap.isEventRunning();
        // if(eventRunning){
        //     return false;
        // }
        //選択肢とか
        // return !$gameMessage.isBusy();
        // const msgBusy =$gameMessage.isBusy();
        // if(msgBusy){
        //     return false;
        // }
        // return true;
    }
    refreshAutoKeeping(){
        if(this.needsAutoModeStop()){
            this._autoTemporary.setAutoMode(false);
        }
    }
    isTextAutoMode(){
        return this._autoTemporary.isAutoMode();
    }
    changeSkipMode(){
        this._readOnlyData.switchs().changeSkipMode();
    }
    isSkipMode(){
        return this._readOnlyData.switchs().isSkipMode();
    }
    stopAutoAndSkip(){
        this.setAutoMode(false);
        this._readOnlyData.switchs().stopSkipMode();
    }


}
function getParam(){ return PluginManager.parameters(PLUGIN_NAME);  }

/**
 * @param {*} param 
 */
function createButtonBehaviorList(param){
    const autoGauge= AutoGaugeBehavior.create(param.autoGauge);
    const auto_ =new AutoButtonBehavior(autoGauge, ButtonLayout.create(param.autoButton),"auto" );
    const log= new LogButtonBehabior(ButtonLayout.create(param.backlogButton),"shift");
    const save = new SaveButtonBehavior(ButtonLayout.create(param.saveButton),"pageup");
    const load = new LoadButtonBehabior(ButtonLayout.create(param.loadButton),"pagedown");
    const voiceRepeat =new VoiceRepeatBehavior(ButtonLayout.create(param.voiceRepeatButton),null);
    const skip =new SKipButtonBehavior(ButtonLayout.create(param.skipButton),"skip");

    const list=[log,save,load,voiceRepeat,skip,auto_].filter(b=>{
        return b.isValid()
    });
    const buttons = Button_Behavior_Setting.create(param.buttonSetting,list);

    return buttons;
}

function getKidokuSwitch(){
    const param= PluginManager.parameters(KIDOKU_PLUGIN_NAME);
    if(param){
        return Number(param.skipModeSwtich||0);
    }
    return 0;
}

/**
 * @type {LogManager_T}
 */
const LogManager=(function(){
    const param=getParam();
    const machinakaModeLines =Number(param.machinakaModeLines||3);
    const machinakaSwitch=Number(param.machinakaModeSwitch||0);
    const machinakaModeDefault =( "true" ===param.macinakaModeDefault);
    const machinakMode = new MachinakMode(machinakaSwitch,machinakaModeDefault);



    /**
     * @type {ManoUZ_LogOptionParametor}
     */
    const option = {
        autoWaitTime:Number(param.autoWaitTime||360),
        logLimitBasic:Number(param.logLimitBasic||40),
        logLimitExtends:Number(param.logLimitExtends||20),
        keepingAutoMode:false,
        gaugeColor:0xFF0000,
        machinakaLines:machinakaModeLines,
    };
    const choiceText  =MulitLangString.create(param.choiceText);
    const choiceFormat =String(param.choiceFormat);
    const choiceCancelText =MulitLangString.create(param.choiceCancelText)


    const style = LogStyle.create(param.logStyle,{
        choice:new ChoiceStyle(choiceFormat,choiceText,choiceCancelText)
    });

    const buttonsBehavior =createButtonBehaviorList(param);
    //const buttons = Button_Behavior_Setting.create(param.buttonSetting,buttonsBehavior)

    const autoReadonry = new AutoReadonry([],null);
    const voiceDirRegex = String(param.voiceFileRegex||"voice");
    /**
     * @type { string[]}
     */
    const voiceFileList = JSON.parse(param.voiceFileList||"[]")
    const voice = VoiceContainer.create(param.voice,voiceFileList,voiceDirRegex);
    const logWriteSwitch =Number(param.logWriteSwitch);
    const logWriteOnBattle = (param.logWriteOnBattle)==="true";

    /**
     * @type {MenuCommandParam}
     */
    const menuCommand={
        commandName:MulitLangString.create(param.menuCommand||"{}"),
        symbol:"UZMSGLOG",
    };
    const skipSwitch = getKidokuSwitch();

    const switchs =new LogSwtichs(logWriteSwitch,0,skipSwitch);

    const readOnryData=new LogReadOnlryData(voice,style,autoReadonry,switchs,logWriteOnBattle,option,menuCommand,machinakMode);
    const workaround =Utils.RPGMAKER_NAME ==="MZ" ? new MZ_Impriment() :null;
    const logm = new LogManager_T(workaround,readOnryData,buttonsBehavior);
    return logm;
}())



const Game_Interpreter_command101=Game_Interpreter.prototype.command101;
Game_Interpreter.prototype.command101 =function(params){
    //メッセージの表示
    const index = this._index;
    
    const result= Game_Interpreter_command101.call(this,params);
    if( result ){
        //イベントを実行したことを記録
        LogManager.setLastCodeIndex(index);
    }
    return result;
};
const Game_Interpreter_command250=Game_Interpreter.prototype.command250;
Game_Interpreter.prototype.command250 =function(param){
    const result= Game_Interpreter_command250.call(this,param);
    //ログ書き込み中ではないのでスキップ
    if(!LogManager.isLogWriteEnabled()){
        return result;
    }
    const isVoice= LogManager.setLastVoiceParam(param[0],null);
    if(isVoice){
        const buffer= GetLastSeBuffer();
        LogManager.setLastVoiceBuffer(buffer);
    }
    return result;
}
function GetLastSeBuffer(){
    const bufLen =AudioManager._seBuffers.length;
    const lastBuffer=AudioManager._seBuffers[bufLen-1];
    if(lastBuffer){
        return lastBuffer;
    }
    return null;
}


const Window_ChoiceList_callOkHandler=Window_ChoiceList.prototype.callOkHandler;
Window_ChoiceList.prototype.callOkHandler =function(){
    if(LogManager.isLogWriteEnabled()){
        const index = this.index();
        LogManager.onChoiceOk(index,this);
    }
    Window_ChoiceList_callOkHandler.call(this);
};
const Window_ChoiceList_callCancelHandler =Window_ChoiceList.prototype.callCancelHandler;
Window_ChoiceList.prototype.callCancelHandler=function(){
    if(LogManager.isLogWriteEnabled()){
        const cancelType=  $gameMessage.choiceCancelType();
        LogManager.onChoiceOk(cancelType,this);
    }
    Window_ChoiceList_callCancelHandler.call(this);
};

class Scene_MessageLog extends Scene_MenuBase{
    create(){
        super.create();
        this.createAllWindows();
    }
    logManager(){
        return LogManager;
    }

    createBackground(){
        const bmp = this.logManager().readOnryData().logStyle().backgroundBitmap();
        if(bmp){
            this._backgroundSprite = new Sprite(bmp);
            this.addChild(this._backgroundSprite);
        }else{
            super.createBackground();
        }
    }
    createAllWindows(){
        this.createHelpWindow();
        this.createMachinakaWindow();
        this.createLogWindow();
        this.linkWindow();
    }

    machinakeModeEnabled(){
        return LogManager.readOnryData().isMachinakaModeEnabled();
    }
    linkWindow(){
        this._machinakaWindow.setLogListWindow(this._logWindow);
        this._machinakaWindow.selectLastItem();
        
        if(this.machinakeModeEnabled()){
            this._machinakaWindow.activate();
            return;
        }
        this._logWindow.selectlLastItem();
        this._logWindow.activate();
    }
    start(){
        super.start();
        this._logWindow.refresh();
    }
    terminate(){
        super.terminate();
    }

    mainAreaLeft(){
        return 0
    }

    needsHelpWindow(){
        return false;
    }
    helpAreaHeight(){
        if(this.needsHelpWindow()){
            return super.helpAreaHeight();
        }
        return 0;
    }
    isBottomHelpMode(){
        return false;
    }
    logWindowRect(){
        const rect=LogManager.readOnryData().logStyle().logWindowRect();
        if(rect){ return rect;}
        
        const machinakaLHeight =this.machinakaWindowHeight();

        const x =this.mainAreaLeft();
        const y= this.mainAreaTop() + machinakaLHeight;
        const height = this.mainAreaHeight() -machinakaLHeight;

        return new Rectangle(
            x,y,
            Graphics.boxWidth, height
        );
    }
    createLogWindow(){
        const rect = this.logWindowRect();
        const logw= new Window_MessageLog(rect);
        if(this.needsHelpWindow()){
            logw.setHelpWindow(this._helpWindow);
        }
        logw.setHandler("ok",this.onBacklogOk.bind(this));
        logw.setHandler("cancel",this.onBacklogCancel.bind(this));
        this._logWindow = logw;
        this.addWindow(logw);
    }
    onBacklogOk(){
        const item= this._logWindow.currentItem();
        if(item){
            const voiceParam = item.voiceParam();
            if(voiceParam){
                ManoUZ_MessageLog.playVoice(voiceParam,{});
            }
        }
        this._logWindow.activate();
    }
    onBacklogCancel(){
        if(this._machinakaWindow.visible && this.machinakeModeEnabled()){
            this._logWindow.deselect();
            this._machinakaWindow.activate();
        }else{
            this.popScene();
        }
    }
    machinakaWindowHeight(){


        if(this.machinakeModeEnabled()){
            const lines =LogManager.machinakaLines();
            const height = this.calcWindowHeight(lines,true);
            return height;
        }
        return 0;
    }

    machinakaWindowRect(){
        const x =this.mainAreaLeft();
        const y= this.mainAreaTop();
        const height = this.machinakaWindowHeight();

        return new Rectangle(x,y,Graphics.boxWidth,height);
    }

    createMachinakaWindow(){
        const rect =this.machinakaWindowRect();

        const mw = new Window_MachinakaSelect(rect);
        mw.visible = this.machinakeModeEnabled();
        mw.setHandler("ok",this.onMachnakaOk.bind(this));
        mw.setHandler("cancel",this.onMachinakaCancel.bind(this));
        const list = LogManager.createMachinakaList();
        mw.setList(list);
        this._machinakaWindow=mw;

        this.addWindow(mw);
    }
    onMachnakaOk(){
        const item = this._machinakaWindow.currentItem();
        this._logWindow.setList(item.multiLineItems());
        this._logWindow.select(0);
        this._logWindow.activate();



    }
    onMachinakaCancel(){
        this.popScene();
    }


}
class I_GaugeValue{
    maxValue(){
        return 1;
    }
    currentValue(){
        return 1;
    }
    valueRate(){
        const max = Math.max(1,this.maxValue());
        return this.currentValue()/max;
    }
}


class Sprite_CircleGauge extends PIXI.Graphics{

    /**
     * @param {number} value 
     * @param {number} radius 
     */
    redraw(value,radius){
        this.geometry.clear();
        const begin =-Math.PI/2;
        this.beginFill(0,0);
        this.arc(radius,radius,radius, begin,(Math.PI * value*2) +begin );
        this.endFill();
    }
}


class Sprite_AutoGaugeCircle extends Sprite_CircleGauge{
    /**
     * 
     * @param {number} radius 
     */
    constructor(radius){
        super();
        this._radius =radius;
    }
    /**
     * 
     * @param {Bitmap} bitmap 
     * @param {number} width 
     * @param {number} color 
     */
    setupBitmapStyle(bitmap,width,color){
        bitmap.addLoadListener( (bmp)=>{
            const texture = new PIXI.Texture(bmp.baseTexture);
            this._texture =texture;
            this.lineTextureStyle({
                texture:texture,
                width:width,
                color:color,
            });
        });
    }
    /**
     * @param {number} value 
     */
    setGaugeValue(value){
        this.redraw(value,this._radius);
    }
    update(){
        //この空っぽのupdateは型チェックで文句を言われないようにするために必要
    }
}



/**
 * 
 * @param {Sprite} sprite 
 */
function getSpriteFrame(sprite){
    return {
        left:sprite.x,
        top:sprite.y,
        right:sprite.x +sprite.width,
        bottom:sprite.y + sprite.height,
    }

}

class ButtonGroop_Base extends PIXI.Container{
    /**
     * @param {Button_Behavior_Setting} behaviorList 
     * 
     */
    constructor(behaviorList){
        super();
        this._localBoundsRect =new Rectangle(0,0,0,0)
        //起動時に表示されないように画面外に飛ばす
        this.y=-123456;
        this._behaviorList=behaviorList;

        this._buttonsSprites =behaviorList.createSpriteList();
        this.calcSize();
        this.addChild(...this._buttonsSprites);        
    }
    /**
     * @param {number} width 
     * @param {number} height 
     */
    #setSize(width,height){
        this._hogeWidth =width;
        this._hogeHeight =height;
    }

    getHogehogeWidth(){
        return this._hogeWidth;
    }
    /**
     * @description ボタンの配置に必要な高さ
     * @returns 
     */
    buttonAreaHeight(){
        return this._hogeHeight;
    }
    buttonHeightMax(){
        const buttonHeightList= this._buttonsSprites.map((button)=>{return button.height });
        return Math.max(...buttonHeightList);
    }

    calcSize(){
        if(this._buttonsSprites.length<=0){
            this.#setSize(0,0);
            return;
        }
        const itemZero=this._buttonsSprites[0];
        const frame = getSpriteFrame(itemZero);
        for (let i = 1; i < this._buttonsSprites.length; i++) {
            const element =getSpriteFrame( this._buttonsSprites[i]);
            frame.left =Math.min(frame.left,element.left);
            frame.right =Math.max(frame.right,element.right);
            frame.top =Math.min(frame.top,element.top);
            frame.bottom =Math.max(frame.bottom,element.bottom)
        }
        const width =Math.abs(frame.left-frame.right );
        const height =Math.abs(frame.top-frame.bottom);
        this.#setSize(width,height);
    }
    isAnyButtonOverlaped(){
        return this._buttonsSprites.some(b=>{
            return b.isBeingTouched()
        })
    }
    updateInput(){
        for (const iterator of this._buttonsSprites) {
            iterator.update();
        }
    }

    startMessage(){
        //トリアコンタンさんの、メッセージウィンドウ出しっぱなしプラグイン対策
        //クロノトリガーのメッセージ出しっぱなしと同じような奴
        this.visible = !this._behaviorList.isButtonDisabled();
        for (const iterator of this._buttonsSprites) {
            //操作を有効にする
            iterator.onStartMessage();
        }
    }
    endMessage(){
        //操作を無効にする
        //現状では非表示状態になれば動かないので、不要
    }

    /**
     * @param {number} _positionType 
     * @returns 
     */
    movementOffsetOfChoice(_positionType){
        return 0;
    }

    /**
     * @param {number} _posType 
     */
    updatePlacement(_posType){

    }
    windowStretchY(){
        return 0;        
    }
    /**
     * @param {Number} openness 
     */
    updateOpenness(openness){
        const rate = openness/255;
        for (const iterator of this._buttonsSprites) {
            iterator.updateOpenness(rate,openness);            
        }
    }
}

class ButtonGroop_Inside extends ButtonGroop_Base{


    windowStretchY(){
        return this.buttonAreaHeight()-16;
    }
    /**
     * 
     * @param {number} posType 
     */
    updatePlacement(posType){
        const x =this.parent.width -this.getHogehogeWidth() ;
        const y =this.parent.height -this.height;
        this.x = x +this._behaviorList.offsetX();
        this.y = y +this._behaviorList.offsetY();
    }
}
class ButtonGroop_Outside extends ButtonGroop_Base{
    /**
     * @param {Number} positionType 
     */
    updatePlacement(positionType){        
        const x =this.parent.width -this.getHogehogeWidth();
        const y=  (positionType===0) ? this.parent.height :-this._hogeHeight;
        this.x = x +this._behaviorList.offsetX();
        this.y = y +this._behaviorList.offsetY();
    }
    /**
     * @param {number} positionType 
     * @returns 
     */
    movementOffsetOfChoice(positionType){
        //Windowが中央
        if(positionType ===1){
            return 0;
        }
        //Windowが下
        if(positionType ===2){
            return -this.height;
        }
        //Windowが上
        return this.height;
    }
}

//絶対座標指定・左上原点
class ButtonGroop_Absolute extends ButtonGroop_Base{
    /**
     * @param {Button_Behavior_Setting} list 
     */
    constructor(list){
        super(list);
    }
    /**
     * @param {number} positionType
     */
    updatePlacement(positionType){
        if(this.parent){
            //メッセージ画面が下に配置されるときだけ表示する
            this.visible = positionType===2;
            //親の座標分マイナスに動かせば、画面原点になる
            this.x = this._behaviorList.offsetX() -this.parent.x;
            this.y = this._behaviorList.offsetY() -this.parent.y;
        }
    }
}


const Window_ChoiceList_updatePlacement=Window_ChoiceList.prototype.updatePlacement;
Window_ChoiceList.prototype.updatePlacement =function(){
    Window_ChoiceList_updatePlacement.call(this);
    //選択肢が右
    if($gameMessage.choicePositionType() === 2){
        const groopHeight =logButtonAreaHeight(this._messageWindow);
        this.y += groopHeight;
    }
};

const Window_Message_initialize =Window_Message.prototype.initialize;
Window_Message.prototype.initialize =function(/** @type {{ clone: () => Rectangle; }} */ rect){
    //@ts-ignore
    this._logButtonGroop=null;
    if(!LogManager.isLogButtonEnabled()){
        Window_Message_initialize.call(this,rect);
        return;
    }
    /**
     * @type {Rectangle}
     */
    const rect2 =rect.clone();
    const buttonGroop=LogManager.createLogButtonSprites();
    //@ts-ignore
    this._logButtonGroop =buttonGroop;
    //ボタン配置用のスペースを確保
    rect2.height += buttonGroop.windowStretchY();
    Window_Message_initialize.call(this,rect2);
    this.addChild(buttonGroop)
};
/**
 * @param {Window_Message} msgWindow 
 * @returns {ButtonGroop_Base}
 */
function getLogButtonGroop(msgWindow){
    //@ts-ignore
    return msgWindow._logButtonGroop;
}
/**
 * @param {Window_Message} msgWindow 
 */
function logButtonAreaHeight(msgWindow){
    const groop = getLogButtonGroop(msgWindow);
    if(groop){
        return groop.movementOffsetOfChoice(msgWindow._positionType);
    }
    return 0;
}

const Window_Message_update =Window_Message.prototype.update ;
Window_Message.prototype.update =function(){
    Window_Message_update.call(this);
    const groop = getLogButtonGroop(this);
    if(groop){
        groop.updateOpenness(this.openness);
    }
};
const Window_Message_newPage =Window_Message.prototype.newPage;
Window_Message.prototype.newPage =function(/** @type {TextState} */ textState){
    const LH = this.lineHeight();
    const lines =Math.round(this._textState.outputHeight /LH);
    //4行に収まらない文章を表示
    //newpageで表示される
    //5行目からの文章を表示している際にログを開いて遷移すると、最初の行から表示やり直しになる

    //gameMessageに5行以上のデータがあると、奇妙な状態になる
    //何か対策が必要


    // if(this._textState.index >0){
    //     const bbb = this._textState.text.slice(0,this._textState.index);
    //     //このタイミングで文字列を送り込むほうが良さそう
    //     this;
    //     //何か表示済み
    // }
    LogManager.addLines(Math.min(lines,4));
    Window_Message_newPage.call(this,textState);
};

const Window_Message_onEndOfText=Window_Message.prototype.onEndOfText;
Window_Message.prototype.onEndOfText =function(){
    const LH = this.lineHeight();
    const lines =Math.round(this._textState.outputHeight /LH);
    LogManager.addLines(Math.min(lines,4));
    LogManager.onEnedOfText();
    Window_Message_onEndOfText.call(this);
};
const Window_Message_startMessage =Window_Message.prototype.startMessage;
Window_Message.prototype.startMessage =function(){
    Window_Message_startMessage.call(this);
    const logButtons = getLogButtonGroop(this);
    if(logButtons){
        if(LogManager.needsShowFast()){
            this._showFast =true;
        }
        const name = this._nameBoxWindow.convertEscapeCharacters($gameMessage.speakerName());
        
        LogManager.startMessage(name,this._textState.text);
        logButtons.startMessage();

    }
};
const Window_Message_terminateMessage=Window_Message.prototype.terminateMessage;
Window_Message.prototype.terminateMessage =function(){
    LogManager.onTerminateMessage();
    Window_Message_terminateMessage.call(this);
    LogManager.refreshAutoKeeping();
};
const Window_Message_updatePlacement=Window_Message.prototype.updatePlacement;
Window_Message.prototype.updatePlacement =function(){

    Window_Message_updatePlacement.call(this);
    const logButtons = getLogButtonGroop(this);
    if(logButtons){
        logButtons.updatePlacement(this._positionType);
    }
};

/**
     * @param {Window_Message} msgWindow
     */
function canInput(msgWindow){
    return msgWindow.isOpen() && !msgWindow.isClosing();
}
/**
 * 
 * @param {ButtonGroop_Base} buttonGroop 
 * @returns 
 */
function updateInput(buttonGroop){
    if($gameParty.inBattle()){
        return;
    }
    LogManager.updateInput();
    if(buttonGroop){
        buttonGroop.updateInput();
    }
}

const Window_Message_updateInput=Window_Message.prototype.updateInput;
Window_Message.prototype.updateInput =function(){
    if(!this._textState &&canInput(this) ){
        updateInput(getLogButtonGroop(this));
    }
    const lastPauseState = this.pause;
    const runResult =  Window_Message_updateInput.call(this);
    if(!this.pause){
        return runResult;
    }
    if(!LogManager.isTextAutoMode()){
        return runResult;
    }
    //メッセージの表示が完了した？
    if(!lastPauseState){
        LogManager.startAutoTimer();
    }
    //時間経過読み込み・通常処理の入力読み込みに相当
    LogManager.updateAutoTimer();
    
    if(LogManager.needsTextTerminate()){
        LogManager.endAutoTimer();
        this.pause = false;
        this._textState = null;
        this.terminateMessage();
        return false;
    }
    return true;
};
const Window_Message_isTriggered =Window_Message.prototype.isTriggered;
Window_Message.prototype.isTriggered =function(){
    //Scene_Battleのためにifで存在チェック
    const logButtons =getLogButtonGroop(this);
    if(logButtons  &&TouchInput.isRepeated() && logButtons.isAnyButtonOverlaped()){
        //ボタンと重なっている場合、Clickによるメッセージ送りを無効化する
        return false;
    }
    return Window_Message_isTriggered.call(this);
}
const Window_ScrollText_startMessage=Window_ScrollText.prototype.startMessage
Window_ScrollText.prototype.startMessage =function(){
    Window_ScrollText_startMessage.call(this);    
    LogManager.startScrollTextMessage($gameMessage._texts);
};

const Scene_Map_createDisplayObjects=Scene_Map.prototype.createDisplayObjects;
Scene_Map.prototype.createDisplayObjects =function(){
    LogManager.setBattleMode(false);
    Scene_Map_createDisplayObjects.call(this);
    if(LogManager.needsShowFast()){
        this._messageWindow.openness =255;
    }
};

const Scene_Battle_createDisplayObjects=Scene_Battle.prototype.createDisplayObjects;
Scene_Battle.prototype.createDisplayObjects =function(){
    LogManager.setBattleMode(true);

    Scene_Battle_createDisplayObjects.call(this);
};
const Game_Map_setupStartingMapEvent=Game_Map.prototype.setupStartingMapEvent;
Game_Map.prototype.setupStartingMapEvent =function(){
    const result=Game_Map_setupStartingMapEvent.call(this);

    //マップイベントが起動された
    if(result){
        const eventId=  this._interpreter.eventId();
        const eventCharacter=this.event(eventId);
        if(eventCharacter){
            //ログの書き込みを開始
            LogManager.onMapEventStart(eventCharacter,this._mapId);
        }
    }
    return result;
};
const Game_Map_updateInterpreter =Game_Map.prototype.updateInterpreter;
Game_Map.prototype.updateInterpreter =function(){
    const isRunning = this._interpreter.isRunning();
    Game_Map_updateInterpreter.call(this);
    //イベントが実行された結果、終了した
    if(isRunning &&!this._interpreter.isRunning()){
        LogManager.onMapEventEnd();
    }
};


const Window_MenuCommand_addOriginalCommands=Window_MenuCommand.prototype.addOriginalCommands;
Window_MenuCommand.prototype.addOriginalCommands =function(){
    Window_MenuCommand_addOriginalCommands.call(this);
    const cmd =LogManager.readOnryData().menuCommand();
    const cmdName = cmd.commandName.text();
    if(cmdName){
        this.addCommand(cmdName,cmd.symbol);
    }
};

const Scene_Menu_createCommandWindow=Scene_Menu.prototype.createCommandWindow;
Scene_Menu.prototype.createCommandWindow =function(){
    Scene_Menu_createCommandWindow.call(this);
    const cmd =LogManager.readOnryData().menuCommand();
    this._commandWindow.setHandler(cmd.symbol,()=>{
        SceneManager.push(Scene_MessageLog);
    });
};


class PluginCommandCaller{
    /**
     * @param {String} commandName
     * @param {ReadonlyArray<String>} paramNames 
     * @param  {(arg:any)=>void} func
     */
    constructor(commandName,paramNames,func){
        this._commandName = commandName;
        this._paramNames =paramNames;
        this._func = func;
    }
    /**
     * @param {ReadonlyArray<String>} mvArgs 
     */
    makeMZ_arg(mvArgs){
        const result ={}
        const len = Math.min(mvArgs.length,this._paramNames.length);
        for(let i=0; i < len;++i){
            const paramName = this._paramNames[i];
            result[paramName] = mvArgs[i];
        }
        return result;

    }
    /**
     * @param {ReadonlyArray<string>} mvArgs 
     */
    convertCall(mvArgs){
        const argMZ = this.makeMZ_arg(mvArgs);
        this.callMZ(argMZ);

    }
    callMZ(arg){
        this._func(arg);
    }
}
class PluginManagerMV_T{
    /**
     * @param {String} pluginName 
     */
    constructor(pluginName){
        /**
         * @type {Map<String,PluginCommandCaller>}
         */
        this._map = new Map();
        this._pluginName =pluginName;
    }
    /**
     * @param {String} commandName 
     * @param {ReadonlyArray<String>} paramNames
     * @param {(arg)=>void} func 
     */
    registerCommand(commandName,paramNames,func){
        if(Utils.RPGMAKER_NAME ==="MZ"){
            this.registerCommandMZ(commandName,func);
        }
        if(this.isMVcommandEnabeled() ){
            this.registerCommandMV(commandName,paramNames,func);
        }
    }
    isMVcommandEnabeled(){
        return Utils.RPGMAKER_NAME ==="MV" ;
    }
    /**
     * @param {String} commandName 
     * @param {(arg:any)=>void} func 
     */
    registerCommandMZ(commandName,func){
        PluginManager.registerCommand(this._pluginName,commandName,func);
    }
    /**
     * @param {String} commandName 
     * @param {ReadonlyArray<String>} paramNames
     * @param {(arg:any)=>void} func 
     */
    registerCommandMV(commandName,paramNames, func){
        const funcObj = new PluginCommandCaller(commandName,paramNames,func);
        this._map.set(commandName,funcObj);
    }
    /**
     * @param {Game_Interpreter} inter
     * @param {String} commandName 
     * @param {String[]} args 
     */
    callMV(inter,commandName,args){
        const func = this._map.get(commandName);
        if(func){
            const argMZ=func.makeMZ_arg(args);
            func.callMZ(argMZ);
            return true;
        }
        return false;
    }
}

const PluginManagerEX=new PluginManagerMV_T(PLUGIN_NAME);
PluginManagerEX.registerCommand("Show",[],()=>{
    SceneManager.push(Scene_MessageLog);
});
PluginManagerEX.registerCommand("SetTitle",[],(arg)=>{
    const title = String(arg.title);
    LogManager.setMachinakaTitle(title);
});

PluginManagerEX.registerCommand("ClearAll",[],()=>{
    LogManager.clearLog();
});
PluginManagerEX.registerCommand("CleanupOldData",[],(arg)=>{
    LogManager.clearOldData();
});


PluginManagerEX.registerCommand("GetLogSize",[],(arg)=>{
    const varibaleId =Number(arg.variableId)
    const size= LogManager.getLogSize();
    $gameVariables.setValue(varibaleId,size);

});
PluginManagerEX.registerCommand("SetAutoMode",["mode"],(arg)=>{
    LogManager.setAutoMode(arg.automode==="true");
});
PluginManagerEX.registerCommand("GetAutoMode",["mode"],(arg)=>{
    const switchId =Number(arg.swtichId);
    const value= LogManager.getAutoMode();
    $gameSwitches.setValue(switchId,value);
});

PluginManagerEX.registerCommand("HasLastVoice",["switchId"],(arg)=>{
    const switchId =Number(arg.switchId);
    $gameSwitches.setValue(switchId,LogManager.hasLastVoice());
});

PluginManagerEX.registerCommand("ShowConsloeProxy",[],()=>{
    const p=  LogManager.getContents().createProxy();
    for (const iterator of p) {
        console.table(iterator);
    }
});

PluginManagerEX.registerCommand("GetAutoWaitTime",[],(arg)=>{
    const speed= LogManager.option().getAutoWaitTime();

    const variableId =Number(arg.variableId);
    $gameVariables.setValue(variableId,speed);
});

PluginManagerEX.registerCommand("SetAutoWaitTime",[],(arg)=>{
    const variableId =Number(arg.variableId);
    const speed =  $gameVariables.value(variableId);
    if(!isNaN(speed)){
        LogManager.option().setAutoWaitTime(speed);
    }
});

PluginManagerEX.registerCommand("SetLogLimitSizeBasic",[],(arg)=>{
    const variableId =Number(arg.variableId);
    const size =  $gameVariables.value(variableId);
    LogManager.option().setLogLimitBasic(size);
});

PluginManagerEX.registerCommand("GetLogLimitSizeBasic",[],(arg)=>{
    const variableId =Number(arg.variableId);
    const size =LogManager.option().getLogLimitSizeBasic();
    $gameVariables.setValue(variableId,size);
});

PluginManagerEX.registerCommand("SetLogLimitSizeExtends",[],(arg)=>{
    const variableId =Number(arg.variableId);
    const size =  $gameVariables.value(variableId);
    LogManager.option().setLogLimitExtends(size);
});

PluginManagerEX.registerCommand("GetLogLimitSizeExtends",[],(arg)=>{
    const variableId =Number(arg.variableId);
    const size =LogManager.option().getLogLimitSizeExtends();
    $gameVariables.setValue(variableId,size);
});


PluginManagerEX.registerCommand("GetKeepingAutoMode",[],(arg)=>{
    /**
     * @type {boolean}
     * @description booleanであることを明示するために型指定する。間違っていたら警告が出る。
     */
    const keep = LogManager.option().isKeepingAutoMode();
    const switchId = Number(arg.switchId);

    $gameSwitches.setValue(switchId,keep);
});


const CONFIG_KEY="UZMSGLOG_CONFIG";
const ConfigManager_makeData =ConfigManager.makeData;
ConfigManager.makeData =function(){
    const result = ConfigManager_makeData.call(this);
    const option =LogManager.cloneOptionValue();
    result[CONFIG_KEY] =option;
    return result;
};
const ConfigManager_applyData =ConfigManager.applyData;
ConfigManager.applyData =function(config){
    ConfigManager_applyData.call(this,config);
    const option =config[CONFIG_KEY];
    LogManager.setOptionValue(option);
}

const DataManager_makeSaveContents =DataManager.makeSaveContents;
DataManager.makeSaveContents =function(){
    const contents= DataManager_makeSaveContents.call(this);
    LogManager.writeSaveContents(contents);
    return contents;
};
const DataManager_extractSaveContents=DataManager.extractSaveContents;
DataManager.extractSaveContents =function(/** @type {any} */ contents){
    DataManager_extractSaveContents.call(this,contents);
    LogManager.readSaveContents(contents);
}
const DataManager_setupNewGame=DataManager.setupNewGame;
DataManager.setupNewGame =function(){
    DataManager_setupNewGame.call(this);
    LogManager.onNewGame();
};
const DataManager_createGameObjects=DataManager.createGameObjects;
DataManager.createGameObjects =function(){
    DataManager_createGameObjects.call(this);
    LogManager.makeSaveContents();
};

const Scene_Boot_start=Scene_Boot.prototype.start;
Scene_Boot.prototype.start =function(){
    Scene_Boot_start.call(this);
    MultiLangManager.refresh();
    LogManager.onBoot();
};
/**
 * @param {Function} class_ 
 * @param {String} name 
 */
function classExport(class_,name){
    window[name] = class_;
}
//class文で書くと、異なる名前空間に置かれる
//そのため、この処理で再設定が必要
const classList=[
    LogList,MultiLine_Message,LogItem_Choice,Log_ContentsObject
];

for (const iterator of classList) {
    classExport(iterator,iterator.name);
}
return new LogManager_Export(LogManager);
}());
