package cn.com.qmth.stmms.biz.utils;

import cn.com.qmth.stmms.common.utils.DateUtils;

import java.util.*;

/**
 * 链表模式实现的任务互斥锁工具
 */
public class TaskLock {

    // 固定头节点
    private LockNode head;

    // 总量计数
    private int count;

    public TaskLock() {
        head = new LockNode(0, 0, 0);
        count = 0;
    }

    public synchronized boolean add(Object id, int number, Object owner) {
        if (head.next == null) {
            head.append(id, number, owner);
            count++;
            return true;
        } else {
            LockNode node = head.next;
            while (true) {
                // id+number，只能被一个owner获取
                if (node.isId(id) && node.isNumber(number)) {
                    return false;
                }
                // id只能被一个owner领取一个number
                else if (node.isId(id) && node.isOwner(owner)) {
                    return false;
                }
                // 跳到下一个node
                else if (node.hasNext()) {
                    node = node.next;
                }
                // 可以领取
                else {
                    node.append(id, number, owner);
                    count++;
                    return true;
                }
            }
        }
    }

    public synchronized boolean exist(Object id, int number, Object owner) {
        LockNode node = head.next;
        while (node != null) {
            if (node.isId(id) && node.isNumber(number)) {
                return node.isOwner(owner);
            } else {
                node = node.next;
            }
        }
        return false;
    }

    public synchronized boolean remove(Object id, int number, Object owner) {
        LockNode node = head.next;
        while (node != null) {
            if (node.isId(id) && node.isNumber(number)) {
                if (node.isOwner(owner)) {
                    node.remove();
                    count--;
                    return true;
                } else {
                    return false;
                }
            } else {
                node = node.next;
            }
        }
        return false;
    }

    public synchronized boolean remove(Object id, int number) {
        LockNode node = head.next;
        while (node != null) {
            if (node.isId(id) && node.isNumber(number)) {
                node.remove();
                count--;
                return true;
            } else {
                node = node.next;
            }
        }
        return false;
    }

    public synchronized void clear() {
        head.next = null;
        count = 0;
    }

    public synchronized void clear(Object owner) {
        LockNode node = head.next;
        while (node != null) {
            if (node.isOwner(owner)) {
                node.remove();
                count--;
            }
            node = node.next;
        }
    }

    public synchronized void expire(long expireTime) {
        long current = System.currentTimeMillis();
        LockNode node = head.next;
        while (node != null) {
            if ((current - node.time) > expireTime) {
                node.remove();
                count--;
            }
            node = node.next;
        }
    }

    public synchronized boolean refresh(Object id, int number, Object owner) {
        LockNode node = head.next;
        while (node != null) {
            if (node.isId(id) && node.isNumber(number) && node.isOwner(owner)) {
                node.time = System.currentTimeMillis();
                return true;
            }
            node = node.next;
        }
        return false;
    }

    public synchronized void refresh(Object owner) {
        LockNode node = head.next;
        while (node != null) {
            if (node.isOwner(owner)) {
                node.time = System.currentTimeMillis();
            }
            node = node.next;
        }
    }

    public int count() {
        return count;
    }

    public int count(Object owner) {
        int count = 0;
        LockNode node = head.next;
        while (node != null) {
            if (node.isOwner(owner)) {
                count++;
            }
            node = node.next;
        }
        return count;
    }

    public List<Map<String, Object>> list() {
        List<Map<String, Object>> list = new LinkedList<>();
        LockNode node = head.next;
        while (node != null) {
            Map<String, Object> map = new HashMap<>();
            map.put("taskId", node.getId());
            map.put("number", node.getNumber());
            map.put("markerId", node.getOwner());
            map.put("time", DateUtils.formatDateTime(new Date(node.getTime())));
            list.add(map);
            node = node.next;
        }
        return list;
    }

    private static class LockNode {

        private LockNode previous;

        private LockNode next;

        private Object id;

        private int number;

        private Object owner;

        private long time;

        LockNode(Object id, int number, Object owner) {
            this.id = id;
            this.number = number;
            this.owner = owner;
            this.time = System.currentTimeMillis();
        }

        private boolean hasNext() {
            return this.next != null;
        }

        private boolean isId(Object id) {
            return this.id.equals(id);
        }

        private boolean isNumber(int number) {
            return this.number == number;
        }

        private boolean isOwner(Object owner) {
            return this.owner.equals(owner);
        }

        private void remove() {
            this.previous.next = this.next;
            if (this.next != null) {
                this.next.previous = this.previous;
            }
        }

        private void append(Object id, int number, Object owner) {
            LockNode node = new LockNode(id, number, owner);
            this.next = node;
            node.previous = this;
        }

        public Object getId() {
            return id;
        }

        public int getNumber() {
            return number;
        }

        public Object getOwner() {
            return owner;
        }

        public long getTime() {
            return time;
        }

    }

}
