package com.zksy.audio.utils;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.zksy.audio.domain.ChannelInfo;
import com.zksy.audio.domain.NoiseInfo;
import com.zksy.audio.service.NoiseInfoService;
import com.zksy.audio.utils.CrtHelper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 消息处理器
 * 负责处理各种请求和响应
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class MessageHandler extends ChannelInboundHandlerAdapter {

	public ConcurrentMap<ChannelId, ChannelInfo> channelGroup = new ConcurrentHashMap<>();

	public ConcurrentMap<String, ChannelInfo> deviceGroup = new ConcurrentHashMap<>();

	@Value("${wlds.fileUploadBaseDir}")
	private String fileUploadBaseDir;

	private final static String head = "524946462440010057415645666D742010000000010001000020000000400000020010006461746100400100";
	private final NoiseInfoService service;
	@Autowired
	public MessageHandler(NoiseInfoService noiseInfoService) {
		this.service = noiseInfoService;
	}
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		//log.info("==收到客户端：{}|{}发送的消息:{}============:", ctx.channel().id().asLongText(), ctx.channel().remoteAddress(), msg);
		try {
			ByteBuf byteBuffer = (ByteBuf) msg;
			if (byteBuffer == null) {
				return;
			}
			int readableLength = byteBuffer.readableBytes();
			byte[] arrayWithOutHead = new byte[readableLength];//去掉包头标识的数据
			byteBuffer.getBytes(byteBuffer.readerIndex(), arrayWithOutHead);

			String hex = CrtHelper.byteToHexStr1(arrayWithOutHead);
			log.info("收到消息:{}", hex);
			double power = 0;
			String deviceNo;
			double gain = 0;

			byte[] bytes;
			String asciiString;
			String path;

			ChannelInfo info;

			ChannelInfo channelInfo = new ChannelInfo("puqi", ctx.channel());

			if (!channelGroup.containsKey(ctx.channel().id())) {
				// 加入客户端
				channelGroup.put(ctx.channel().id(), channelInfo);
			}

			info = channelGroup.get(ctx.channel().id());

			// 申报身份
			if (hex.startsWith("242424")) {
				String reativePath = "";
				String[] hexArray = Arrays.stream(hex.split("0D0A")).filter(StrUtil::isNotEmpty).toArray(String[]::new);
				log.info("hexArray.length:{}", hexArray.length);
				if (hexArray.length >= 1) {
					bytes = CrtHelper.strToToHexByte1(hexArray[0]);
					asciiString = new String(bytes, StandardCharsets.US_ASCII);
					deviceNo = asciiString.replaceAll("\\$\\$\\$", "").replaceAll("\r\n", "").trim();
					path = getPathName(deviceNo, reativePath);

					info.setDeviceNo(deviceNo);
					info.setPath(path);
					info.setFirst(true);
					info.setFilePath(path);
					info.setLength(0);

					deviceGroup.put(deviceNo, info);
					channelGroup.put(ctx.channel().id(), info);
					info = channelGroup.get(ctx.channel().id());
				}
				if (hexArray.length >= 2) {
					bytes = CrtHelper.strToToHexByte1(hexArray[1]);
					asciiString = new String(bytes, StandardCharsets.US_ASCII);
					asciiString = asciiString.replaceAll("VBAT:", "").replaceAll("V", "").replaceAll("\r\n", "").trim();
					if (Validator.isNumber(asciiString)) {
						power = Convert.toDouble(asciiString);
					}
				}
				if (hexArray.length >= 3) {
					String gainHex = hexArray[3];
					bytes = CrtHelper.strToToHexByte1(gainHex);
					asciiString = new String(bytes, StandardCharsets.US_ASCII);
					asciiString = asciiString.replaceAll("Gain:", "").replaceAll("\r\n", "");
					if (Validator.isNumber(asciiString)) {
						gain = Convert.toDouble(asciiString);
					}
				}

				channelGroup.get(ctx.channel().id()).setPower(power);
				channelGroup.get(ctx.channel().id()).setGain(gain);

				if (hexArray.length >= 4) {
					String videoHex = hexArray[3];
					info = channelGroup.get(ctx.channel().id());
					path = info.getPath();
					info.setLength(info.getLength() + (videoHex.length() / 2));
					String finalPath1 = path;
					ThreadUtil.execAsync(() -> {
						saveHexStringToFile(finalPath1, videoHex);
					});
				}

			}
			// 申报电量
			else if (hex.startsWith("56424154")) {
				if (!channelGroup.containsKey(ctx.channel().id())) {
					return;
				}
				if (StrUtil.isEmpty(channelGroup.get(ctx.channel().id()).getDeviceNo())) {
					return;
				}
				String[] hexArray = Arrays.stream(hex.split("0D0A")).filter(StrUtil::isNotEmpty).toArray(String[]::new);
				if (hexArray.length >= 1) {
					bytes = CrtHelper.strToToHexByte1(hexArray[0]);
					asciiString = new String(bytes, StandardCharsets.US_ASCII);
					asciiString = asciiString.replaceAll("VBAT:", "").replaceAll("V", "").replaceAll("\r\n", "").trim();
					if (Validator.isNumber(asciiString)) {
						power = Double.parseDouble(asciiString);
					}
				}
				if (hexArray.length >= 2) {
					String gainHex = hexArray[1];
					bytes = CrtHelper.strToToHexByte1(gainHex);
					asciiString = new String(bytes, StandardCharsets.US_ASCII);
					asciiString = asciiString.replaceAll("Gain:", "").replaceAll("\r\n", "");
					if (Validator.isNumber(asciiString)) {
						gain = Double.parseDouble(asciiString);
					}
				}
				channelGroup.get(ctx.channel().id()).setGain(gain);
				channelGroup.get(ctx.channel().id()).setPower(power);

				if (hexArray.length >= 3) {
					String videoHex = hexArray[2];
					info = channelGroup.get(ctx.channel().id());
					path = info.getPath();
					info.setLength(info.getLength() + (videoHex.length() / 2));
					String finalPath1 = path;
					ThreadUtil.execAsync(() -> {
						saveHexStringToFile(finalPath1, videoHex);
					});
				}
			}
			// 增益值
			else if (hex.startsWith("4761696E")) {
				if (!channelGroup.containsKey(ctx.channel().id())) {
					return;
				}
				if (StrUtil.isEmpty(channelGroup.get(ctx.channel().id()).getDeviceNo())) {
					return;
				}
				String[] hexArray = Arrays.stream(hex.split("0D0A")).filter(StrUtil::isNotEmpty).toArray(String[]::new);
				if (hexArray.length >= 1) {
					String gainHex = hexArray[0];
					bytes = CrtHelper.strToToHexByte1(gainHex);
					asciiString = new String(bytes, StandardCharsets.US_ASCII);
					asciiString = asciiString.replaceAll("Gain:", "").replaceAll("\r\n", "");
					if (Validator.isNumber(asciiString)) {
						gain = Double.parseDouble(asciiString);
					}
				}
				channelGroup.get(ctx.channel().id()).setGain(gain);
				if (hexArray.length >= 2) {
					String videoHex = hexArray[1];
					info = channelGroup.get(ctx.channel().id());
					path = info.getPath();
					info.setLength(info.getLength() + (videoHex.length() / 2));
					String finalPath1 = path;
					ThreadUtil.execAsync(() -> {
						saveHexStringToFile(finalPath1, videoHex);
					});
				}
			} else if (hex.startsWith("4F4B")) {
				bytes = CrtHelper.strToToHexByte1(hex);
				asciiString = new String(bytes, StandardCharsets.US_ASCII);
				log.info("==收到客户端：{}，GPS信息：{}", ctx.channel().id().asLongText(), asciiString);
				info = channelGroup.get(ctx.channel().id());
				String pattern = ",(\\d+\\.\\d+),\\d+\\.\\d+";
				Pattern regex = Pattern.compile(pattern);
				Matcher match = regex.matcher(asciiString);
				if (match.matches()) {
					// 提取经纬度，注意第一个捕获组是纬度，第二个捕获组（如果有的话）是经度
					// 但由于我们只关心第一个逗号后的经纬度，所以可以直接拆分
					String[] coords = match.group(0).replaceFirst(",", "").split(",");
					if (coords.length == 2) {
						double lat = Double.parseDouble(coords[0]);
						double lon = Double.parseDouble(coords[1]);
						log.info("经纬度：{},{}", lat, lon);
						//-----------
					}
				} else {
					log.info("==收到客户端：{}的设备调试信息：{}", ctx.channel().id().asLongText(), asciiString);

				}
			} else {
				if (!channelGroup.containsKey(ctx.channel().id())) {
					return;
				}
				if (StrUtil.isEmpty(channelGroup.get(ctx.channel().id()).getDeviceNo())) {
					return;
				}
				info = channelGroup.get(ctx.channel().id());
				path = channelGroup.get(ctx.channel().id()).getPath();
				String finalPath1 = path;
				String headHex;
				byte[] length = new byte[4];
				if (info.getFirst() && !hex.startsWith("52494646") ){
					//用于没带头文件的音频数据
					headHex = head + hex;
					long videoMaxSize = 80 * 1024 + 44;
					info.setVideoMaxSize(videoMaxSize);

				} else {
					headHex = hex;
					if (hex.startsWith("52494646")){
						//24001900  ====> 163840
						length[0] = (byte) Integer.parseInt(hex.substring(14, 16), 16);
						length[1] = (byte) Integer.parseInt(hex.substring(12, 14), 16);
						length[2] = (byte) Integer.parseInt(hex.substring(10, 12), 16);
						length[3] = (byte) Integer.parseInt(hex.substring(8, 10), 16);
						long videoMaxSize = (Long.parseLong(CrtHelper.byteToHexStr1(length), 16) - 36) / 10 + 44;
						info.setVideoMaxSize(videoMaxSize);
					}
				}
				info.setFirst(false);
				info.setLength(info.getLength() + headHex.length() / 2);
				ThreadUtil.execAsync(() -> {
					saveHexStringToFile(finalPath1, headHex);
				});
				log.info("======收到客户端总字节{}-----videoMaxSize:{}",info.getLength(),info.getVideoMaxSize());
				// 如果接收音频文件字节大于头文件标识长度；
				if (info.getLength() >= info.getVideoMaxSize()) {
					if (info.getGatheTime() == null) {
						info.setGatheTime(DateUtil.date());
					}
					// TODO: 2023/9/5 音频文件信息入库
					int rms = DataParser.calculateRMS(fileUploadBaseDir+path);
					double centerFrequency = DataParser.calcCenterFrequency(fileUploadBaseDir+path);
					NoiseInfo noise = new NoiseInfo();
					noise.setEncode(info.getDeviceNo());
					noise.setPower(info.getPower());
					noise.setGain(info.getGain());
					noise.setRms(rms);
					noise.setCenterFrequency(centerFrequency);
					noise.setFilepath(info.getFilePath());
					noise.setTs(DateUtil.format(DateUtil.offset(info.getGatheTime(), DateField.HOUR, -8),
							DatePattern.NORM_DATETIME_PATTERN));
					//log.info(JsonUtils.toJsonString(noise));
					service.save(noise);
					//设备默认上报三次音频数据, 数据接收完成回复设备Ok,停止上报
					byte[] cmdBytes = "OK\r\n".getBytes(StandardCharsets.US_ASCII);
					ctx.channel().writeAndFlush(Unpooled.copiedBuffer(cmdBytes));
				}

			}

		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}

	}

	private String getPathName(String deviceNo, String tempPath) {
		String ext = ".wav";
		tempPath = "/devices/" + deviceNo + "/" + DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMAT);
		File desc = new File(fileUploadBaseDir, tempPath);
		if (!desc.exists()) {
			if (!desc.getParentFile().exists()) {
				desc.getParentFile().mkdirs();
			}
		}
		String fileName = deviceNo + "_" + DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_FORMAT) + ext;
		return tempPath + "/" + fileName;
	}

	private void saveHexStringToFile(String filePath, String hexString) {
		if (StrUtil.isEmpty(hexString)) {
			return;
		}
		log.info("音频字节:{}",hexString);
		byte[] bytes = CrtHelper.strToToHexByte1(hexString);
		try {
			FileUtils.writeByteArrayToFile(new File(fileUploadBaseDir + filePath), bytes, true);
		} catch (IOException e) {
		}
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		super.channelActive(ctx);
		log.info("==客户端：{}，连接成功", ctx.channel().remoteAddress());
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		log.error("TCP异常：{}", cause.getMessage());
		super.exceptionCaught(ctx, cause);
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		log.info("==客户端：{}，掉线了", ctx.channel().remoteAddress());
		ChannelInfo channelInfo = channelGroup.remove(ctx.channel().id());
		if (channelInfo != null && StrUtil.isNotEmpty(channelInfo.getDeviceNo())) {
			deviceGroup.remove(channelInfo.getDeviceNo());
		}
		super.channelInactive(ctx);
	}

	@Override
	public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
		log.info("==客户端：{}，掉线了，ChannelUnregistered", ctx.channel().remoteAddress());
		super.channelUnregistered(ctx);
	}

	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
		log.info("==服务端：{}，下线了", ctx.toString());
		super.handlerRemoved(ctx);
	}
}