package com.mvc.service.tcpsocket;

import com.mvc.model.TAireadModel;
import com.mvc.service.aiiden.TaskReviewService;
import com.mvc.task.SocketProperties;
import com.mvc.utils.NumberUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class TcpClient {
    @Resource
    private SocketProperties properties;

    @Resource
    private TaskReviewService taskReviewService;

    private static Map<SocketAddress, TAireadModel> myMap = new HashMap<>();

    private static Logger log = LoggerFactory.getLogger(TcpClient.class);

    /** 弃用此方式,不太完善,同时为了解决连接上马上发数,水利所服务器收不到的问题 (改用下面Netty连接方式,并且独立线程来处理任务)*/
    public String SendtoServer(byte[] bytes, String taskReviewId) {
        Socket socket = null;
        OutputStream os = null;
        String msg = "";
        try {
            //1.创建Socket对象，指明服务器端的ip和端口号
            InetAddress inet = InetAddress.getByName(properties.getSlsip());
            //socket = new Socket(inet,properties.getSlsport());
            socket = new Socket();
            SocketAddress endpoint = new InetSocketAddress(inet, properties.getSlsport());
            socket.connect(endpoint, 5000);          //设置连接超时时间

            //2.获取一个输出流，用于输出数据
            os = socket.getOutputStream();
            socket.setSoTimeout(5000);                      //设置读写超时时间5000毫秒
            InputStream inputStream = socket.getInputStream();
            //3.写出数据的操作
            os.write(bytes);
            os.flush();

            /* 尝试新写法 开始 */
            byte[] buffer = new byte[100];
            int bytesRead = inputStream.read(buffer);
            byte[] temp = new byte[bytesRead];
            System.arraycopy(buffer, 0, temp, 0, bytesRead);
            msg = NumberUtil.bytesToHexStr(temp);
            System.out.println("收到水利所回执报文: " + msg);

            /*String receivedMessage = new String(buffer, 0, bytesRead,StandardCharsets.UTF_8);
            msg = new String(buffer, 0, bytesRead,StandardCharsets.UTF_8);
            System.out.println("收到水利所回执报文: " + receivedMessage);*/
            /* 尝试新写法 结束 */

            //socket.shutdownOutput();
            //等回执...

            /*InputStream is=socket.getInputStream();
            BufferedReader br=new BufferedReader(new InputStreamReader(is));
            String info=null;

            while ((info=br.readLine())!=null){
                msg= NumberUtil.bytesToHexStr(info.getBytes(StandardCharsets.UTF_8));
                log.info("收到水利所回执报文:"+msg);
                break;
            }*/
        } catch (IOException e) {
            log.info("[" + properties.getSlsip() + "]等待水利所回执超时-taskReviewId:" + taskReviewId);
            e.printStackTrace();
        } finally {
            //4.资源的关闭
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    log.info("向水利所推送通道关闭:" + socket.getLocalPort());
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return msg;
        }
    }

    /**
     * 发送服务到水利所服务(Netty方式)
     * @throws InterruptedException
     */
    public void SendMessageToSlsServer(String msg, TAireadModel aireadModel) throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            /*pipeline.addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2)); // 解码器
                            pipeline.addLast(new LengthFieldPrepender(2)); // 编码器
                            pipeline.addLast(new ByteArrayDecoder(), new ByteArrayEncoder()); // 字节数组的编码和解码*/
                            pipeline.addLast(new ReadTimeoutHandler(3, TimeUnit.SECONDS)); // 5秒的读取超时
                            pipeline.addLast(new ClientHandler());
                        }
                    });
            Channel channel = bootstrap.connect(properties.getSlsip(), properties.getSlsport()).sync().channel();
            // 向服务器发送数据(加了一些延迟)
            Thread.sleep(10);//**发现水利所平台经常出现没有回执的问题,怀疑与连接后马上发数有关系,加了一个延迟,测试是有效果的 2023-10-15 guozhao
            //log.info("TcpClient:向水利所发送数据[" + properties.getSlsip() + "]:" + msg);
            byte[] bytes = NumberUtil.hexStringToBytes(msg);
            channel.writeAndFlush(Unpooled.wrappedBuffer(bytes));
            myMap.put(channel.remoteAddress(), aireadModel);
            channel.closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }

    private class ClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
            // 处理来自服务器的字节数组消息
            byte[] receivedBytes = new byte[msg.readableBytes()];
            msg.readBytes(receivedBytes);
            String message = NumberUtil.bytesToHexStr(receivedBytes);
            log.info("TcpClient:收到回执:" + message);

            updateTaskReviewModel(ctx.channel().remoteAddress(), message);
        }

        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
            if (evt instanceof ReadTimeoutException) {
                //读取超时，关闭连接
                log.info("TcpClient:等待水利所回执超时,通道关闭:" + ctx.channel().localAddress());
                ctx.close();
            }
        }
    }

    /**
     * 更新审核记录表
     * @param socketAddress
     * @param message
     */
    private void updateTaskReviewModel(SocketAddress socketAddress, String message) {
        try {
            if (myMap.containsKey(socketAddress)) {
                String reviewId = myMap.get(socketAddress).getTaskReviewId();
                myMap.remove(socketAddress);
                taskReviewService.updateModelByTaskReviewId(reviewId,message);
                log.info("TcpClient:更新记录:" + message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
