package agents;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.TickerBehaviour;
import jade.core.behaviours.WakerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.wrapper.AgentController;
import jade.wrapper.ControllerException;

import java.util.HashSet;
import java.util.Set;

public class Watcher extends Agent {

    private Set<String> warnedAgents = new HashSet<>();

    private void print(String data, String behaviourId) {
        if (behaviourId != null)
            System.out.println("A[" + getLocalName() + "|" + behaviourId + "] " + data);
        else
            System.out.println("A[" + getLocalName() + "] " + data);
    }

    private void checkAndKill(String name) {
        DFAgentDescription template = new DFAgentDescription();
        ServiceDescription tsd = new ServiceDescription();
        tsd.setType("Service3");
        template.addServices(tsd);

        try {
            DFAgentDescription[] results = DFService.search(this, template);
            boolean stillPresent = false;
            for (DFAgentDescription d : results) {
                if (d.getName().getLocalName().equals(name)) {
                    stillPresent = true;
                    break;
                }
            }
            if (stillPresent) {
                print("violator " + name + " still in DF – force killing", "WakerBehaviour");
                AgentController ac = getContainerController().getAgent(name);
                ac.kill();
                print("violator " + name + " killed", "WakerBehaviour");
            } else {
                print("violator " + name + " already gone from DF", "WakerBehaviour");
            }
        } catch (FIPAException e) {
            print("DF search error: " + e.getMessage(), "WakerBehaviour");
        } catch (ControllerException e) {
            print("kill error for " + name + ": " + e.getMessage(), "WakerBehaviour");
        }
    }

    private class PatrolBehaviour extends TickerBehaviour {

        PatrolBehaviour(Agent a, long period) {
            super(a, period);
        }

        @Override
        protected void onTick() {
            DFAgentDescription template = new DFAgentDescription();
            ServiceDescription tsd = new ServiceDescription();
            tsd.setType("Service3");
            template.addServices(tsd);

            DFAgentDescription[] results;
            try {
                results = DFService.search(myAgent, template);
            } catch (FIPAException e) {
                print("DF search error: " + e.getMessage(), "PatrolBehaviour");
                return;
            }

            for (DFAgentDescription d : results) {
                String name = d.getName().getLocalName();
                if (!warnedAgents.contains(name)) {
                    warnedAgents.add(name);
                    print("detected violator: " + name + " – sending ForbiddenSrvc", "PatrolBehaviour");

                    ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
                    msg.addReceiver(new AID(name, AID.ISLOCALNAME));
                    msg.setContent("ForbiddenSrvc");
                    myAgent.send(msg);

                    final String violatorName = name;
                    myAgent.addBehaviour(new WakerBehaviour(myAgent, 5000) {
                        @Override
                        protected void onWake() {
                            checkAndKill(violatorName);
                        }
                    });
                }
            }
        }
    }

    @Override
    protected void setup() {
        print("started – monitoring for Service3 violators", null);
        addBehaviour(new PatrolBehaviour(this, 2000));
    }

    @Override
    protected void takeDown() {
        print("terminated", null);
    }
}
