package com.genersoft.iot.vmp.jt1078.proc.request;

import com.genersoft.iot.vmp.jt1078.annotation.MsgId;
import com.genersoft.iot.vmp.jt1078.bean.JTAlarmSign;
import com.genersoft.iot.vmp.jt1078.bean.JTDeviceConfig;
import com.genersoft.iot.vmp.jt1078.bean.common.ConfigAttribute;
import com.genersoft.iot.vmp.jt1078.bean.config.*;
import com.genersoft.iot.vmp.jt1078.proc.Header;
import com.genersoft.iot.vmp.jt1078.proc.response.J8001;
import com.genersoft.iot.vmp.jt1078.proc.response.Rs;
import com.genersoft.iot.vmp.jt1078.service.Ijt1078Service;
import com.genersoft.iot.vmp.jt1078.session.Session;
import com.genersoft.iot.vmp.jt1078.session.SessionManager;
import io.netty.buffer.ByteBuf;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEvent;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

/**
 * 查询终端参数应答
 *
 */
@MsgId(id = "0104")
public class J0104 extends Re {

    Integer respNo;
    Integer paramLength;

    @Override
    protected Rs decode0(ByteBuf buf, Header header, Session session) {
        respNo = buf.readUnsignedShort();
        paramLength = (int) buf.readUnsignedByte();
        if (paramLength <= 0) {
            return null;
        }
        JTDeviceConfig deviceConfig = new JTDeviceConfig();
        Field[] fields = deviceConfig.getClass().getDeclaredFields();
        Map<Long, Field> allFieldMap = new HashMap<>();
        Map<Long, ConfigAttribute> allConfigAttributeMap = new HashMap<>();
        for (Field field : fields) {
            ConfigAttribute configAttribute = field.getAnnotation(ConfigAttribute.class);
            if (configAttribute != null) {
                allFieldMap.put(configAttribute.id(), field);
                allConfigAttributeMap.put(configAttribute.id(), configAttribute);
            }
        }
        for (int i = 0; i < paramLength; i++) {
            long id = buf.readUnsignedInt();
            if (!allFieldMap.containsKey(id)) {
                continue;
            }
            short length = buf.readUnsignedByte();
            Field field = allFieldMap.get(id);
            try {

                switch (allConfigAttributeMap.get(id).type()) {
                    case "Long":
                        Method methodForLong = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), Long.class);
                        methodForLong.invoke(deviceConfig, buf.readUnsignedInt());
                        continue;
                    case "String":
                        Method methodForString = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), String.class);
                        String val = buf.readCharSequence(length, Charset.forName("GBK")).toString().trim();
                        methodForString.invoke(deviceConfig, val);
                        continue;
                    case "Integer":
                        Method methodForInteger = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), Integer.class);
                        methodForInteger.invoke(deviceConfig, buf.readUnsignedShort());
                        continue;
                    case "Short":
                        Method methodForShort = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), Short.class);
                        methodForShort.invoke(deviceConfig, buf.readUnsignedByte());
                        continue;
                    case "IllegalDrivingPeriods":
                        JTIllegalDrivingPeriods illegalDrivingPeriods = new JTIllegalDrivingPeriods();
                        int startHour = buf.readUnsignedByte();
                        int startMinute = buf.readUnsignedByte();
                        int stopHour = buf.readUnsignedByte();
                        int stopMinute = buf.readUnsignedByte();
                        illegalDrivingPeriods.setStartTime(startHour + ":" + startMinute);
                        illegalDrivingPeriods.setEndTime(stopHour + ":" + stopMinute);
                        Method methodForIllegalDrivingPeriods = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), JTIllegalDrivingPeriods.class);
                        methodForIllegalDrivingPeriods.invoke(deviceConfig, illegalDrivingPeriods);
                        continue;
                    case "CollisionAlarmParams":
                        JTCollisionAlarmParams collisionAlarmParams = new JTCollisionAlarmParams();
                        collisionAlarmParams.setCollisionAlarmTime(buf.readUnsignedByte());
                        collisionAlarmParams.setCollisionAcceleration(buf.readUnsignedByte());
                        Method methodForCollisionAlarmParams = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), JTCollisionAlarmParams.class);
                        methodForCollisionAlarmParams.invoke(deviceConfig, collisionAlarmParams);
                        continue;
                    case "CameraTimer":
                        JTCameraTimer cameraTimer = new JTCameraTimer();
                        long cameraTimerContent = buf.readUnsignedInt();
                        cameraTimer.setSwitchForChannel1((cameraTimerContent & 1) == 1);
                        cameraTimer.setSwitchForChannel2((cameraTimerContent >>> 1 & 1) == 1);
                        cameraTimer.setSwitchForChannel3((cameraTimerContent >>> 2 & 1) == 1);
                        cameraTimer.setSwitchForChannel4((cameraTimerContent >>> 3 & 1) == 1);
                        cameraTimer.setSwitchForChannel5((cameraTimerContent >>> 4 & 1) == 1);
                        cameraTimer.setStorageFlagsForChannel1((cameraTimerContent >>> 7 & 1) == 1);
                        cameraTimer.setStorageFlagsForChannel2((cameraTimerContent >>> 8 & 1) == 1);
                        cameraTimer.setStorageFlagsForChannel3((cameraTimerContent >>> 9 & 1) == 1);
                        cameraTimer.setStorageFlagsForChannel4((cameraTimerContent >>> 10 & 1) == 1);
                        cameraTimer.setStorageFlagsForChannel5((cameraTimerContent >>> 11 & 1) == 1);
                        cameraTimer.setTimeUnit((cameraTimerContent >>> 15 & 1) == 1);
                        cameraTimer.setTimeInterval((int)cameraTimerContent >>> 16);
                        Method methodForCameraTimer = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), JTCameraTimer.class);
                        methodForCameraTimer.invoke(deviceConfig, cameraTimer);
                        continue;
                    case "GnssPositioningMode":
                        JTGnssPositioningMode gnssPositioningMode = new JTGnssPositioningMode();
                        short gnssPositioningModeContent = buf.readUnsignedByte();
                        gnssPositioningMode.setGps((gnssPositioningModeContent& 1) == 1);
                        gnssPositioningMode.setBeidou((gnssPositioningModeContent >>> 1 & 1) == 1);
                        gnssPositioningMode.setGlonass((gnssPositioningModeContent >>> 2 & 1) == 1);
                        gnssPositioningMode.setGaLiLeo((gnssPositioningModeContent >>> 3 & 1) == 1);
                        Method methodForGnssPositioningMode = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), JTGnssPositioningMode.class);
                        methodForGnssPositioningMode.invoke(deviceConfig, gnssPositioningMode);
                        continue;
                    case "VideoParam":
                        JTVideoParam videoParam = JTVideoParam.decode(buf);
                        Method methodForVideoParam = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), JTVideoParam.class);
                        methodForVideoParam.invoke(deviceConfig, videoParam);
                        continue;
                    case "ChannelListParam":
                        JTChannelListParam channelListParam = JTChannelListParam.decode(buf);
                        Method methodForChannelListParam = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), JTChannelListParam.class);
                        methodForChannelListParam.invoke(deviceConfig, channelListParam);
                        continue;
                    case "ChannelParam":
                        JTChannelParam channelParam = JTChannelParam.decode(buf);
                        Method methodForChannelParam = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), JTChannelParam.class);
                        methodForChannelParam.invoke(deviceConfig, channelParam);
                        continue;
                    case "AlarmRecordingParam":
                        JTAlarmRecordingParam alarmRecordingParam = JTAlarmRecordingParam.decode(buf);
                        Method methodForAlarmRecordingParam = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), JTAlarmRecordingParam.class);
                        methodForAlarmRecordingParam.invoke(deviceConfig, alarmRecordingParam);
                        continue;
                    case "VideoAlarmBit":
                        JTVideoAlarmBit videoAlarmBit = JTVideoAlarmBit.decode(buf);
                        Method methodForVideoAlarmBit = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), JTVideoAlarmBit.class);
                        methodForVideoAlarmBit.invoke(deviceConfig, videoAlarmBit);
                        continue;
                    case "AnalyzeAlarmParam":
                        JTAnalyzeAlarmParam analyzeAlarmParam = JTAnalyzeAlarmParam.decode(buf);
                        Method methodForAnalyzeAlarmParam = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), JTAnalyzeAlarmParam.class);
                        methodForAnalyzeAlarmParam.invoke(deviceConfig, analyzeAlarmParam);
                        continue;
                    case "AwakenParam":
                        JTAwakenParam awakenParamParam = JTAwakenParam.decode(buf);
                        Method methodForAwakenParam = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), JTAwakenParam.class);
                        methodForAwakenParam.invoke(deviceConfig, awakenParamParam);
                        continue;
                    case "AlarmSign":
                        JTAlarmSign alarmSign = JTAlarmSign.decode(buf);
                        Method methodForAlarmSign = deviceConfig.getClass().getDeclaredMethod("set" + StringUtils.capitalize(field.getName()), JTAlarmSign.class);
                        methodForAlarmSign.invoke(deviceConfig, alarmSign);
                        continue;
                    default:
                        System.err.println(field.getGenericType().getTypeName());
                        continue;
                }
            } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }
        SessionManager.INSTANCE.response(header.getPhoneNumber(), "0104", (long) respNo, deviceConfig);
        return null;
    }

    @Override
    protected Rs handler(Header header, Session session, Ijt1078Service service) {
        J8001 j8001 = new J8001();
        j8001.setRespNo(header.getSn());
        j8001.setRespId(header.getMsgId());
        j8001.setResult(J8001.SUCCESS);
        return j8001;
    }

    @Override
    public ApplicationEvent getEvent() {
        return null;
    }
}
