package com.lion.lionwebsite.Service;

import com.lion.lionwebsite.Dao.normal.GalleryMapper;
import com.lion.lionwebsite.Domain.Gallery;
import com.lion.lionwebsite.Domain.GalleryTask;
import com.lion.lionwebsite.Message.*;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.Promise;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.net.*;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Service
@Data
@Slf4j
public class RemoteService {

    ChannelFuture channelFuture;

    Channel channel;

    String ip = "5.255.110.45";

    short port = 26321;

    GalleryMapper galleryMapper;

    PushService pushService;

    HashMap<Integer, Promise<AbstractMessage>> promiseHashMap;

    EventLoop eventLoopGroup;

    ExecutorService downloadThread;

    Thread monitor;

    AtomicInteger atomicInteger;

    public RemoteService(GalleryMapper galleryMapper, PushService pushService){
        this.galleryMapper = galleryMapper;
        this.pushService = pushService;
        atomicInteger = new AtomicInteger(0);
        eventLoopGroup = new DefaultEventLoop();
        downloadThread = Executors.newCachedThreadPool();
        promiseHashMap = new HashMap<>();

        if(!initChannel()){     //如果远程服务器连接失败，则开启本地监听
            monitor = new Thread(this::monitorFunc);
            monitor.start();
        }
    }

    public boolean initChannel(){
        try {
            channelFuture = new Bootstrap()
                    .channel(NioSocketChannel.class)
                    .group(new NioEventLoopGroup())
                    .handler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel channel){
                            channel.pipeline().addLast(new LengthFieldBasedFrameDecoder(100000000, 1, 4));
                            channel.pipeline().addLast(new MessageCodec());
                            channel.pipeline().addLast(new LoggingHandler());
                            channel.pipeline().addLast(new MyChannelInboundHandlerAdapter());
                        }
                    }).connect(new InetSocketAddress(ip, port)).sync();
            log.info("connect success");
            if(pushService != null)
                pushService.storageNodeOnline();

            channel = channelFuture.channel();
            channel.writeAndFlush(new IdentityMessage("lionwebsite"));
            return true;
        }catch (Exception e){
            e.printStackTrace();
            log.info("connect node failed, wait for node back online");
            return false;
        }
    }

    public boolean isDead(){
        return channelFuture.channel() == null || !channelFuture.channel().isActive();
    }

    public byte addGalleryToQueue(Gallery gallery){
        GalleryTask galleryTask = new GalleryTask();
        galleryTask.setGid(gallery.getGid());

        DownloadPostMessage dpm = new DownloadPostMessage();
        dpm.messageId = atomicInteger.getAndIncrement();
        dpm.setGalleryTask(galleryTask);
        channel.writeAndFlush(dpm);

        DefaultPromise<AbstractMessage> promise = new DefaultPromise<>(eventLoopGroup);
        promiseHashMap.put(dpm.messageId, promise);
        try {
            boolean result = promise.await(10, TimeUnit.SECONDS);
            if(result){
                ResponseMessage rsm = (ResponseMessage)promise.getNow();
                return rsm.getResult();
            }
            else return -1;
        }catch (InterruptedException e){
            e.printStackTrace();
            return -1;
        }
    }

    public byte deleteGallery(Gallery gallery){
        DeleteGalleryMessage dgm = new DeleteGalleryMessage();
        dgm.setGalleryName(gallery.getName());
        dgm.messageId = atomicInteger.getAndIncrement();

        channel.writeAndFlush(dgm);
        DefaultPromise<AbstractMessage> promise = new DefaultPromise<>(eventLoopGroup);
        promiseHashMap.put(dgm.messageId, promise);
        try{
            boolean result = promise.await(10, TimeUnit.SECONDS);
            if(result){
                ResponseMessage rsm = (ResponseMessage) promise.getNow();
                return rsm.getResult();
            }else return -1;
        }catch (InterruptedException e){
            return -1;
        }
    }

    public void monitorFunc(){
        System.out.println("监听端口： " + (port + 1) + " 等待节点上线");
        try(ServerSocket socket = new ServerSocket(port + 1)) {
            Socket client;
            while(true){
                client = socket.accept();
                client.close();
                if(client.getInetAddress().getHostAddress().equals("5.255.110.45")){
                    System.out.println("尝试连接");
                    initChannel();
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    class MyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter{
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg)  {
            //如果不是响应信息或者响应信息为失败，则打印
            if(!(msg instanceof ResponseMessage rm) || rm.getResult() != 0)
                if(! (msg instanceof MaintainMessage))
                    System.out.println(msg);

            //下载状态
            if(msg instanceof DownloadStatusMessage dsm){
                GalleryTask[] galleryTasks = dsm.getGalleryTasks();
                for (GalleryTask galleryTask : galleryTasks) {
                    Gallery gallery = galleryMapper.selectGalleryByGid(galleryTask.getGid());
                    gallery.setProceeding(galleryTask.getProceeding());

                    if(!gallery.getName().equals(galleryTask.getName()) && galleryTask.getName() != null)
                        gallery.setName(galleryTask.getName());

                    if(galleryTask.getStatus() == GalleryTask.COMPRESS_COMPLETE) {
                        gallery.setStatus("下载完成");
                        pushService.downloadComplete(gallery);
                    }
                    else if(galleryTask.getStatus() == GalleryTask.COMPRESSING)
                        gallery.setStatus("压缩中");
                    else if(galleryTask.getProceeding() != 0)
                        gallery.setStatus("下载中");

                    log.info(gallery.getName() + "下载进度:" + gallery.getProceeding() + "/" + gallery.getPages());
                    galleryMapper.updateGallery(gallery);
                }
            }
            else if(msg instanceof ResponseMessage rsm)
                promiseHashMap.get(rsm.messageId).setSuccess(rsm);
        }

        @Override
        public void channelUnregistered(ChannelHandlerContext ctx) {
            System.out.println(ctx.channel());
            System.out.println(channel);
            if(ctx.channel() != null && ctx.channel().remoteAddress().toString().equals(channel.remoteAddress().toString())){
                System.out.println("activate monitor thread, waiting for node back online");
                pushService.storageNodeOffline();
                monitor = new Thread(RemoteService.this::monitorFunc);
                monitor.start();
            }
        }
    }
}
