package lc.stack.zero;

import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import lc.mecha.http.client.HttpUtil;
import lc.mecha.log.MechaLogger;
import lc.mecha.log.MechaLoggerFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet;

/**
 * Implementation of a Zero client.
 *
 * @author Alex Leigh
 */
public class ZeroClient implements Runnable {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(ZeroClient.class);
    public static int ZERO_PORT = 5200;
    private String url = null;
    private final HashSet<Personality> capabilities = new HashSet<>();

    @Override
    public void run() {
        try {
            DatagramSocket serverSocket = new DatagramSocket(ZERO_PORT);
            serverSocket.setBroadcast(true);
            serverSocket.setReuseAddress(true);
            byte[] receiveData = new byte[1500];
            //noinspection InfiniteLoopStatement

            while (true) {
                DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                serverSocket.receive(receivePacket);
                //  HexDump.hexDump(receivePacket.getData(), 0, receivePacket.getLength());
                // FIXME: If this throws we'll break the loop
                Beacon b = new Beacon(receivePacket.getData());
                processBeacon(b);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void processBeacon(Beacon b) throws KeyStoreException, NoSuchAlgorithmException, IOException, KeyManagementException {
        logger.debug("Received beacon: {}", b);
        if (this.url == null) {
            reconfigure(b.getZaiUrl());
        } else if (this.url.equalsIgnoreCase(b.getZaiUrl()) && b.isAuthoritative()) {
            reconfigure(b.getZaiUrl());
        }
    }

    /**
     * Initiate a reconfiguration to a new activation index URL.
     *
     * @param zaiUrl The activation index URL.
     */
    private void reconfigure(String zaiUrl) throws KeyStoreException,
            NoSuchAlgorithmException, KeyManagementException, IOException {
        this.url = zaiUrl;
        logger.info("Reconfiguration event. [ZAI: {}]", this.url);

        HashSet<String> capNames = new HashSet<>();
        for (Personality p : capabilities) {
            capNames.add(p.getName());
        }

        logger.info("Capabilities: {}", capNames);

        HttpClient client = HttpUtil.makeOverlyTrustingClient(null);
        HttpResponse response = client.execute(new HttpGet(this.url));
        HttpEntity entity = response.getEntity();

        JsonElement json = new JsonParser().parse(new InputStreamReader(entity.getContent()));

        logger.info("Read JSON: {}", json);


    }

    public HashSet<Personality> getCapabilities() {
        return capabilities;
    }
}
