package org.mitallast.queue.raft.state;

import com.google.inject.Inject;
import org.mitallast.queue.common.component.AbstractComponent;
import org.mitallast.queue.common.settings.Settings;
import org.mitallast.queue.raft.cluster.Member;
import org.mitallast.queue.raft.util.ExecutionContext;
import org.mitallast.queue.transport.DiscoveryNode;

import java.util.*;

public class ClusterState extends AbstractComponent implements Iterable<MemberState> {
    private final Map<DiscoveryNode, MemberState> members = new HashMap<>();
    private final List<MemberState> activeMembers = new ArrayList<>();
    private final List<MemberState> passiveMembers = new ArrayList<>();
    private final ExecutionContext executionContext;

    @Inject
    public ClusterState(Settings settings, ExecutionContext executionContext) {
        super(settings);
        this.executionContext = executionContext;
    }

    ClusterState addMember(MemberState member) {
        executionContext.checkThread();
        members.put(member.getNode(), member);
        if (member.getType() == Member.Type.ACTIVE) {
            addActiveMember(member);
        } else {
            addPassiveMember(member);
        }
        return this;
    }

    private void addActiveMember(MemberState member) {
        executionContext.checkThread();
        activeMembers.add(member);
        logger.info("add active member {}", member.getNode());
        logger.info("active members: {}", activeMembers);
    }

    private void addPassiveMember(MemberState member) {
        executionContext.checkThread();
        passiveMembers.add(member);
    }

    ClusterState removeMember(MemberState member) {
        executionContext.checkThread();
        members.remove(member.getNode());
        if (member.getType() == Member.Type.ACTIVE) {
            removeActiveMember(member);
        } else {
            removePassiveMember(member);
        }
        return this;
    }

    private void removeActiveMember(MemberState member) {
        executionContext.checkThread();
        Iterator<MemberState> iterator = activeMembers.iterator();
        while (iterator.hasNext()) {
            if (iterator.next().getNode().equals(member.getNode())) {
                iterator.remove();
            }
        }
    }

    private void removePassiveMember(MemberState member) {
        executionContext.checkThread();
        Iterator<MemberState> iterator = passiveMembers.iterator();
        while (iterator.hasNext()) {
            if (iterator.next().getNode().equals(member.getNode())) {
                iterator.remove();
            }
        }
    }

    MemberState getMember(DiscoveryNode node) {
        executionContext.checkThread();
        return members.get(node);
    }

    List<MemberState> getActiveMembers() {
        executionContext.checkThread();
        return activeMembers;
    }

    List<MemberState> getPassiveMembers() {
        executionContext.checkThread();
        return passiveMembers;
    }

    @Override
    public Iterator<MemberState> iterator() {
        executionContext.checkThread();
        return new ClusterStateIterator(members.entrySet().iterator());
    }

    private class ClusterStateIterator implements Iterator<MemberState> {
        private final Iterator<Map.Entry<DiscoveryNode, MemberState>> iterator;
        private MemberState member;

        private ClusterStateIterator(Iterator<Map.Entry<DiscoveryNode, MemberState>> iterator) {
            executionContext.checkThread();
            this.iterator = iterator;
        }

        @Override
        public boolean hasNext() {
            executionContext.checkThread();
            return iterator.hasNext();
        }

        @Override
        public MemberState next() {
            executionContext.checkThread();
            member = iterator.next().getValue();
            return member;
        }

        @Override
        public void remove() {
            executionContext.checkThread();
            iterator.remove();
            removeMember(member);
        }
    }
}

@@ -30,24 +30,72 @@
 aft.state;%0A%0A
+import com.google.common.collect.ImmutableList;%0A
 import com.g
@@ -406,16 +406,52 @@
 .util.*;
+%0Aimport java.util.stream.Collectors;
 %0A%0Apublic
@@ -2702,16 +2702,23 @@
  %7D%0A%0A    
+public 
 MemberSt
@@ -2830,32 +2830,48 @@
 de);%0A    %7D%0A%0A    
+public Immutable
 List%3CMemberState
@@ -2948,16 +2948,37 @@
  return 
+ImmutableList.copyOf(
 activeMe
@@ -2974,32 +2974,33 @@
 Of(activeMembers
+)
 ;%0A    %7D%0A%0A    Lis
@@ -2996,16 +2996,32 @@
  %7D%0A%0A    
+public Immutable
 List%3CMem
@@ -3107,30 +3107,747 @@
  return 
-passiveMembers
+ImmutableList.copyOf(passiveMembers);%0A    %7D%0A%0A    public ImmutableList%3CMemberState%3E getMembers() %7B%0A        executionContext.checkThread();%0A        return ImmutableList.copyOf(passiveMembers);%0A    %7D%0A%0A    public ImmutableList%3CDiscoveryNode%3E activeNodes() %7B%0A        return ImmutableList.copyOf(getActiveMembers().stream().map(MemberState::getNode).collect(Collectors.toList()));%0A    %7D%0A%0A    public ImmutableList%3CDiscoveryNode%3E passiveNodes() %7B%0A        return ImmutableList.copyOf(getPassiveMembers().stream().map(MemberState::getNode).collect(Collectors.toList()));%0A    %7D%0A%0A    public ImmutableList%3CDiscoveryNode%3E nodes() %7B%0A        return ImmutableList.copyOf(getMembers().stream().map(MemberState::getNode).collect(Collectors.toList()))
 ;%0A    %7D%0A

refactor transport cluster