
/*:
 * @plugindesc Add Option to Message log
 * @author siguren
 * 
 * @target MZ
 * @base ManoUZ_MessageLog
 * 
 * @command OpenScene
 * @text Open Option Screen
 * 
 * @param optionText
 * @desc Sets display name on Option Screen.
 * @default Message log Setting
 * 
 * @param textON
 * @default ON
 * 
 * @param textOFF
 * @default OFF
 * 
 * @param logLimitBasicText
 * @text Basic number of logs saved:Item Name
 * @default Basic number of logs saved
 * 
 * @param logLimitBasicHelp
 * @text Basic number of logs saved:Description
 * @default Affects the size of saved data.
 * @parent logLimitBasicText
 * 
 * @param logLimitBasicItemList
 * @text Number of logs saved:List
 * @type struct<EnumItem>[]
 * @default ["{\"text\":\"Few\",\"value\":\"20\"}","{\"text\":\"Standard\",\"value\":\"40\"}","{\"text\":\"Many\",\"value\":\"60\"}","{\"text\":\"Extreme\",\"value\":\"100\"}"]
 * @parent logLimitBasicText
 * 
 * @param logLimitExtendsText
 * @text Number of logs saved/added:Item Name
 * @default Number of logs saved(Add)
 * 
 * @param logLimitExtendsHelp
 * @type multiline_string
 * @text Number of logs saved/added:Description
 * @default Affects memory consumption.
 * @parent logLimitExtendsText
 * 
 * @param logLimitExtendsItemList
 * @text Number of logs saved/added:List
 * @desc This item is for changing settings.
 * @type struct<EnumItem>[]
 * @default ["{\"text\":\"Few\",\"value\":\"20\"}","{\"text\":\"Standard\",\"value\":\"50\"}","{\"text\":\"Many\",\"value\":\"80\"}"]
 * @parent logLimitExtendsText
 * 
 * @param autoWaitTimeText
 * @text Auto Read:Item Name
 * @default Auto Read Wait Time
 * 
 * @param autoWaitTimeHelp
 * @type multiline_string
 * @text Auto Read Wait Time:Description
 * @default Waiting time after sentence is displayed.
 * @parent autoWaitTimeText
 * 
 * @param autoWaitTimeItemList
 * @text Auto Read Wait Time:Item List
 * @type struct<EnumItem>[]
 * @default ["{\"text\":\"Short\",\"value\":\"180\"}","{\"text\":\"Standard\",\"value\":\"360\"}","{\"text\":\"Long\",\"value\":\"480\"}","{\"text\":\"Very Long\",\"value\":\"600\"}"]
 * @parent autoWaitTimeText
 * 
 * @param gaugeColorText
 * @text Gauge Color:Item Name
 * @type string
 * @default Gauge Color
 * 
 * @param gaugeColorHelp
 * @type multiline_string
 * @text Gauge Color:Description
 * @default The color of the gauge
 * that appears during the Auto Read Wait Time.
 * @parent gaugeColorText
 * 
 * @param gaugeColorItemListOld
 * @text Gauge Color List
 * @type number[]
 * @desc Numeric Value
 * Manually convert a hexadecimal number to a decimal number.
 * @default ["13181999","12674371","8104783","3050327","45797","20362","16628736"]
 * @parent gaugeColorText
 * 
 * @param keepAutoMode
 * @text Keep Auto Read Mode:Item Name
 * @default Keep Auto Read Mode
 * 
 * @param keepAutoModeHelp
 * @type multiline_string
 * @text Keep Auto Read Mode:Description
 * @default After the conversation has ended, keeps Auto Read Mode on.
 * @parent keepAutoMode
 * 
 * @param machinakaMode
 * @text NPC convo Mode Line:Item Name
 * @default NPC convo Mode Line
 * 
 * @param machinakaModeHelp
 * @text NPC convo Mode Line:Description
 * @type multiline_string
 * @default number for simultaneous displays in NPC convo Mode.
 * @parent machinakaMode
 * 
 * 
 * 
 * @help
 * Attach optional features to ManoUZ_MessageLog.
 * 
 * The main unit has Plugin Commands for changing values directly.
 * 
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////

/*:ja
 * @plugindesc メッセージログにオプションを追加
 * @author しぐれん
 * 
 * @target MZ
 * @base ManoUZ_MessageLog
 * 
 * @command OpenScene
 * @text オプション画面を開く/OpenScene
 * 
 * @param optionText
 * @text オプション画面での表示名
 * @default メッセージログ設定
 * 
 * @param textON
 * @default ON
 * 
 * @param textOFF
 * @default OFF
 * 
 * 
 * @param logLimitBasicText
 * @text ログ保存数(基本)：項目名
 * @default ログ保存数(基本)
 * 
 * @param logLimitBasicHelp
 * @text ログの保存数(基本)：解説
 * @default セーブデータの大きさに影響します。
 * @parent logLimitBasicText
 * 
 * @param logLimitBasicItemList
 * @text ログ保存数：候補リスト
 * @type struct<EnumItem>[]
 * @default ["{\"text\":\"少ない\",\"value\":\"20\"}","{\"text\":\"標準\",\"value\":\"40\"}","{\"text\":\"多い\",\"value\":\"60\"}","{\"text\":\"とても多い\",\"value\":\"100\"}"]
 * @parent logLimitBasicText
 * 
 * @param logLimitExtendsText
 * @text ログ保存数・追加分：項目名
 * @default ログ保持数(追加)
 * 
 * @param logLimitExtendsHelp
 * @text ログ保存数・追加分：説明文
 * @default メモリの消費量に影響します。
 * @parent logLimitExtendsText
 * 
 * @param logLimitExtendsItemList
 * @text 追加のログ保存数：項目リスト
 * @desc 設定変更用の項目です。
 * @type struct<EnumItem>[]
 * @default ["{\"text\":\"少ない\",\"value\":\"20\"}","{\"text\":\"標準\",\"value\":\"50\"}","{\"text\":\"多い\",\"value\":\"80\"}"]
 * @parent logLimitExtendsText
 * 
 * @param autoWaitTimeText
 * @text オート待ち時間：項目名
 * @default オート待ち時間
 * 
 * @param autoWaitTimeHelp
 * @text オート待ち時間：説明文
 * @default 文章表示完了後の待機時間です。
 * @parent autoWaitTimeText
 * 
 * @param autoWaitTimeItemList
 * @text オート待ち時間:項目リスト
 * @type struct<EnumItem>[]
 * @default ["{\"text\":\"短い\",\"value\":\"180\"}","{\"text\":\"標準\",\"value\":\"360\"}","{\"text\":\"長い\",\"value\":\"480\"}","{\"text\":\"とても長い\",\"value\":\"600\"}"]
 * @parent autoWaitTimeText
 * 
 * @param gaugeColorText
 * @text ゲージ色：項目名
 * @type string
 * @default ゲージ色
 * 
 * @param gaugeColorHelp
 * @text ゲージ色：説明文
 * @type string
 * @default オート待ち時間の間に表示するゲージの色。
 * @parent gaugeColorText
 * 
 * 
 * @param gaugeColorItemListOld
 * @text ゲージ色一覧
 * @type number[]
 * @desc 数値指定です。
 * 各自で16進数を10進数に変換して設定します。
 * @default ["13181999","12674371","8104783","3050327","45797","20362","16628736"]
 * @parent gaugeColorText
 * 
 * 
 * @param keepAutoMode
 * @text オートモード保持：項目名
 * @default オートモードを保持
 * 
 * @param keepAutoModeHelp
 * @text オートモード保持：説明文
 * @default 会話終了後もオートモードを維持します。
 * @parent keepAutoMode
 * 
 * @param machinakaMode
 * @text 街中モード行数：項目名
 * @default 街中モード行数
 * 
 * @param machinakaModeHelp
 * @text 街中モード行数：説明文
 * @type string
 * @default 表示する数を増やすと、その分ログを表示する場所が減ります。
 * @parent machinakaMode
 * 
 * 
 * 
 * @help
 * ManoUZ_MessageLogにオプション機能を取り付けます。
 * 
 * 数値を直接変更するプラグインコマンドは本体側にあります。
 * 
*/
/*~struct~EnumItem:
 * @param text
 * @type string
 * 
 * @param value
 * @type number
 * @default 0
*/


(function(){
    'use strict';

/**
 * @typedef {Object} OptionParametor
 * @property {number} logLimitBasic
 * @property {number} logLimitExtends
 * @property {number} autoWaitTime
 * @property {boolean} keepingAutoMode
 * @property {number} gaugeColor
 * @property {number} machinakaLines
 */

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

/**
 * @returns {OptionParametor}
 */
function getOption(){
    return ManoUZ_MessageLog.cloneOptionValue()
}
/**
 * @param {OptionParametor} option 
 */
function setOption(option){
    ManoUZ_MessageLog.setOptionValue(option);
}

class Option_ItemBase{
    /**
     * 
     * @param {string} key 
     * @param {string} name
     * @param {string} help
     */
    constructor(key,name,help){
        this._key=key;
        this._help =help;
        this._name=name;

    }
    helpText(){
        return this._help;
    }
    key(){
        return this._key;
    }

    name(){
        return this._name;
    }
    /**
     * @param {number} index 
     * @returns 
     */
    text(index){
        return "";
    }
    /**
     * 
     * @param {number} index 
     * @returns 
     */
    textColor(index){
        return ColorManager.normalColor();
    }
    /**
     * @param {number} value 
     * @returns 
     */
    indexOf(value){
        const maxItems = this.maxItems();
        for(let i =0; i < maxItems; ++i){
            const vv = this.value(i);
            if(vv ===value){
                return i;
            }
        }
        return 0;
    }
    maxItems(){
        return 1;
    }
    /**
     * @returns {number}
     * @param {number} _index
     */
    value(_index){
        return 0;
    }
}
/**
 * @typedef {object} ValueTextPair
 * @property {number} value
 * @property {string} text
 */


class OptionItemEnum extends Option_ItemBase{

    /**
     * @param {string} key
     * @param {string} name
     * @param {string} help
     * @param {ValueTextPair[]} list 
     */
    constructor(key,name,help,list){
        super(key,name,help);
        //this._defaultValue=defaultValue;

        this._list=list;

    }
    /**
     * @param {string} key
     * @param {string} name 
     * @param {string} helpText
     * @param {string} listText 
     */
    static create(key,name,helpText,listText){
        /**
         * @type {string[]}
         */
        const listObj =JSON.parse(listText ||"[]");

        return new OptionItemEnum(key,name,helpText,listObj.map( (text)=>{
            const obj = JSON.parse(text);
            return {
                text:obj.text,
                value:Number(obj.value),
            }
        }));
    }

    maxItems(){
        return this._list.length;
    }
    /**
     * @param {number} index 
     * @returns 
     */
    text(index){
        const item = this._list[index];
        if(item){
            return item.text;
        }
        return "";
    }
    /**
     * 
     * @param {number} index 
     */
    value(index){
        const item = this._list[index];
        if(item){
            return item.value;
        }
        return 0;
    }

}
/**
 * @param {string} key
 * @param {string} name 
 * @param {string} helpText
 * @param {string} listText 
 */
function createEnumOption(key,name,helpText,listText){
    return OptionItemEnum.create(key,String(name),String(helpText),listText);
}

class OptionItem_Boolean extends Option_ItemBase{
    /**
     * @param {number} index 
     * @returns 
     */
    value(index){
        return index;
    }
    /**
     * @param {number} index 
     * @returns 
     */
    text(index){
        return index=== 0 ?  "OFF":"ON";
    }
    maxItems(){
        return 2;
    }
}

class OptionItem_Number extends Option_ItemBase{
    /**
     * @param {string} key
     * @param {string} name
     * @param {string} help
     * 
     * @param {number} min 
     * @param {number} max 
     */
    constructor(key,name,help,min,max){
        super(key,name,help);
        this._min =min;
        this._max =max;
        this._name =name;
    }
    /**
     * @param {number} index 
     * @returns 
     */
    text(index){
        return this.value(index).toString();
    }
    maxItems(){
        return this._max -this._min;
    }
    /**
     * 
     * @param {number} index 
     * @returns 
     */
    value(index){
        return index +this._min;
    }

}

class OptionItemColorSelect extends Option_ItemBase{
    /**
     * @param {string} name 
     * @param {string} help
     * @param {number[]} colorList
     */
    constructor(name,help,colorList){
        super("color",name,help);

        this._colorList = colorList;
    }

    text(){
        return "????";
    }
    /**
     * @param {number} index 
     * @returns 
     */
    textColor(index){
        const color = this.value(index);
        const _0x =color.toString(16)
        const zzz =_0x.padStart(6,"0")

        return `#${zzz}`;
    }
    maxItems(){
        return this._colorList.length;
    }
    /**
     * 
     * @param {number} index 
     * @returns 
     */
    value(index){
        return this._colorList[index];
    }
}

/**
 * @template {Option_ItemBase} OptionItemType
 */
class OptionItemCase{
    /**
     * 
     * @param {OptionItemType} obj 
     * @param {(opt:OptionParametor)=>number} optionReadFunc
     * 
     */
    constructor(obj,optionReadFunc){
        this._index=0;
        this._item=obj;
        this._readFunc =optionReadFunc;
    }
    /**
     * @param {OptionParametor} option 
     */
    readIndex(option){
        const value = this._readFunc(option)
        this._index= this._item.indexOf(value);
    }
    helpText(){
        return this._item.helpText();
    }
    name(){
        return this._item.name();
    }
    text(){
        return this._item.text(this._index);
    }
    textColor(){
        return this._item.textColor(this._index);
    }
    changePrev(){
        if(this._index > 0){
            this._index -=1;
            return true;
        }
        this._index = this._item.maxItems()-1;
        return true;
    }
    changeNext(){
        if(this._index + 1 < this._item.maxItems() ){
            this._index +=1;
            return true;
        }
        this._index =0;
        return true;
    }
    /**
     * @returns {OptionItemType | Option_ItemBase}
     */
    item(){
        return this._item;
    }
    maxItems(){
        return this._item.maxItems();
    }
    value(){
        return this._item.value(this._index);
    }
    /**
     * @param {number} value 
     */
    selectValue(value){
        const index = this._item.indexOf(value);
        this._index =index;
    }
}



class OptionClass{
    /**
     * 
     * @param {Option_ItemBase[]} list 
     */
    constructor(list){
        /**
         * @type {Map<string,OptionItemCase< Option_ItemBase > >}
         */
        this._map = new Map();
        this.addItem(setting.logLimitBasic,(opt)=> opt.logLimitBasic);
        this.addItem(setting.logLimitExtends,(opt)=>opt.logLimitExtends);
        this.addItem(setting.autoSpeed,(opt)=>opt.autoWaitTime);
        this.addItem(setting.keepAutoMode,(opt)=>opt.keepingAutoMode ? 1:0);
        this.addItem(setting.colorList,(opt)=> opt.gaugeColor);
        this.addItem(setting.machinaka,(opt)=> opt.machinakaLines);

    }
    /**
     * 
     * @param {string} key 
     */
    getValue(key){
        const item = this._map.get(key);
        if(item){
            return item.value();
        }
        return 0;
    }

    /**
     * @returns {OptionParametor}
     *  
     */
    createOption(){
        const autokeep =this.getValue(setting.keepAutoMode.key()) !==0
        return {
            autoWaitTime :this.getValue(setting.autoSpeed.key()),
            logLimitBasic:this.getValue(setting.logLimitBasic.key()),
            logLimitExtends:this.getValue(setting.logLimitExtends.key()),
            keepingAutoMode:autokeep,
            gaugeColor:this.getValue(setting.colorList.key()),
            machinakaLines:this.getValue(setting.machinaka.key()),
        }
    }

    /**
     * @private
     * @param {Option_ItemBase} item 
     * @param {(option:OptionParametor)=>number} func
     */
    addItem(item,func){
        const key = item.key();
        this._map.set(key,new OptionItemCase(item,func));
    }
    /**
     * 
     * @param {OptionParametor} option 
     */
    setOption(option){
        for (const iterator of this._map.values()) {
            iterator.readIndex(option);
        }
    }
    /**
     * @returns 
     */
    createKeyList(){
        return Array.from(this._map.keys() );
    }
    /**
     * @param {string} key 
     * @returns {OptionItemCase<Option_ItemBase>}
     */
    getItem(key){
        return this._map.get(key);
    }   
}

const setting = (function(){
    const param =getParam();
    const logLimitBasic = createEnumOption(
        "logbasic",
        param.logLimitBasicText,
        param.logLimitBasicHelp ||"",
        (param.logLimitBasicItemList||"[]")
    );

    const logLimitExtends = createEnumOption(
        "logextends",
        param.logLimitExtendsText,
        (param.logLimitExtendsHelp ||""),
        (param.logLimitExtendsItemList||"[]")
    );

    const autoSpeed =createEnumOption(
        "autospeed",
        param.autoWaitTimeText,
        param.autoWaitTimeHelp,
        (param.autoWaitTimeItemList||"[]")
    );

    /**
     * @type {String[]}
     */
    const colorArray =JSON.parse(param.gaugeColorItemListOld);
    const colorList = new OptionItemColorSelect(
        String(param.gaugeColorText),
        String(param.gaugeColorHelp),
        colorArray.map( (c)=>  Number(c) )
    );
    const keepAutoMode = new OptionItem_Boolean(
        "autokeep",
        String(param.keepAutoMode),
        String(param.keepAutoModeHelp)
    );

    const machinaka = new OptionItem_Number(
        "machinaka",        
        String(param.machinkaMode||param.machinakaMode),
        String(param.machinakaModeHelp||param.machinkaModeHelp),
        1,9);

    const optionText =String(param.optionText);

    const result ={
        optionText,
        logLimitBasic,
        logLimitExtends,
        colorList,
        autoSpeed,
        keepAutoMode,
        machinaka,
    };
    return result;
})();


class Window_LogOption extends Window_Selectable{
    /**
     * @param {Rectangle} rect 
     */
    constructor(rect){
        super(rect);
        this._optionClass = new OptionClass(setting.itemList);
        this._list = this._optionClass.createKeyList();
    }
    /**
     * 
     * @param {OptionParametor} value 
     */
    setOption(value){
        this._optionClass.setOption(value);
        this.refresh();
    }
    /**
     *  @returns {OptionParametor}
     */
    createOption(){
        return this._optionClass.createOption();
    }
    maxItems(){
        if(this._list){
            return this._list.length;
        }
        return 0;
    }
    changeNextValue(){
        const index = this.index();
        const key = this._list[index];
        const item = this._optionClass.getItem(key);
        if(item){
            if(item.changeNext()){
                this.playCursorSound();
                this.redrawItem(index);    
            }
        }
    }
    changePrevValue(){
        const index = this.index();
        const key = this._list[index];
        const item = this._optionClass.getItem(key);
        if(item){
            if(item.changePrev()){
                this.playCursorSound();
                this.redrawItem(index);    
            }
        }
    }
    cursorLeft(){
        this.changePrevValue();
    }
    playOkSound(){}
    cursorRight(){
        this.changeNextValue()
    }

    /**
     * 
     * @param {OptionItemCase<Option_ItemBase>} item 
     * @param {Rectangle} rect 
     */
    drawItemText(item,rect){
        const color = item.textColor();
        this.changeTextColor(color);
        this.drawText(item.text(),rect.x,rect.y,rect.width,"right");

    }
    /**
     * @param {number} index 
     */
    itemAt(index){
        const key = this._list[index];
        if(!key){
            return null;
        }
        return this._optionClass.getItem(key);
    }
    /**
     * @param {number} index 
     */
    drawItem(index){
        const item = this.itemAt(index);
        if(item){
            const rect = this.itemRectWithPadding(index);
            const nameWidth = Math.min(rect.width,200);
            const name = item.name()||"";
            this.drawTextEx(name,rect.x,rect.y,nameWidth);
            if(item.item()!==setting.colorList){
                this.drawItemText(item,rect);
            }else{
                const colorRect = new Rectangle(rect.x +240,rect.y + 2,rect.width- 240,rect.height-4);
                const color = item.textColor()
                this.drawColorBar(colorRect,color);
            }
        }
    }
    /**
     * @param {Rectangle} rect 
     * @param {string} color
     */
    drawColorBar(rect,color){
        this.contents.fillRect(rect.x,rect.y,rect.width,rect.height,color);
    }
    updateHelp(){
        const item = this.itemAt(this.index());
        if(item){
            this._helpWindow.setText(item.helpText());
        }
    }


}
/**
 * 
 * @param {string} symbol 
 * @param {Sprite_Button} other 
 * @param {number} buttonY
 * @param {number} spacing 
 */
function createButton(symbol,other,buttonY,spacing){
    const button =new Sprite_Button(symbol);
    button.x = other.x - button.width -spacing;
    button.y = buttonY;
    return button;
}

class Scene_MessageLogOption extends Scene_MenuBase{
    constructor(){
        super();
        this._helpWindow =null;
        this._opitonWindow=null;
    }
    create(){
        super.create();
        this.createAllWindows();
    }
    createAllWindows(){
        this.createHelpWindow();
        this.createLogOpitonWindow();

    }
    logWindowRect(){
        const x = 0;
        const y = this.mainAreaTop();
        const w = Graphics.boxWidth;
        const h = this.mainAreaHeight();
        return new Rectangle(x,y,w,h);
    }
    createButtons(){
        super.createButtons();
        if(ConfigManager.touchUI){
            this.createCursorButton();
        }
    }
    
    createCursorButton(){
        if(!this._cancelButton){
            return;
        }
        const spacing = 4;
        const buttonY = this.buttonY();

        const right =createButton("pagedown",this._cancelButton,buttonY,spacing);
        const left =createButton("pageup",right,buttonY,spacing);

        this.addWindow(right);
        this.addWindow(left);


    }
    createLogOpitonWindow(){
        const low = new Window_LogOption(this.logWindowRect());
        low.setOption(getOption());
        low.setHandler("ok",this.onOptionOk.bind(this));
        low.setHandler("cancel",this.saveOption.bind(this));
        low.setHandler("pageup",this.onOptionPageup.bind(this));
        low.setHandler("pagedown",this.onOptionPagedown.bind(this));

        this._opitonWindow=low;
        this.addWindow(low);
        low.select(0);
        low.setHelpWindow(this._helpWindow);
        low.activate();
    }
    onOptionOk(){
        this._opitonWindow.changeNextValue();
        this._opitonWindow.activate();
    }
    onOptionPageup(){
        this._opitonWindow.changePrevValue();
        this._opitonWindow.activate();
    }
    onOptionPagedown(){
        this._opitonWindow.changeNextValue();
        this._opitonWindow.activate();
    }

    saveOption(){
        const value =this._opitonWindow.createOption();
        setOption(value);
        this.popScene();
    }
}

const MESSAGELOG_OPTION ="MSGLOG_OPTION";

const Window_Options_makeCommandList =Window_Options.prototype.makeCommandList;

Window_Options.prototype.makeCommandList =function(){
    Window_Options_makeCommandList.call(this);
    this.addCommand(setting.optionText,MESSAGELOG_OPTION);
};
const Window_Options_drawItem=Window_Options.prototype.drawItem;
Window_Options.prototype.drawItem =function(index){

    const symbol = this.commandSymbol(index);
    if(symbol ===MESSAGELOG_OPTION){
        const rect = this.itemRectWithPadding(index);
        this.drawTextEx(setting.optionText,rect.x,rect.y,rect.width);
    }else{
        Window_Options_drawItem.call(this,index);
    }
};
const Window_Options_processOk=Window_Options.prototype.processOk;
Window_Options.prototype.processOk =function(){
    Window_Options_processOk.call(this);
    if(SceneManager.isSceneChanging()){
        return;
    }
    const symbol = this.currentSymbol();
    if(symbol ===MESSAGELOG_OPTION){        
        SceneManager.push(Scene_MessageLogOption);
        return;
    }
};

PluginManager.registerCommand(PLUGIN_NAME,"OpenScene",()=>{
    SceneManager.push(Scene_MessageLogOption);
})

}());
