package com.neusoft.smart.pos.printer.service;

import com.neusoft.smart.pos.dto.printer.redis.PrinterTaskBean;
import com.neusoft.smart.pos.framework.exception.BusinessCommonException;
import com.neusoft.smart.pos.framework.utils.BeanUtil;
import com.neusoft.smart.pos.printer.constants.PrintConstants;
import com.neusoft.smart.pos.printer.constants.PrinterErrorCode;
import com.neusoft.smart.pos.printer.persistent.domain.PrinterTaskInfo;
import com.neusoft.smart.pos.printer.persistent.domain.PrinterTaskLog;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

@Slf4j
public class PrintQueueHandleServiceBak {
    
    
    @Autowired
    private RedisTemplate redisTemplate;
    
    @Autowired
    private PrinterTaskService printerTaskService;
    
    /**
     * 批量任务进队列
     *
     * @param printerTaskBeans
     */
    public void pushTasks(List<PrinterTaskBean> printerTaskBeans) {
        Map<Integer, List<PrinterTaskBean>> map = new HashMap<>();
        for (PrinterTaskBean printerTaskBean : printerTaskBeans) {
            if (map.containsKey(printerTaskBean.getPrinterId())) {
                map.get(printerTaskBean.getPrinterId()).add(printerTaskBean);
            } else {
                List<PrinterTaskBean> temp = new ArrayList<>();
                temp.add(printerTaskBean);
                map.put(printerTaskBean.getPrinterId(), temp);
            }
        }
        map.forEach((key, value) -> {
            RedisLock lock = new RedisLock(redisTemplate, "printer_" + key + "_tasks", 10000, 20000);
            try {
                if (lock.lock()) {
                    PrinterTaskInfo printerTaskInfo = new PrinterTaskInfo();
                    for (PrinterTaskBean printerTaskBean : value) {
                        printerTaskBean.setTaskStatus(PrintConstants.UNPRINTED.getCode());
                        printerTaskBean.setPrinterId(printerTaskBean.getPrinterId());
                        BeanUtils.copyProperties(printerTaskBean, printerTaskInfo);
                        printerTaskService.createInfo(printerTaskInfo);
                        this.createLog(false, printerTaskInfo.getId(), PrintConstants.UNPRINTED.getCode());
                        printerTaskBean.setId(printerTaskInfo.getId());
                    }
                    BoundValueOperations<String, LinkedList<PrinterTaskBean>> boundValueOperations = redisTemplate.boundValueOps("printer_" + key + "_tasks");
                    val list = boundValueOperations.get();
                    list.addAll(value);
                    boundValueOperations.set(list);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
    }
    
    /**
     * 打印任务进队列
     *
     * @param isHighestPriority 是否优先
     * @param printerTaskBean   打印任务对象
     */
    public void pushTask(Boolean isHighestPriority, PrinterTaskBean printerTaskBean) {
        if (!redisTemplate.hasKey("printer_" + printerTaskBean.getPrinterId() + "_tasks")) {
            throw new BusinessCommonException(PrinterErrorCode.INVALIDATE_PRINTER_ERROR);
        }
        RedisLock lock = new RedisLock(redisTemplate, "printer_" + printerTaskBean.getPrinterId() + "_tasks", 10000, 20000);
        try {
            if (lock.lock()) {
                PrinterTaskInfo printerTaskInfo = new PrinterTaskInfo();
                printerTaskBean.setTaskStatus(PrintConstants.UNPRINTED.getCode());
                printerTaskBean.setPrinterId(printerTaskBean.getPrinterId());
                BeanUtils.copyProperties(printerTaskBean, printerTaskInfo);
                printerTaskService.createInfo(printerTaskInfo);
                this.createLog(false, printerTaskInfo.getId(), PrintConstants.UNPRINTED.getCode());
                printerTaskBean.setId(printerTaskInfo.getId());
                BoundValueOperations<String, LinkedList<PrinterTaskBean>> boundValueOperations = redisTemplate.boundValueOps("printer_" + printerTaskBean.getPrinterId() + "_tasks");
                val list = boundValueOperations.get();
                if (isHighestPriority) {
                    list.addFirst(printerTaskBean);
                } else {
                    list.addLast(printerTaskBean);
                }
                boundValueOperations.set(list);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    /**
     * 打印任务完成, 清除出队列
     *
     * @param printerId
     * @param taskId    打印任务id
     */
    public void removeTask(Integer printerId, Integer taskId) {
        this.createLog(true, taskId, PrintConstants.PRINTED.getCode());
        RedisLock lock = new RedisLock(redisTemplate, "printer_" + printerId + "_tasks", 10000, 20000);
        try {
            if (lock.lock()) {
                BoundValueOperations<String, LinkedList<PrinterTaskBean>> boundValueOperations = redisTemplate.boundValueOps("printer_" + printerId + "_tasks");
                LinkedList<PrinterTaskBean> tasks = boundValueOperations.get();
                PrinterTaskBean printerTaskBean = tasks.parallelStream().filter(item -> item.getId().equals(taskId)).findFirst().orElse(null);
                if (null != printerTaskBean) {
                    tasks.remove(printerTaskBean);
                }
                boundValueOperations.set(tasks);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    /**
     * 从打印队列中取popCount条数据
     *
     * @param printerId
     * @param peekCount
     * @return
     */
    public LinkedList<PrinterTaskBean> peekTasks(Integer printerId, Integer peekCount) {
        LinkedList<PrinterTaskBean> result = new LinkedList<>();
        RedisLock lock = new RedisLock(redisTemplate, "printer_" + printerId + "_tasks", 10000, 20000);
        try {
            if (lock.lock()) {
                BoundValueOperations<String, LinkedList<PrinterTaskBean>> boundValueOperations = redisTemplate.boundValueOps("printer_" + printerId + "_tasks");
                LinkedList<PrinterTaskBean> tasks = boundValueOperations.get();
                for (int i = 0; i < peekCount && i < tasks.size(); i++) {
                    PrinterTaskBean printerTaskBean = tasks.get(i);
                    //克隆一个对象,如果是未打印过的保证status状态没有变化,标记该数据没有被申请过打印
                    result.push(BeanUtil.parse(printerTaskBean, PrinterTaskBean.class));
                    //放到结果集合之后更改打印任务状态,表示已经申请打印了
                    printerTaskBean.setTaskStatus(PrintConstants.PRINTING.getCode());
                    this.createLog(true, printerTaskBean.getId(), PrintConstants.PRINTING.getCode());
                }
                boundValueOperations.set(tasks);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //为了让分布式锁的算法更稳键些，持有锁的客户端在解锁之前应该再检查一次自己的锁是否已经超时，再去做DEL操作，因为可能客户端因为某个耗时的操作而挂起，
            //操作完的时候锁因为超时已经被别人获得，这时就不必解锁了。 ————这里没有做
            lock.unlock();
        }
        return result;
    }
    
    
    private void createLog(Boolean isUpdateStatus, Integer taskId, String taskStatus) {
        if (isUpdateStatus) {
            printerTaskService.updateTaskStatus(taskId, taskStatus);
        }
        PrinterTaskLog printerTaskLog = new PrinterTaskLog();
        printerTaskLog.setTaskId(taskId);
        printerTaskLog.setTaskStatus(taskStatus);
        printerTaskService.createLog(printerTaskLog);
    }
    
}
