//@ts-check
/*:
 * @plugindesc Manage Skip and Already-Read Texts
 * @author siguren
 * 
 * @target MZ
 * 
 * @command DisableReadCheck
 * @text Disable Already-Read Check
 * @desc Disables recording and check for Already-Read until this event is completed.
 * 
 * @command EnableReadCheck
 * @text Enable Already-Read Check
 * @desc Enables recording and check for Already-Read until this event is completed.
 * Nothing happens if you enable it during a common event.
 * 
 * @command DisplayKidokuState
 * @text Open Already-Read Flag viewing screen.
 * @desc The content is for advanced users.
 * 
 * @param skipModeSwtich
 * @text Skip:Control Switch
 * @desc When turned ON, fast-forwards when an Already-Read text appears.
 * @type switch
 * @default 0
 * 
 * @param ctrlSkip
 * @text Skip:Enable skip with CTRL key
 * @desc It will be in Skip mode while CTRL key is pressed.
 * @type boolean
 * @default true
 * 
 * @param kidokuTextColorIndex
 * @text Setting for Already-Read text color
 * @type number
 * @default 6
 * 
 * @param excludedCharacters
 * @text Events that do not record Already-Read Texts
 * @desc There will be no Already-Read Check for events with the specified walking graphic.
 * 
 * @type file[]
 * @dir img/characters
 * @default ["!SF_Chest","!Chest"]
 * 
 * @help
 * Manage Already-Read for sentences.
 * One Already-Read Flag uses 8bytes.
 * 
 * The line number of the event command is used for saving.
 * Rewriting the event data makes the Already-Read data inaccurate.
 * Since line numbers are saved in 12bit (max4095), 
 * if the event's page content is too large, it may not be saved correctly.
 * 
 *It will not record if the call hierarchy of common events is deep.
 * 
 * The Already-Read flag is independent of each page of events.
 * Data is not mixed with other events or event pages.
 * 
 * ■Changing Already-Read text color
 * Change the color of sentences or options that have already been displayed or selected.
 * The color is changed when the text color is set to default or \c[0].
 * 
 * ■Disabling Already-Read Check
 * Already-Read Check can be disabled for events that
 * do not require them, such as texts in stores and inns.
 * 
 * ■Already-Read Check Restriction for Common Events
 * Already-Read Check cannot be activated when restricted.
 * Already-Read Check is prohibited in the following situations.
 * ・Common Events of Parallel Execution
 * ・Common Events under Automatic Execution
 * ・Events that are invoked during combat
 * 
 * ■About Already-Read Skip Mode
 * It will be in Already-Read Skip Mode when the specified switch is ON, or while the CTRL key is pressed.
 * 
 * In this mode, texts that are already read will be skipped.
 * It will pause if there is an unread text.
 * 
 * ■About Already-Read Flag viewing screen
 * The Already-Read Flag viewing screen can be opened by the plugin command.
 * Although it is made to be somewhat easy to read,
 * it is intended for advanced users.
 * 
 * ■About the Already-Read Flag Save Data
 * A dedicated file is created for this.
 * It will be shared among multiple saved data.
 * The file size will be about 100KB at the most.
 * 
*/

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

/*:
 * @plugindesc 既読の管理・スキップ
 * @author しぐれん
 * 
 * @target MZ
 * 
 * @command DisableReadCheck
 * @text 既読チェックの無効化
 * @desc このイベントが終了するまで既読の記録・確認を無効化します。
 * 
 * @command EnableReadCheck
 * @text 既読チェックの有効化
 * @desc このイベントが終了するまで既読の記録・確認を有効化します。
 * コモンイベント内で実行した場合は、何も起きません。
 * 
 * @command DisplayKidokuState
 * @text 既読フラグ閲覧画面を開く
 * @desc 表示内容は上級者向けです。
 * 
 * @param skipModeSwtich
 * @text スキップ:制御スイッチ
 * @desc ONの場合、既読テキストが来た時に早送りします。
 * スキップ中キャンセル操作を行うと、スイッチをOFFにします。
 * @type switch
 * @default 0
 * 
 * 
 * @param ctrlSkip
 * @text スキップ:CTRLキーで有効化
 * @desc CTRLキーが押されている間、スキップモードになります。
 * @type boolean
 * @default true
 * 
 * @param kidokuTextColorIndex
 * @text 既読テキスト色設定
 * @type number
 * @default 6
 * 
 * @param excludedCharacters
 * @text 既読を記録しないイベント
 * @desc 指定した歩行グラフィックのイベントは
 * 既読チェックを行いません。
 * @type file[]
 * @dir img/characters
 * @default ["!SF_Chest","!Chest"]
 * 
 * @help
 * 文章の既読を管理します。
 * 一つの既読フラグで8byte使います。
 * 
 * イベントコマンドの行番号を使って保存しています。
 * イベントデータを書き換えると、既読データが不正確になります。
 * 行番号は12bit(最大4095)で保存しているため、
 * イベントのページ内容が巨大だと正しく保存されない可能性があります。
 * 
 * コモンイベントの呼び出し階層が深い場合、記録を行いません。
 * 
 * 既読フラグはイベントのページごとに独立しています。
 * 他のイベントや他のイベントページとデータが混ざることはありません。
 * 
 * ■既読テキストの色変更
 * 既に表示されたことのある文章・既に選んだことのある選択肢の色を変えます。
 * テキストの色が初期値または\c[0]で通常色になっている場合、色が変更されます。
 * 
 * ■既読チェックの無効化
 * ショップや宿屋などの既読チェックをする必要が無いイベントでは、
 * 既読チェックを無効化できます。
 * 
 * ■コモンイベントでの既読チェック禁止処理
 * 禁止されている状態では、既読チェックを有効化できません。
 * 以下の状況では既読チェックが禁止されます。
 * ・並列実行のコモンイベント
 * ・自動実行のコモンイベント
 * ・戦闘中に呼び出されたイベント
 * 
 * ■既読スキップモードについて
 * 指定のスイッチがONの場合、またはCTRLキーが押し続けられている間は、
 * 既読スキップモードになります。
 * 既読スキップモードでは、既読文章が表示された場合に文章をスキップします。
 * 既読ではない文章の場合、そこで一時停止します。
 * 
 * スイッチがONの場合にキャンセル操作が行われると、
 * スイッチをOFFにしてスキップ状態を終了します。
 * 
 * ■既読フラグ閲覧画面について
 * プラグインコマンドで既読フラグ閲覧画面を呼び出せます。
 * ある程度は見やすくしてありますが、
 * 上級者向け扱いです。
 * 
 * ■既読フラグのセーブデータについて
 * 専用のファイルを作成します。
 * 複数のセーブデータ間で共有されます。
 * 大きくても100KBほどに収まります。
 * 
*/
/** 
 * @typedef {object} KidokuEventDataConcept
 * @property {number} eventId
 * @property {number} mapId
 * @property {number} pageIndex
 */
 class KidokuEventData{
    // /**
    //  * @param {number} mapId 
    //  */
    // setMapId(mapId) {
    //     this._mapId =mapId;
    // }
    // get mapId(){
    //     return this._mapId;
    // }
    // getMapId() {
    //     return this._mapId;
    // }
    getEventId(){
        return this._eventId;
    }

    getPageIndex(){
        return this._pageIndex;
    }
    constructor(){
        this.clear();

    }
    // /**
    //  * @private
    //  * @param {number} depth3bit 
    //  * @param {number} msg16bit 
    //  * @param {number} choice15bit
    //  */
    // createEventBit(depth3bit,msg16bit,choice15bit){
    //     return createEventBitImple(this._eventId,this._pageIndex,depth3bit,msg16bit,choice15bit);
    // }
    clear(){
        this.set(0,0);
    }
    /**
     * 
     * @param {number} eventId 
     * @param {number} pageIndex 
     */
    set(eventId,pageIndex){
        this._eventId =eventId;
        this._pageIndex = pageIndex;
    }
}

class KidokuEventState{
    constructor(){
        this.clearLastMessageLine();
        this.setChoiceHedder(NaN);
        this.setWritePermission(false);
    }
    
    /**
     * @param {boolean} value 
     */
    setWritePermission(value){
        this._writePermission =value;
    }
    /**
     * @returns {boolean}
     */
    canWriteng() {
        return this._writePermission;
    }

    /**
     * @param {number} depth
     * @param {number} line 
     */
    setLastMassageLine(depth,line){
        this._lastMessageLine=line;
        this._depth=depth;
    }
    clearLastMessageLine(){
        //以前はNaNにしていた
        //イベントの先頭で選択肢を配置された場合に備えて(0,0)に変更した
        this.setLastMassageLine(0,0);
    }
    lastMessageLine(){
        return this._lastMessageLine;
    }
    depth(){
        return this._depth;
    }
    /**
     * @param {number} line 
     */
    setChoiceHedder(line){
        this._choiceHedder=line;
    }
    choiceHedder(){
        return this._choiceHedder;
    }

}

class KidokuFlags{
    constructor(){
        /**
         * @type {Array<number[]>}
         */
        this._table=[null];
    }

    /**
     * @returns {ReadonlyArray<number>}
     * @param {number} mapId 
     */
    getFlags(mapId){
        if(mapId > 0){
            if(this._table[mapId]){
                return this._table[mapId];
            }
        }
        return [];
    }

    /**
     * @param {number} mapId 
     * @param {Iterable<number>} set 
     */
    saveMapData(mapId,set){
        if(mapId > 0){
            const list =Array.from(set);
            this._table[mapId] =list;    
        }
    }
    /**
     * @returns {ReadonlyArray<number>}
     */
    mapNumbers(){
        const result =[];
        const length =this._table.length
        for (let index = 0; index < length; index++) {
            if(this._table[index]){
                result.push(index);
            }
        }
        return result;
    }
}

class KidokuContents{
    /**
     * 
     * @param {boolean} value 
     */
    setReadcheckEnable(value) {
        this._state.setWritePermission(value);
    }
    // /**
    //  * 
    //  * @param {number} mapId 
    //  */
    // setMapId(mapId) {
    //     this._eventData.setMapId(mapId);
    // }
    /**
     * @param {number} version 
     */
     constructor(version){
        this._flags = new KidokuFlags();
        this._eventData = new KidokuEventData();
        this._state = new KidokuEventState();
        this._version = version;
        this.setMapId(0);
    }

    mapNumbers() {
        return this._flags.mapNumbers();
    }
    /**
     * @param {number} eventId
     * @param {number} pageIndex
     */
    setupEventData(eventId,pageIndex){
        this._eventData.set(eventId,pageIndex);
    }
    clearEventData(){
        this._eventData.clear();
        this._state.setWritePermission(false);
    }
    /**
     * @param {number} depth 
     * @param {number} choiceIndex 
     */
    createChoiceBit(depth, choiceIndex) {
        const msgLine = this._state.choiceHedder();
        
        if(this._state.depth() === depth && !isNaN(msgLine)){
            return this.createEventBit(this._eventData,depth,msgLine,choiceIndex)
        }
        return 0;
    }

    /**
     * @private
     * @this {Readonly<KidokuContents>}
     * @param {number} value 
     */
    shift32bit(value){
        return (value &0x7fffffff)  * 4294967296;
    }

    /**
     * @private
     * @param {KidokuEventData} data 
     * @param {number} depth3bit
     * @param {number} textLine16bit
     * @param {number} dataType15bit
     */
    createEventBit(data,depth3bit,textLine16bit,dataType15bit){
        const baseData31bit =
        (    data.getEventId() & 0xff) | 
        ((data.getPageIndex()  & 0b11111 ) << 8) |
        ((  depth3bit    & 0b111) << 13) |
        ((dataType15bit  & 0x7fff)<<16);
        const shifted31bit = this.shift32bit(textLine16bit);
        return baseData31bit + shifted31bit;
    }
    /**
     * @param {number} depth 
     * @param {number} msgLine 
     * @returns 
     */
    createMessageBit(depth,msgLine){
        return this.createEventBit(this._eventData,depth,msgLine,0x7fff);
    }
    /**
     * @param {number} line 
     */
    setChoiceHedder(line){
        this._state.setChoiceHedder(line);

    }
    /**
     * @param {number} depth 
     * @param {number} line 
     */
    setLastMassageLine(depth, line) {
        this._state.setLastMassageLine(depth,line)
    }
    version(){
        return this._version;
    }
    depth(){
        return this._state.depth();
    }
    /**
     * @param {Iterable<number>} values 
     */
    saveMapData(values){
        this._flags.saveMapData(this.mapId(),values);    
    }
    canWrite(){
        return ( this._eventData && this._eventData.getEventId() > 0 
            && this._state.canWriteng()
        );
    }

    /**
     * @returns 
     */
    eventId(){
        return this._eventData.getEventId();
    }
    /**
     * @this {Readonly<KidokuContents>}
     * @returns 
     */
    mapId(){
        return this._mapIdV2;
    }
    /**
     * @param {number} mapId 
     */
    setMapId(mapId){
        this._mapIdV2=mapId;
    }
    /**
     * @this {Readonly<KidokuContents>}
     * @param {number} mapId 
     * @returns {ReadonlyArray<number>}
     */
    loadMapData(mapId){
        return this._flags.getFlags(mapId);
    }
}

(function(){
    "use stirct";

/**
 * @typedef {"save"|"load"|""} SaveStateEnum
 */
/**
 * @typedef {object} KidokuReadonlryData
 * @property {number} colorIndex
 * @property {number} skipModeSwtich
 * @property {ReadonlySet<string>} excludedCharacters
 * @property {boolean} ctrlSkip
 */

const PLUGIN_VERSION =20221015;
const SAVEDATA_KEY="KIDOKU";
const SAVE_FILE_NAME ="kidoku";

/**
 * @type {String}
 */
const  PLUGIN_NAME= ('ManoUZ_Kidoku_AlreadyRead');
function getParam(){ return PluginManager.parameters(PLUGIN_NAME);  }

/**
 * @param {number} value 
 * @returns 
 */
const shift32bit =(value)=>{
    return (value &0x7fffffff)  * 4294967296;
}

// /**
//  * @param {number} eventId8bit 
//  * @param {number} pageIndex5bit 
//  * @param {number} depth3bit 
//  * @param {number} dataType15bit
//  * @param {number} textLine16bit 
//  */
// const createEventBitImple =(eventId8bit,pageIndex5bit,depth3bit,textLine16bit,dataType15bit)=>{
//     const baseData31bit =
//     (    eventId8bit & 0xff) | 
//     ((pageIndex5bit  & 0b11111 ) << 8) |
//     ((  depth3bit    & 0b111) << 13) |
//     ((dataType15bit  & 0x7fff)<<16);
//     const shifted31bit = shift32bit(textLine16bit);
//     return baseData31bit +shifted31bit;
// };
// /**
//  * 
//  * @param {KidokuEventDataConcept} data 
//  * @param {number} depth3bit 
//  * @param {number} msg16bit 
//  * @param {number} choice15bit 
//  */
// const createEventKidoku =(data,depth3bit,msg16bit,choice15bit)=>{
//     return createEventBitImple(data.eventId,data.pageIndex,depth3bit,msg16bit,choice15bit);
// }

// const createEventData =()=>{
    
// }


/**
 * @typedef {object} KidokTemp
 * @property {boolean} colorChanging
 * @property {ReadonlyArray<boolean>} choiceTable
 * @property {boolean} hasChanged
 */
class KidokuEventManager_T{
    /**
     * 
     * @param {boolean} value 
     */
    setReadcheckEnable(value) {
        this._contents.setReadcheckEnable(value)
    }
    /**
     * @param {Readonly<KidokuReadonlryData>} data 
     */
    constructor(data){
        this._readonlryData =data;
        /**
         * @type {Set<Number>}
         */
        this._set = new Set();
        this.setContents(null);
        this.clearTemp();
        /**
         * @type {SaveStateEnum}
         */
        this._sharedAccessState ="";
        this.setParallelGuard(false);
    }

    /**
     * @private
     * @param {Game_Event} eventCharacter 
     */
    isEventValid(eventCharacter){
        if(eventCharacter){
            const name = eventCharacter.characterName();
            return !this._readonlryData.excludedCharacters.has(name);
        }
        return false;
    }
    areReadChecksAllowed(depth){

    }
    /**
     * @param {number} eventId 
     * @param {number} mapId
     */
    onEventStart(eventId,mapId){
        const eventCharacter = $gameMap.event(eventId);

        //宝箱除外設定など
        const isEventValid = this.isEventValid(eventCharacter);
        this._contents.setReadcheckEnable(isEventValid);        
        //開始時のマップイベントがある？
        if(eventId >0){
            const pageIndex =  eventCharacter._pageIndex;
            this._contents.setupEventData(eventId,pageIndex);
            this.setupMapData(mapId);
        }else{
            //自動実行・並列実行コモンイベントなので、無効にする
            this._contents.clearEventData();
        }
    }
    /**
     * @param {number} eventId 
     */
    onEventEnd(eventId){



    }
    clearTemp(){
        /**
         * @type {KidokTemp}
         */
        this._temp ={
            hasChanged:false,
            colorChanging:false,
            choiceTable:[],
        };
    }
    makeContents() {
        if(!this._contents){
            this._contents =new KidokuContents(this.version());
            this._set =new Set();
        }
    }
    version(){
        return PLUGIN_VERSION;
    }

    /**
     * @param {number} value 
     */
    saveKidokBits(value){
        if(value>0){
            this._set.add(value);
            this._temp.hasChanged = true;
        }else{
            const text =value.toString(16);
            throw new Error(`KidokuValue:${text}`);
        }
    }
    /**
     * @private
     * @param {KidokuContents} contents 
     */
    setContents(contents){
        this._contents = contents;
    }
    getSaveContents(){
        this.commitCurrentMapData();
        return this._contents;
    }
    /**
     * @param {KidokuContents} contents 
     */
    loadContents(contents){
        this.setContents(null);
        this._set =null;
        if(this.tryLoadContents(contents)){

        }
        this.makeContents();
    }
    /**
     * @param {KidokuContents} contents 
     */
    tryLoadContents(contents){
        if(!contents){
            return true;
        }
        try {
            if(PLUGIN_VERSION===contents.version()){
                const mapId = contents.mapId();
                const flags = contents.loadMapData(mapId);
                this.setContents(contents);
                this._set = new Set(flags);
  
                //TODO:kidokuStateのメソッドを呼び出す処理
                return true;
            }
        } catch (error) {
            console.error("Kidoku Load falled");
            console.table(contents);
        }
        return false;
    
    }

    /**
     * @returns 
     */
    currentEventId(){
        return this._contents.eventId();
    }

    /**
     * @param {number} depth 
     * @returns 
     */
    depthValid(depth){
        return depth <=7;
    }
    /**
     * @param {number} depth
     * @param {number} line 
     */
    onMessage(depth,line){
        if(this.canWriting() && this.depthValid(depth)){
            this._contents.setLastMassageLine(depth,line);
            const bits = this._contents.createMessageBit(depth,line);
            this.setLastTextState((this._set.has(bits)));
            this.saveKidokBits(bits);    
        }else{
            this.setLastTextState(false);
        }
        //まだ読んでないテキストなら、既読スキップモードを解除
        if(!this.needsColorChange()){
            this.terminateSkipMode();
        }
    }

    /**
     * @param {boolean} state 
     */
    setLastTextState(state){
        this._temp.colorChanging =state;
    }

    /**
     * @param {number} line 
     * @param {number} depth
     * @param {ReadonlyArray<string>} choice
     */
    onSetupChoice(line,depth,choice){
        if(this.canWriting() && this.depthValid(depth)){
            this._contents.setChoiceHedder(line);
            const xxx= choice.map( (text,index)=>{
                const bit = this._contents.createChoiceBit(depth,index);
                return this._set.has(bit);
            } );
            this._temp.choiceTable=xxx;    
        }else{
            this._temp.choiceTable=[];
        }
    }

    /**
     * @description 選択肢のジャンプ先ラベルを通過した時
     * @param {number} depth
     * @param {number} index 
     * @param {string} text 
     */
    onChoiceSelected(depth,index,text){
        if(this.canWriting()){
            const bit = this._contents.createChoiceBit(depth,index);
            this._temp.colorChanging=false;
            this.saveKidokBits(bit);
            this._contents.setChoiceHedder(NaN);    
        }
    }
    /**
     * @param {number} index 
     * @param {string} text 
     * @returns 
     */
    isSelectedChoice(index,text){
        return !!this._temp.choiceTable[index] ;
    }
    needsColorChange(){
        return this._temp.colorChanging;
    }
    kidokuTextColor(){
        return ColorManager.textColor(this._readonlryData.colorIndex);
    }
    /**
     * @param {number} index 
     */
    onChoiceDrawItem(index){
        this._temp.colorChanging =this.isSelectedChoice(index,"");
    }
    /**
     * @returns {Iterable<Number>}
     */
    currentMapValues(){
        return this._set;
    }
    
    /**
     * @param {number} newMapId 
     */
    onMadLoad(newMapId){
        this.commitCurrentMapData();
    }

    /**
     * @private
     * @param {number} mapId 
     */
    setupMapData(mapId){
        if(this._contents.mapId()!==mapId ){
            const flags= this._contents.loadMapData(mapId);
            this._set =new Set(flags);
            this._contents.setMapId(mapId);    
        }
    }
    /**
     * @description 現在マップの既読データをメインデータに転送する
     */
    commitCurrentMapData(){
        if( this._temp.hasChanged && this._set.size >0){
            this._contents.saveMapData(this._set);
            this._temp.hasChanged =false;
        }
    }
    mapNumbers(){
        return this._contents.mapNumbers();
    }
    /**
     * @returns {ReadonlyArray<number>}
     * @param {number} mapId 
     */
    getMapFlags(mapId){
        const  aa = this._contents._flags.getFlags(mapId);

        return aa;
    }
    isSheredStorage(){
        return true;
    }
    /**
     * @private
     * @returns {boolean}
     */
    canFileAccess(){
        return this.isSheredStorage() && this._sharedAccessState ==="";
    }
    /**
     * @private
     */
    fileAccessEnd(){
        this._sharedAccessState ="";
    }
    loadShared(){
        if(this.canFileAccess()){
            this._sharedAccessState ="load";
            //@ts-ignore
            StorageManager.loadObject(SAVE_FILE_NAME).then( (data)=>{
                if($dataSystem.advanced.gameId ===data.gameId){
                    this.loadContents(data.contents)
                }
                return 0;
            }).catch(()=>{
                this.makeContents();
            }).finally(()=>{
                this.fileAccessEnd()
            });
        }

    }
    saveShared(){
        if(this.canFileAccess()){
            this.commitCurrentMapData();
            this._sharedAccessState="save";
            $dataSystem;
            const saveObject ={
                gameId:$dataSystem.advanced.gameId,
                contents:this._contents,
            };
            //@ts-ignore
            StorageManager.saveObject(SAVE_FILE_NAME,saveObject).then(()=>{
                return 0;
            }).finally(()=>this.fileAccessEnd());
        }
    }
    canWriting(){
        return !this._parallelGuard && this._contents.canWrite();
    }
    /**
     * @param {boolean} value 
     */
    setParallelGuard(value){
        this._parallelGuard =value;
    }
    isSkipMode(){
        return $gameSwitches.value(this._readonlryData.skipModeSwtich);
    }
    needsMessageShowFast(){
        return this._temp.colorChanging && ( this.isSkipMode() || this.isCtrlSkipPressed() );
    }

    isCtrlSkipPressed(){
        return  this._readonlryData.ctrlSkip && this._temp.colorChanging && Input.isLongPressed("control");
    }
    terminateSkipMode(){
        $gameSwitches.setValue(this._readonlryData.skipModeSwtich,false);
    }
}

const KidokuManager=(()=>{
    const param = getParam();

    /**
     * @type {Iterable<string>}
     */
    const excludedCharacters =JSON.parse(param.excludedCharacters||"[]");


    /**
     * @type {KidokuReadonlryData}
     */
    const data ={
        ctrlSkip:true,
        colorIndex:Number(param.kidokuTextColorIndex||6),
        skipModeSwtich:Number(param.skipModeSwtich||0),
        excludedCharacters:new Set(excludedCharacters),
    };
    return new KidokuEventManager_T(data)
})();
// const Game_Map_unlockEvent=Game_Map.prototype.unlockEvent;
// Game_Map.prototype.unlockEvent =function(eventId){
//     KidokuManager.onMapEventEnd(this._mapId,eventId)

//     //イベントが終了した時
//     Game_Map_unlockEvent.call(this,eventId);
// };

const Game_Interpreter_command402 =Game_Interpreter.prototype.command402;
Game_Interpreter.prototype.command402 =function( params){

    const index= this._index;
    const result= Game_Interpreter_command402.call(this,params);
    if(index ===this._index ){

        KidokuManager.onChoiceSelected(this._depth,params[0],params[1]);
    }
    return result;
};

const Game_Interpreter_command101 =Game_Interpreter.prototype.command101
Game_Interpreter.prototype.command101 =function(/** @type {any} */ params){
    const index = this._index;
    const result =Game_Interpreter_command101.call(this,params);
    if(result){
        KidokuManager.onMessage(this._depth,index);
    }
    return result;
};
const Game_Interpreter_setupChoices =Game_Interpreter.prototype.setupChoices;
Game_Interpreter.prototype.setupChoices=function(/** @type {string[][]} */ params){
    KidokuManager.onSetupChoice(this._index,this._depth,params[0]);
    Game_Interpreter_setupChoices.call(this,params);
};
const Game_Interpreter_setup=Game_Interpreter.prototype.setup;
Game_Interpreter.prototype.setup=function(
    /** @type {any[]} */ list,
    /** @type {number} */ eventId
){
    Game_Interpreter_setup.call(this,list,eventId);
    if(eventId >0){
        if(this._depth ===0 ){
            KidokuManager.onEventStart(eventId,this._mapId);
            return;
        }
    }
    //コモンイベント呼び出しなので、空のデータを割り当てる
    KidokuManager.onEventStart(0,0);
};


const Game_Interpreter_terminate=Game_Interpreter.prototype.terminate;
Game_Interpreter.prototype.terminate =function(){
    if(this._depth===0 && this._eventId > 0){
        KidokuManager.onEventEnd(this._eventId);
    }

    Game_Interpreter_terminate.call(this);
};
const Game_CommonEvent_update=Game_CommonEvent.prototype.update;
Game_CommonEvent.prototype.update =function(){

    KidokuManager.setParallelGuard(true);
    Game_CommonEvent_update.call(this);
    KidokuManager.setParallelGuard(false);

};

const Window_Message_processColorChange=Window_Message.prototype.processColorChange;
Window_Message.prototype.processColorChange =function(/** @type {number} */ colorIndex){
    if(colorIndex ===0 && KidokuManager.needsColorChange() && !$gameParty.inBattle()){
        this.changeTextColor(KidokuManager.kidokuTextColor());
    }else{
        Window_Message_processColorChange.call(this,colorIndex);
    }
};

const Window_Message_resetTextColor=Window_Message.prototype.resetTextColor;
Window_Message.prototype.resetTextColor =function(){

    Window_Message_resetTextColor.call(this);
    if(KidokuManager.needsColorChange()){
        this.changeTextColor(KidokuManager.kidokuTextColor());
    }
};

const Window_Message_updateShowFast =Window_Message.prototype.updateShowFast;
Window_Message.prototype.updateShowFast =function(){
    Window_Message_updateShowFast.call(this);
    this._showFast = this._showFast ||KidokuManager.needsMessageShowFast();
};

const Window_Message_flushTextState =Window_Message.prototype.flushTextState;
Window_Message.prototype.flushTextState=function(textState){
    Window_Message_flushTextState.call(this,textState);
    if(
        !this._pauseSkip &&
         KidokuManager.needsMessageShowFast() &&
         this.isEndOfText(textState)
    ){
        this.pause = false;
        this._pauseSkip = true;
        this.startWait(2);
    }
};

const Window_Message_isTriggered=Window_Message.prototype.isTriggered;
Window_Message.prototype.isTriggered =function(){
    return Window_Message_isTriggered.call(this) ||  KidokuManager.needsMessageShowFast();
};

const Window_ChoiceList_processColorChange=Window_ChoiceList.prototype.processColorChange;
Window_ChoiceList.prototype.processColorChange =function(/** @type {number} */ colorIndex){
    if(colorIndex ===0 && KidokuManager.needsColorChange()){
        this.changeTextColor(KidokuManager.kidokuTextColor());
    }else{
        Window_ChoiceList_processColorChange.call(this,colorIndex);
    }
};
const Window_ChoiceList_resetTextColor=Window_ChoiceList.prototype.resetTextColor;
Window_ChoiceList.prototype.resetTextColor =function(){

    Window_ChoiceList_resetTextColor.call(this);
    if(KidokuManager.needsColorChange()){
        this.changeTextColor(KidokuManager.kidokuTextColor());
    }
};
const Window_ChoiceList_drawItem =Window_ChoiceList.prototype.drawItem ;
Window_ChoiceList.prototype.drawItem =function(/** @type {number} */ index){
    KidokuManager.onChoiceDrawItem(index);
    Window_ChoiceList_drawItem.call(this,index);
};

const DataManager_createGameObjects=DataManager.createGameObjects;
DataManager.createGameObjects =function(){
    KidokuManager.makeContents();
    DataManager_createGameObjects.call(this);
};
const DataManager_loadMapData=DataManager.loadMapData;
DataManager.loadMapData =function(mapId){
    KidokuManager.onMadLoad(mapId);
    DataManager_loadMapData.call(this,mapId);
};

const DataManager_extractSaveContents =DataManager.extractSaveContents;
DataManager.extractSaveContents =function(contents){
    DataManager_extractSaveContents.call(this,contents);
    if(!KidokuManager.isSheredStorage()){
        const kidokuContents = contents[SAVEDATA_KEY];
        KidokuManager.loadContents(kidokuContents);
    }
};


const DataManager_saveGame=DataManager.saveGame;
DataManager.saveGame =function(fileId){
    const result =DataManager_saveGame.call(this,fileId);
    KidokuManager.saveShared();
    return result;

};
const Scene_Boot_loadPlayerData=Scene_Boot.prototype.loadPlayerData;
Scene_Boot.prototype.loadPlayerData =function(){
    Scene_Boot_loadPlayerData.call(this);
    KidokuManager.loadShared();
};

class Window_KidokuMapList extends Window_Selectable{
    initialize(rect){
        /**
         * @type {ReadonlyArray<number>}
         */
        this._list=[];
        super.initialize(rect);
    }
    /**
     * 
     * @param {Window_KidokuFlags} fw 
     */
    setFlagWindow(fw){
        this._flagWindow=fw;
    }
    /**
     * @param {number} index 
     */
    select(index){
        super.select(index);
        if(this._flagWindow){
            const mapId = this.itemAt(index);
            const flags =KidokuManager.getMapFlags(mapId);
            this._flagWindow.setContents(flags,mapId);            
        }
    }
    /**
     * @param {number} mapId
     */
    selectOf(mapId){
        const center = Math.round(this._list.length/2);
        const centerValue =this._list[center];
        if(mapId <centerValue){
            const index= this._list.indexOf(mapId);
            this.select(index);            
        }else{
            const index= this._list.lastIndexOf(mapId);
            this.select(index);            
        }
        if(this._index <=0){
            this.select(0);
        }
    }
    /**
     * @param {ReadonlyArray<number>} numbers 
     */
    setMapNumbers(numbers){
        this._list =numbers;
        this.refresh();
    }
    maxItems(){
        return this._list.length;
    }
    /**
     * @param {number} index 
     * @returns 
     */
    itemAt(index){
        return this._list[index];
    }
    updateHelp(){
        const text =this.mapNameText(this.index());
        this._helpWindow.setText(text);
    }
    /**
     * @param {number} index
     */
    mapNameText(index){
        const mapId = this.itemAt(index);
        if(isNaN(mapId)){
            return "";
        }
        const mapIdText = mapId.toString().padStart(4,"0");
        const info= $dataMapInfos[mapId];
        return `Map${mapIdText}:${info ? info.name : ""}`;
    }
    /**
     * @param {number} index 
     */
    drawItem(index){
        const mapNumber = this.itemAt(index);
        if(isNaN(mapNumber)){
            return;
        }
        const rect = this.itemRectWithPadding(index);
        const mapNumberText = `Map${mapNumber.toString().padStart(4,"0")}`;
        this.drawText(mapNumberText,rect.x,rect.y,rect.width);
    }
}

class Window_KidokuFlags extends Window_Selectable{
    /**
     * @param { Rectangle} rect
     */
    initialize(rect){
        /**
         * @type {ReadonlyArray<number>}
         */
        this._list=[];
        super.initialize(rect);
    }
    /**
     * @param {Iterable<number>} list 
     * @param {number} mapId
     */
    setContents(list,mapId){
        this._mapId=mapId;
        this._list =Array.from(list);
        this.refresh();
    }
    /**
     * @param {number} mapId 
     */
    setMapId(mapId){
        this._list = KidokuManager.getMapFlags(mapId);
        this._mapId =mapId;
        this.refresh();
    }
    /**
     * @returns 
     */
    maxItems(){
        return this._list.length;
    }
    /**
     * @param {number} value 
     * @returns 
     */
    obtainPageIndex(value){
        const masked = (0x1f00 & value);
        const pageIndex = masked >>8;
        return pageIndex ;
    }
    /**
     * @param {number} value 
     * @returns 
     */
    obtainEventId(value){
        return (0xff & value);
    }

    /**
     * @param {number} value 
     */
    obtainEventName(value){
        const eventId = this.obtainEventId(value);
        if(this._mapId ===$gameMap.mapId()){
            const eventCharacter = $gameMap.event(eventId);
            if(eventCharacter){
                return eventCharacter.event().name;
            }
        }
        return `EV${eventId.toString().padStart(3,"0")}`;
    }
    /**
     * @param {number} value 
     */
    createHelpText(value){
        const pageIndex =this.obtainPageIndex(value);
        const eventId = this.obtainEventId(value);

        // if(!eventCharacter){
        //     return "";
        // }
        // const eventData =eventCharacter.event();
        // const page =eventData.pages[pageIndex];

        return `page:${pageIndex+1}`;


    }
    updateHelp(){
        const value = this.valueAt(this.index());
        if(isNaN(value)){
            this._helpWindow.clear();
        }else{
            const text = this.createHelpText(value);
            this._helpWindow.setText(text);
        }
    }

    /**
     * @param {number} index 
     * @returns 
     */
    valueAt(index){
        return this._list[index];
    }

    /**
     * @param {number} index 
     */
    drawItem(index){
        if(index < this._list.length){
            const rect = this.itemLineRect(index);
            const value = this.valueAt(index);
            const eventName = this.obtainEventName(value);
            const text =value.toString(16).padStart(12,"0");
            this.drawText(text,rect.x,rect.y,rect.width,"right");
            this.drawText(eventName,rect.x,rect.y,rect.width,"left");
        }
    }
    
}

class Scene_KidokuView extends Scene_MenuBase{
    create(){
        super.create();
        this.createAllWindows();
    }

    createAllWindows(){
        this.createHelpWindow();
        this.createMapListWindow();
        this.createKidokFlagWindow();
        this.windowLink();
    }
    windowLink(){
        this._mapListWindow.setHelpWindow(this._helpWindow);
        this._mapListWindow.setFlagWindow(this._flagListWindow);
        this._mapListWindow.selectOf($gameMap.mapId());
        this._mapListWindow.callUpdateHelp();
    }
    createMapListWindow(){
        const rect = this.mapListWindowRect();

        const mw = new Window_KidokuMapList(rect);
        mw.setMapNumbers(KidokuManager.mapNumbers());
        mw.setHandler("ok",this.onMapListOk.bind(this));
        mw.setHandler("cancel",this.popScene.bind(this));
        this._mapListWindow=mw;
        this.addWindow(mw);

    }
    onMapListOk(){
        this._flagListWindow.select(0);
        this._flagListWindow.activate();
    }
    mapListWindowRect(){
        const width = this.numberWindowWidth();
        return new Rectangle(0,this.mainAreaTop(),width,this.mainAreaHeight());
    }
    numberWindowWidth(){
        return 160;
    }
    kidokuFlagRect(){
        const x = this.numberWindowWidth();
        const width = Graphics.boxWidth -x
        return new Rectangle(x,this.mainAreaTop(),width,this.mainAreaHeight());
    }
    createKidokFlagWindow(){
        const rect=this.kidokuFlagRect();
        const fw = new Window_KidokuFlags(rect);
        fw.setContents(KidokuManager.currentMapValues(),$gameMap.mapId());
        fw.activate();
        fw.setHandler("cancel",this.onKidokuFlagCancel.bind(this));
        fw.setHelpWindow(this._helpWindow);
        fw.select(0);
        this._flagListWindow=fw;
        this.addWindow(fw);
    }
    onKidokuFlagCancel(){
        this._flagListWindow.deselect();
        this._mapListWindow.activate();
    }
    

}


PluginManager.registerCommand(PLUGIN_NAME,"DisplayKidokuState",()=>{
    KidokuManager.commitCurrentMapData();
    SceneManager.push(Scene_KidokuView);
});

// /**
//  * 
//  * @param {Game_Interpreter} inter 
//  * @param {boolean} value 
//  */
// function xxxxEEE(inter,value){
//     //@ts-ignore
//     inter._kidoku=value;

// }

// /**
//  * @param {Game_Interpreter} inter 
//  */
// const isFlagOk=(inter)=>{
//     //@ts-ignore
//     return !!inter._kidoku

// }
PluginManager.registerCommand(PLUGIN_NAME,"DisableReadCheck",function(){

    if(this._depth ===0){
        KidokuManager.setReadcheckEnable(false);

    }
});
PluginManager.registerCommand(PLUGIN_NAME,"EnableReadCheck",function(){
    if( this._depth===0   &&
        this._eventId > 0 &&
        this._eventId == KidokuManager.currentEventId()
    ){
        KidokuManager.setReadcheckEnable(true);
    }

});
/**
 * @param {Function} class_ 
 * @param {String} name 
 */
 function classExport(class_,name){
    window[name] = class_;
}
//class文で書くと、異なる名前空間に置かれる
//そのため、この処理で再設定が必要
const classList=[
    KidokuEventData,KidokuEventState,KidokuFlags,KidokuContents
];

for (const iterator of classList) {
    classExport(iterator,iterator.name);
}


}());
