package com.qsjnic.mcu.service;

import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

import com.qsjnic.mcu.contract.IMcuFrameListener;
import com.qsjnic.mcu.contract.IMcuKeyListener;
import com.qsjnic.mcu.contract.IMcuRadioListener;
import com.qsjnic.mcu.contract.IMcuService;
import com.qsjnic.mcu.contract.IMcuUpgradeListener;
import com.qsjnic.mcu.contract.IMcuVehicleListener;
import com.qsjnic.mcu.contract.McuErrorCode;
import com.qsjnic.mcu.contract.McuServiceManager;
import com.qsjnic.mcu.contract.McuVersion;
import com.qsjnic.mcu.contract.RadioState;
import com.qsjnic.mcu.contract.UpgradeProgress;
import com.qsjnic.mcu.contract.UpgradeRequest;
import com.qsjnic.mcu.contract.VehicleSnapshot;

import com.qsjnic.mcu.domain.Cmds;
import com.qsjnic.mcu.domain.Commands;
import com.qsjnic.mcu.domain.MsgType;

import com.qsjnic.mcu.service.feature.KeyFeature;
import com.qsjnic.mcu.service.feature.RadioFeature;
import com.qsjnic.mcu.service.feature.UpgradeFeature;
import com.qsjnic.mcu.service.feature.VehicleFeature;

public class McuBinder extends IMcuService.Stub {

    private static final String TAG = "McuBinder";

    private static final int OK              = McuErrorCode.OK;
    private static final int ERR_LINK_DOWN   = McuErrorCode.ERR_LINK_DOWN;
    private static final int ERR_INVALID_ARG = McuErrorCode.ERR_INVALID_ARG;

    private final VehicleFeature vehicle;
    private final KeyFeature key;
    private final RadioFeature radio;
    private final UpgradeFeature upgrade;
    private final RemoteCallbackList<IMcuFrameListener> frameListeners;

    public McuBinder(VehicleFeature vehicle, KeyFeature key, RadioFeature radio, UpgradeFeature upgrade,
                     RemoteCallbackList<IMcuFrameListener> frameListeners) {
        this.vehicle = vehicle;
        this.key = key;
        this.radio = radio;
        this.upgrade = upgrade;
        this.frameListeners = frameListeners;
    }

    @Override
    public int getAidlVersion() throws RemoteException {
        return McuServiceManager.AIDL_VERSION;
    }

    @Override
    public String getServiceVersion() throws RemoteException {
        return McuServiceManager.SDK_VERSION;
    }

    @Override
    public boolean isLinkAlive() throws RemoteException {
        return McuLink.get().isLinkAlive();
    }

    @Override
    public void registerVehicleListener(IMcuVehicleListener cb) throws RemoteException {
        vehicle.register(cb);
    }

    @Override
    public void unregisterVehicleListener(IMcuVehicleListener cb) throws RemoteException {
        vehicle.unregister(cb);
    }

    @Override
    public VehicleSnapshot getLatestVehicleSnapshot() throws RemoteException {
        return vehicle.snapshot();
    }

    @Override
    public McuVersion getMcuVersion() throws RemoteException {
        return vehicle.mcuVersion();
    }

    @Override
    public int requestInitInfo() throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.REQ_INIT, Commands.Public.reqInit(), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int setBacklight(int duty1Percent, int duty2Percent) throws RemoteException {
        try {
            byte[] payload = Commands.Public.tftDuty(duty1Percent, duty2Percent);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.TFT_DUTY, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setMute(boolean on) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.REQ_MUTE, Commands.Public.reqMute(on), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int setAmpPower(boolean on) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.AMP_PWR, Commands.Public.ampPower(on), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    // ---- Key ----

    @Override
    public void registerKeyListener(IMcuKeyListener cb) throws RemoteException {
        key.register(cb);
    }

    @Override
    public void unregisterKeyListener(IMcuKeyListener cb) throws RemoteException {
        key.unregister(cb);
    }

    @Override
    public int sendKeyEvent(int keyValue, int keyStatus) throws RemoteException {
        try {
            byte[] payload = Commands.Key.key(keyValue, keyStatus);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.KEY, Cmds.Key.A2M_KEY, payload, 1500);
            Log.i(TAG, String.format("sendKeyEvent key=0x%02X status=0x%02X seq=%d", keyValue, keyStatus, seq));
            return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            Log.w(TAG, "sendKeyEvent invalid arg: " + e.getMessage());
            return ERR_INVALID_ARG;
        }
    }

    // ---- Radio ----

    @Override
    public void registerRadioListener(IMcuRadioListener cb) throws RemoteException {
        radio.register(cb);
    }

    @Override
    public void unregisterRadioListener(IMcuRadioListener cb) throws RemoteException {
        radio.unregister(cb);
    }

    @Override
    public RadioState getLatestRadioState() throws RemoteException {
        return radio.snapshot();
    }

    @Override
    public int setBand(int band) throws RemoteException {
        return setBandWithFreq(band, 0);
    }

    @Override
    public int setBandWithFreq(int band, int freq) throws RemoteException {
        try {
            byte[] payload = Commands.Radio.setBand(band, freq);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.RADIO, Cmds.Radio.SET_BAND, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setFrequency(int freq) throws RemoteException {
        try {
            byte[] payload = Commands.Radio.setFrequency(freq);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.RADIO, Cmds.Radio.SET_FREQ, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setFreqRange(int area, int amMin, int amMax,
                            int fmMin, int fmMax, int amStep, int fmStep) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        byte[] payload = Commands.Radio.setFreqRange(area, amMin, amMax, fmMin, fmMax, amStep, fmStep);
        int seq = McuLink.get().sendAndWaitAck(MsgType.RADIO, Cmds.Radio.FREQ_RANGE, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int setSeek(int mode, int pty) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        byte[] payload = Commands.Radio.setSeek(mode, pty);
        int seq = McuLink.get().sendAndWaitAck(MsgType.RADIO, Cmds.Radio.SEEK, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int setSwitch(int subType, int value) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        byte[] payload = Commands.Radio.setSwitch(subType, value);
        int seq = McuLink.get().sendAndWaitAck(MsgType.RADIO, Cmds.Radio.SWITCH, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int setAntPower(boolean on) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.RADIO, Cmds.Radio.ANT_PWR, Commands.Radio.antPower(on), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int setRegMute(boolean mute) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.RADIO, Cmds.Radio.REG_MUTE, Commands.Radio.regMute(mute), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    // ---- Upgrade ----

    @Override
    public void registerUpgradeListener(IMcuUpgradeListener cb) throws RemoteException {
        upgrade.register(cb);
        // Legacy progress path: UpgradeManager owns the McuLink.setUpgradeListener
        // wiring and is where native progress lands. Register on both lists so
        // clients see progress regardless of which broadcaster fires.
        UpgradeManager.getInstance().register(cb);
    }

    @Override
    public void unregisterUpgradeListener(IMcuUpgradeListener cb) throws RemoteException {
        upgrade.unregister(cb);
        UpgradeManager.getInstance().unregister(cb);
    }

    @Override
    public UpgradeProgress getUpgradeProgress() throws RemoteException {
        return upgrade.progress();
    }

    @Override
    public int startUpgrade(UpgradeRequest req) throws RemoteException {
        return UpgradeManager.getInstance().startUpgrade(req);
    }

    @Override
    public int stopUpgrade() throws RemoteException {
        return UpgradeManager.getInstance().stopUpgrade();
    }

    // ---- Update A2M — Boot Update ----

    @Override
    public int startBootUpgrade(UpgradeRequest req) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        return UpgradeManager.getInstance().startBootUpgrade(req);
    }

    @Override
    public int sendBootUpgradeData(byte[] chunk, int index) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        return UpgradeManager.getInstance().sendBootUpgradeData(chunk, index);
    }

    @Override
    public int sendBootUpgradeChecksum(int checksum) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        return UpgradeManager.getInstance().sendBootUpgradeChecksum(checksum);
    }

    @Override
    public int sendBootUpgradeChecksumMd5(byte[] md5) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        return UpgradeManager.getInstance().sendBootUpgradeChecksumMd5(md5);
    }

    // ---- Public A2M ----

    @Override
    public int setTimeInfo(int year, int month, int day, int hour, int minute, int second) throws RemoteException {
        try {
            byte[] payload = Commands.Public.timeInfo(year, month, day, hour, minute, second);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.TIME_INFO, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int requestSleep() throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.REQ_SLEEP, Commands.Public.reqSleep(), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int requestTypeInfo(int type) throws RemoteException {
        try {
            byte[] payload = Commands.Public.reqTypeInfo(type);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.REQ_TYPE, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int requestArmReset() throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.REQ_ARM_RESET, Commands.Public.reqArmReset(), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int setTftOnOff(boolean on) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.TFT_ON_OFF, Commands.Public.tftOnOff(on), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int factoryRestore() throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.FACTORY_RESTORE, Commands.Public.factoryRestore(), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int setKeyBacklightDutyCycle(int dutyPercent) throws RemoteException {
        try {
            byte[] payload = Commands.Public.keyBacklight(dutyPercent);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.KEY_BACKLIGHT, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setSubMute(boolean on) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.SUB_MUTE, Commands.Public.subMute(on), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int setKeyPwrStatus(int status) throws RemoteException {
        try {
            byte[] payload = Commands.Public.keyPwrStatus(status);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.KEY_PWR_STATUS, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setReverse(boolean on) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.REVERSE, Commands.Public.reverse(on), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int setSpeedRatio(int ratio) throws RemoteException {
        try {
            byte[] payload = Commands.Public.speedRatio(ratio);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.SPEED_RATIO, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int requestArmForceUpgrade() throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.ARM_FORCE_UPGRADE, Commands.Public.reqArmForceUpgrade(), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int requestTripClear() throws RemoteException {
        return requestTripClearType(1);
    }

    @Override
    public int requestTripClearType(int type) throws RemoteException {
        try {
            byte[] payload = Commands.Public.reqTripClear(type);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.TRIP_CLEAR, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setSwitchStatus(int switchBitmap) throws RemoteException {
        try {
            byte[] payload = Commands.Public.switchStatus(switchBitmap);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.SWITCH_STATUS, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setBtErrorCode(int code) throws RemoteException {
        try {
            byte[] payload = Commands.Public.btErrorCode(code);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.BT_ERROR_CODE, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setGpsInfo(int latE6, int lonE6, int speedKmh, int heading) throws RemoteException {
        try {
            byte[] payload = Commands.Public.gpsInfo(latE6, lonE6, speedKmh, heading);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.GPS_INFO, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setGpsInfoFull(int moduleStatus, int lonPpm, int lonStatus, int latPpm, int latStatus) throws RemoteException {
        try {
            byte[] payload = Commands.Public.gpsInfoFull(moduleStatus, lonPpm, lonStatus, latPpm, latStatus);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.GPS_INFO, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setTotalMiles(int milesTenthKm) throws RemoteException {
        try {
            byte[] payload = Commands.Public.totalMiles(milesTenthKm);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.TOTAL_MILES, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setWorkMode(int mode) throws RemoteException {
        try {
            byte[] payload = Commands.Public.workMode(mode);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.WORK_MODE, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setFuelCal(byte[] data) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.FUEL_CAL, Commands.Public.fuelCal(data), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int setVehicleCfg(byte[] data) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.VEHICLE_CFG, Commands.Public.vehicleCfg(data), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int requestVehicleCfg() throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.REQ_VEHICLE_CFG, Commands.Public.reqVehicleCfg(), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int setArmSwVersion(String version) throws RemoteException {
        try {
            byte[] payload = Commands.Public.armSwVersion(version);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.ARM_SW_VERSION, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setArmHwVersion(String version) throws RemoteException {
        try {
            byte[] payload = Commands.Public.armHwVersion(version);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.ARM_HW_VERSION, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int setAutoClearMeterType(int type) throws RemoteException {
        try {
            byte[] payload = Commands.Public.autoClearMeterType(type);
            if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
            int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.AUTO_CLEAR_METER, payload, 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
        } catch (IllegalArgumentException e) {
            return ERR_INVALID_ARG;
        }
    }

    @Override
    public int responseFactoryCmd(byte[] data) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.FACTORY_CMD_RESP, Commands.Public.responseFactoryCmd(data), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    @Override
    public int responseTestCmd(byte[] data) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        int seq = McuLink.get().sendAndWaitAck(MsgType.PUBLIC, Cmds.Public.TEST_CMD_RESP, Commands.Public.responseTestCmd(data), 1500); return seq >= 0 ? OK : McuErrorCode.ERR_TIMEOUT;
    }

    // ===== AIDL v2 =====

    @Override
    public int sendCommand(int msgType, int cmd, byte[] body, int timeoutMs) throws RemoteException {
        if (!McuLink.get().isLinkAlive()) return ERR_LINK_DOWN;
        if (body != null && body.length > 2040) return ERR_INVALID_ARG;
        int seq = McuLink.get().sendAndWaitAck(msgType, cmd, body, timeoutMs);
        return seq >= 0 ? OK : (seq == -1 ? McuErrorCode.ERR_TIMEOUT : ERR_LINK_DOWN);
    }

    @Override
    public void postCommand(int msgType, int cmd, byte[] body) throws RemoteException {
        if (McuLink.get().isLinkAlive()) {
            McuLink.get().sendBusinessFrame(msgType, cmd, body);
        }
    }

    @Override
    public void registerFrameListener(IMcuFrameListener cb) throws RemoteException {
        frameListeners.register(cb);
    }

    @Override
    public void unregisterFrameListener(IMcuFrameListener cb) throws RemoteException {
        frameListeners.unregister(cb);
    }
}
