package com.leroymerlin.pandroid.event;

import android.os.Handler;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * Created by florian on 26/11/15.
 */
public class EventBusManagerImpl implements EventBusManager {

    private static EventBusManagerImpl instance;

    private final Handler handler;

    private List<EventBusReceiver> receivers = new ArrayList<>();

    private final List<Message> box = new ArrayList<>();


    public EventBusManagerImpl() {
        handler = new Handler();
    }

    public static EventBusManagerImpl getMainInstance() {
        if (instance == null)
            instance = new EventBusManagerImpl();
        return instance;
    }

    private void checkBox(List<EventBusReceiver> eventBusReceivers) {
        synchronized (box) {
            for (int i = box.size() - 1; i >= 0; i--) {
                deliverMessage(box.get(i), eventBusReceivers);
                if (box.get(i).isDelivered())
                    box.remove(i);
            }
        }
    }

    private void deliverMessage(Message message) {
        deliverMessage(message, receivers);
    }

    private void deliverMessage(Message message, List<EventBusReceiver> eventBusReceivers) {
        for (int i = eventBusReceivers.size() - 1; i >= 0; i--) {
            EventBusReceiver receiver = eventBusReceivers.get(i);
            if ((message.messageTag == null && (receiver.getTags() == null || receiver.getTags().isEmpty())) || (receiver.getTags() != null && receiver.getTags().contains(message.messageTag))) {
                if (receiver.handle(message.data)) {
                    message.setDelivered();
                }
            }
        }
    }


    @Override
    public String send(Object data) {
        return send(data, null);
    }

    @Override
    public String send(final Object data, String messageTag) {
        return send(data, messageTag, DeliveryPolicy.UNCHECKED);
    }

    @Override
    public String send(Object data, String messageTag, DeliveryPolicy deliveryPolicy) {
        return send(data, messageTag, deliveryPolicy, null);
    }

    @Override
    public String send(Object data, String messageTag, DeliveryPolicy deliveryPolicy, String messageID) {
        final Message message = new Message(data, messageTag, deliveryPolicy, messageID);
        synchronized (box) {
            if (messageID != null)
                cancel(messageID);
            box.add(0, message);
        }
        handler.post(new Runnable() {
            @Override
            public void run() {
                synchronized (box) {
                    if (box.remove(message)) {
                        handleMessage(message);
                    }
                }
            }
        });
        return message.id;
    }

    @Override
    public String sendSync(Object data) {
        return sendSync(data, null);
    }

    @Override
    public String sendSync(Object data, String messageTag) {
        return sendSync(data, messageTag, DeliveryPolicy.UNCHECKED);
    }

    @Override
    public String sendSync(Object data, String messageTag, DeliveryPolicy deliveryPolicy) {
        return sendSync(data, messageTag, deliveryPolicy, null);
    }

    @Override
    public String sendSync(Object data, String messageTag, DeliveryPolicy deliveryPolicy, String messageID) {
        Message message = new Message(data, messageTag, deliveryPolicy, messageID);
        if (messageID != null)
            cancel(messageID);
        handleMessage(message);
        return message.id;
    }

    private void handleMessage(Message message) {
        deliverMessage(message);
        if (!message.isDelivered()) {
            synchronized (box) {
                box.add(0, message);
            }
        }
    }

    @Override
    public boolean cancel(String id) {
        synchronized (box) {
            for (int i = box.size() - 1; i >= 0; i--) {
                if (box.get(i).id.equals(id)) {
                    box.remove(i);
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public boolean clearEventBox() {
        synchronized (box) {
            box.clear();
        }
        return false;
    }

    @Override
    public void registerReceiver(EventBusReceiver eventBusReceiver) {
        ArrayList<EventBusReceiver> eventBusReceivers = new ArrayList<>();
        eventBusReceivers.add(eventBusReceiver);
        registerReceivers(eventBusReceivers);
    }

    @Override
    public void registerReceivers(List<EventBusReceiver> eventBusReceivers) {
        this.receivers.addAll(eventBusReceivers);
        checkBox(eventBusReceivers);
    }

    @Override
    public void unregisterReceiver(EventBusReceiver eventBusReceiver) {
        this.receivers.remove(eventBusReceiver);
    }

    @Override
    public void unregisterReceivers(List<EventBusReceiver> eventBusReceivers) {
        this.receivers.removeAll(eventBusReceivers);
    }


    private class Message {
        private final DeliveryPolicy deliveryPolicy;
        String messageTag;
        Object data;
        String id;
        private boolean delivered;

        public Message(Object data, String messageTag, DeliveryPolicy deliveryPolicy, String messageId) {
            this.data = data;
            this.messageTag = messageTag;
            this.deliveryPolicy = deliveryPolicy;
            if (messageId == null)
                id = UUID.randomUUID().toString();
            else
                id = messageId;
        }

        @Override
        public boolean equals(Object o) {
            return (o instanceof Message && ((Message) o).id.equals(id)) || id.equals(o);
        }

        @Override
        public int hashCode() {
            return id.hashCode();
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            if (null == messageTag) {
                builder.append("receiversTag : all");
            } else {
                builder.append("receiversTag : ").append(messageTag);
            }
            if (data != null)
                builder.append("\n").append("data : ").append(data.getClass().getSimpleName()).append(" : ").append(data.toString());
            else
                builder.append("\n").append("data : ").append("null");

            return builder.toString();
        }

        public boolean isDelivered() {
            return deliveryPolicy == DeliveryPolicy.UNCHECKED || (delivered && deliveryPolicy == DeliveryPolicy.AT_LEAST_ONE);
        }

        public void setDelivered() {
            delivered = true;
        }
    }

}

@@ -847,32 +847,82 @@
  i %3E= 0; i--) %7B%0A
+                if (box.get(i).isTreated()) %7B%0A    
                 
@@ -976,32 +976,36 @@
                 
+    
 if (box.get(i).i
@@ -1030,32 +1030,36 @@
                 
+   
+ 
 box.remove(i);%0A 
@@ -1049,32 +1049,50 @@
  box.remove(i);%0A
+                %7D%0A
             %7D%0A  
@@ -3669,32 +3669,62 @@
 sage message) %7B%0A
+        message.setTreated();%0A
         deliverM
@@ -5274,16 +5274,138 @@
 livered;
+ //handle by only one receiver%0A        private boolean treated; //have been handle by the manager (related to unsync send)
 %0A%0A      
@@ -6638,39 +6638,32 @@
      %7D%0A%0A        
-public 
 boolean isDelive
@@ -6812,31 +6812,24 @@
  %7D%0A%0A        
-public 
 void setDeli
@@ -6869,26 +6869,163 @@
  true;%0A        %7D
+%0A%0A        boolean isTreated() %7B%0A            return treated;%0A        %7D%0A%0A%0A        void setTreated() %7B%0A            treated = true;%0A        %7D
 %0A    %7D%0A%0A%7D%0A

fix (EventBus) : fix bug with register receiver before message delivery