package com.huangzj.base.module;

import lombok.Getter;
import lombok.Setter;

/**
 * 玩家侧逻辑模块的骨架：tick、存档、登录回调都先空着，子类按需填。
 * 和修仙那套 GeneralModule 思路类似，这里做了精简，只保留 demo 要用的钩子。
 */
@Getter
@Setter
public abstract class GeneralModule<P> extends AbsHostModule<P> {

    /** tick / 存盘时共用的锁，避免和业务线程打架（demo 里只用在 runTick）。 */
    protected final Object saveLockObj = new Object();

    /** 是否有脏数据要同步，具体怎么用看业务。 */
    protected boolean needSync;

    /** 是否已经进入过登录后流程，占位字段。 */
    protected boolean enterAfterLogin;

    /** 为 true 时才会被 {@link com.huangzj.base.spring.module.PlayerModuleRegistry#runTickAll(long)} 扫到。 */
    private boolean enableTick;

    protected GeneralModule(P player) {
        super(player);
    }

    /** 标记需要同步，一般配合 syncChange 使用。 */
    public void setNeedSync() {
        this.needSync = true;
    }

    /** 同步做完了就清掉标记。 */
    public void finishSync() {
        this.needSync = false;
    }

    /** 当前模块归属的玩家 id，子类必须给出。 */
    public abstract long getPlayerId();

    /** 拉档，默认 true 表示不用拦。 */
    public boolean loadData() {
        return true;
    }

    /** 拉档后的二次处理，默认放行。 */
    public boolean afterLoadData() {
        return true;
    }

    /** 存盘，子类可写 Mongo/Redis；默认啥也不干。 */
    public boolean saveData() {
        return true;
    }

    /** 把内存改动推到客户端或其它服，force 是否强推由子类解释。 */
    public void syncChange(boolean force) {
    }

    /** 模块类型，默认就是具体类。 */
    public Class<?> getModuleClass() {
        return getClass();
    }

    /** 登录完成要下发的消息，占位。 */
    public void loginSendMsg(boolean isReconnect) {
    }

    /** 登录后逻辑，占位。 */
    public void afterLogin(boolean isReconnect) {
    }

    /** 下线清理，占位。 */
    public void offline() {
    }

    /** 处理游戏内推送/内部消息，占位。 */
    public void handleGameMsg(Object msg) {
    }

    /** 红点刷新，占位。 */
    public void refreshRedPoint() {
    }

    /** 跨天/重置类逻辑，占位。 */
    public void dailyResetData() {
    }

    /** 打开定时 tick，注册表只会调度 isEnableTick 为 true 的模块。 */
    public void enableTick() {
        this.enableTick = true;
    }

    /** 若要做精准调度可覆写；默认 0 表示「不承诺下次时间点」。 */
    public long calcNextTickAt() {
        return 0L;
    }

    /**
     * 外层统一包一层锁和异常，真正的周期逻辑写在 {@link #tick(long)}。
     */
    public void runTick(long runAt) {
        try {
            synchronized (saveLockObj) {
                tick(runAt);
            }
        } catch (Exception e) {
            onTickError(e);
        }
    }

    /** 每个调度周期进来一次，子类写业务。 */
    protected void tick(long runAt) {
    }

    /** tick 抛错时的钩子，默认吞掉，需要打日志或告警就覆写。 */
    protected void onTickError(Exception e) {
    }
}
