package com.bigstark.gunner.library;

import android.os.Message;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Gunner {

    private volatile static Gunner instance;


    static Gunner getInstance() {
        if (instance == null) {
            synchronized (Gunner.class) {
                if (instance == null) {
                    instance = new Gunner();
                }
            }
        }

        return instance;
    }


    public static void shoot(Object target) {
        Gunner instance = getInstance();
        Method[] methods = target.getClass().getDeclaredMethods();
        for (Method method : methods) {

            Bullet bullet = method.getAnnotation(Bullet.class);
            if (bullet == null) {
                continue;
            }

            method.setAccessible(true);
            int sequence = bullet.sequence();
            long delay = bullet.delay();

            instance.enqueue(method, sequence, delay);
        }

        instance.shootInternal(target);
    }


    public static void cancel(Object target) {
        getInstance().cancelInternal();
    }


    private ArrayList<BulletHandler> methodSet = new ArrayList<>();
    private GunHandler gunHandler = new GunHandler();


    void enqueue(Method method, int sequence, long delay) {
        if (sequence < 0) {
            throw new IllegalArgumentException(String.format("[%s]'s sequence must be over 0. Please check the sequence", method.getName()));
        }

        if (delay < 0) {
            throw new IllegalArgumentException(String.format("[%s]'s delay must be over 0. Please check the delay", method.getName()));
        }

        BulletHandler handler = new BulletHandler(method, sequence, delay);
        methodSet.add(handler);
    }


    void shootInternal(Object target) {
        Collections.sort(methodSet, bulletComparator);
        gunHandler.setTarget(target);

        long delay = 0;

        for (BulletHandler bulletHandler : methodSet) {
            delay += bulletHandler.getDelay();

            Message msg = gunHandler.obtainMessage(GunHandler.WHAT_METHOD);
            msg.obj = bulletHandler.getMethod();;
            gunHandler.sendMessageDelayed(msg, delay);
        }

        methodSet.clear();
    }


    void cancelInternal() {
        gunHandler.removeMessages(GunHandler.WHAT_METHOD);
    }


    class BulletHandler {

        private Method method;
        private int sequence;
        private long delay;

        public BulletHandler(Method method, int sequence, long delay) {
            this.method = method;
            this.sequence = sequence;
            this.delay = delay;
        }

        public Method getMethod() {
            return method;
        }

        public int getSequence() {
            return sequence;
        }

        public long getDelay() {
            return delay;
        }
    }

    Comparator<BulletHandler> bulletComparator = new Comparator<BulletHandler>() {
        @Override
        public int compare(BulletHandler lhs, BulletHandler rhs) {
            int lSeq = lhs.getSequence();
            int rSeq = rhs.getSequence();
            return rSeq < lSeq ? 1 : rSeq == lSeq ? 0 : -1;
        }
    };


}

@@ -178,16 +178,87 @@
 parator;
+%0Aimport java.util.HashMap;%0Aimport java.util.List;%0Aimport java.util.Map;
 %0A%0Apublic
@@ -582,32 +582,167 @@
 stance;%0A    %7D%0A%0A%0A
+    public static void shoot(Object... targets) %7B%0A        for (Object target : targets) %7B%0A            shoot(target);%0A        %7D%0A    %7D%0A%0A%0A
     public stati
@@ -1293,32 +1293,169 @@
 arget);%0A    %7D%0A%0A%0A
+    public static void cancel(Object... targets) %7B%0A        for (Object target : targets) %7B%0A            cancel(target);%0A        %7D%0A    %7D%0A%0A%0A
     public stati
@@ -1522,16 +1522,22 @@
 nternal(
+target
 );%0A    %7D
@@ -1555,114 +1555,125 @@
 ate 
-ArrayList%3CBulletHandler%3E methodSet = new ArrayList%3C%3E();%0A    private GunHandler gunHandler = new GunHandler
+Map%3CInteger, GunHandler%3E gunHandlerMap = new HashMap%3C%3E();%0A    private List%3CBulletHandler%3E methodSet = new ArrayList%3C%3E
 ();%0A
@@ -2297,16 +2297,67 @@
 arator);
+%0A%0A        GunHandler gunHandler = new GunHandler();
 %0A       
@@ -2740,43 +2740,277 @@
 ();%0A
-    %7D%0A%0A%0A    void cancelInternal() %7B
+%0A        gunHandlerMap.put(target.hashCode(), gunHandler);%0A    %7D%0A%0A%0A    void cancelInternal(Object target) %7B%0A        if (!gunHandlerMap.containsKey(target.hashCode())) %7B%0A            return;%0A        %7D%0A%0A        GunHandler gunHandler = gunHandlerMap.get(target.hashCode());
 %0A   
@@ -3060,24 +3060,74 @@
 HAT_METHOD);
+%0A%0A        gunHandlerMap.remove(target.hashCode());
 %0A    %7D%0A%0A%0A   
@@ -3982,11 +3982,10 @@
     %7D;%0A%0A
-%0A
 %7D%0A

support multiple shoot and cancel.