package cz.senslog.connector.fetch.fieldclimate;

import cz.senslog.connector.model.config.PropertyConfig;
import cz.senslog.connector.model.fieldclimate.SensorType;

import java.util.*;

class AllowedStation {

    private static class Sensor {
        final Integer id;
        final Set<Integer> channels;
        Sensor(Integer id, Set<Integer> channels) {
            this.id = id;
            this.channels = channels;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Sensor sensor = (Sensor) o;
            return id.equals(sensor.id);
        }

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

    private final Map<String, Map<Integer, Sensor>> stations;

    AllowedStation(PropertyConfig config) {
        Set<Long> sensorTypes = SensorType.getIDs();
        Set<String> attributes = config.getAttributes();
        Map<String, Map<Integer, Sensor>> stations = new HashMap<>(attributes.size());
        for (String stationId : attributes) {
            Object attrValue = config.getProperty(stationId);
            if (attrValue instanceof List) {
                List<?> sensors = (List<?>)attrValue;
                for (Object sensor : sensors) {
                    if (sensor instanceof Map) {
                        Map<?, ?> sensorMap = (Map<?, ?>)sensor;
                        for (Map.Entry<?, ?> sensorEntry : sensorMap.entrySet()) {
                            if (sensorEntry.getKey() instanceof Integer) {
                                Integer sensorId = (Integer)sensorEntry.getKey();
                                if (!sensorTypes.contains(sensorId.longValue())) {
                                    throw new UnsupportedOperationException(String.format(
                                            "For allowed sensor '%s' does not exist converter.", sensorId)
                                    );
                                }
                                if (sensorEntry.getValue() instanceof List) {
                                    List<?> channels = (List<?>)sensorEntry.getValue();
                                    Set<Integer> channelsNew = new HashSet<>(channels.size());
                                    for (Object channel : channels) {
                                        if (channel instanceof Integer) {
                                            channelsNew.add((Integer)channel);
                                        }
                                    }
                                    stations.computeIfAbsent(stationId, k -> new HashMap<>())
                                            .put(sensorId, new Sensor(sensorId, channelsNew));
                                }
                            }
                        }
                    } else if (sensor instanceof Integer) {
                        Integer sensorId = (Integer)sensor;
                        if (!sensorTypes.contains(sensorId.longValue())) {
                            throw new UnsupportedOperationException(String.format(
                                    "For allowed sensor '%s' does not exist converter.", sensorId)
                            );
                        }
                        stations.computeIfAbsent(stationId, k -> new HashMap<>())
                                .put(sensorId, new Sensor(sensorId, Collections.emptySet()));
                    }
                }
            }
        }

        this.stations = stations;
    }

    public boolean isAllowed(String stationId) {
        return stations.containsKey(stationId);
    }

    public boolean isAllowed(String stationId, Long sensorId) {
        return isAllowed(stationId) && stations.get(stationId).containsKey(sensorId.intValue());
    }

    public boolean isAllowed(String stationId, Long sensorId, Long channel) {
        return isAllowed(stationId, sensorId) &&
                stations.get(stationId).get(sensorId.intValue()).channels.contains(channel.intValue());
    }
}
