import itertools
from hazelcast.future import combine_futures, ImmediateFuture
from hazelcast.protocol.codec import map_add_entry_listener_codec, map_add_entry_listener_to_key_codec, \
    map_add_entry_listener_with_predicate_codec, map_add_entry_listener_to_key_with_predicate_codec, \
    map_add_index_codec, map_clear_codec, map_contains_key_codec, map_contains_value_codec, map_delete_codec, \
    map_entry_set_codec, map_entries_with_predicate_codec, map_evict_codec, map_evict_all_codec, map_flush_codec, \
    map_force_unlock_codec, map_get_codec, map_get_all_codec, map_get_entry_view_codec, map_is_empty_codec, \
    map_is_locked_codec, map_key_set_codec, map_key_set_with_predicate_codec, map_load_all_codec, \
    map_load_given_keys_codec, map_lock_codec, map_put_codec, map_put_all_codec, map_put_if_absent_codec, \
    map_put_transient_codec, map_size_codec, map_remove_codec, map_remove_if_same_codec, \
    map_remove_entry_listener_codec, map_replace_codec, map_replace_if_same_codec, map_set_codec, map_try_lock_codec, \
    map_try_put_codec, map_try_remove_codec, map_unlock_codec, map_values_codec, map_values_with_predicate_codec, \
    map_add_interceptor_codec, map_execute_on_all_keys_codec, map_execute_on_key_codec, map_execute_on_keys_codec, \
    map_execute_with_predicate_codec
from hazelcast.proxy.base import Proxy, EntryEvent, EntryEventType, get_entry_listener_flags
from hazelcast.util import check_not_none, thread_id, to_millis


class Map(Proxy):
    def add_entry_listener(self, include_value=False, key=None, predicate=None, added=None, removed=None, updated=None,
                           evicted=None, evict_all=None, clear_all=None, merged=None, expired=None):
        flags = get_entry_listener_flags(added=added, removed=removed, updated=updated,
                                         evicted=evicted, evict_all=evict_all, clear_all=clear_all, merged=merged,
                                         expired=expired)

        if key and predicate:
            key_data = self._to_data(key)
            predicate_data = self._to_data(predicate)
            request = map_add_entry_listener_to_key_with_predicate_codec.encode_request(self.name, key_data,
                                                                                        predicate_data, include_value,
                                                                                        flags, False)
        elif key and not predicate:
            key_data = self._to_data(key)
            request = map_add_entry_listener_to_key_codec.encode_request(self.name, key_data, include_value, flags,
                                                                         False)
        elif not key and predicate:
            predicate = self._to_data(predicate)
            request = map_add_entry_listener_with_predicate_codec.encode_request(self.name, predicate, include_value,
                                                                                 flags, False)
        else:
            request = map_add_entry_listener_codec.encode_request(self.name, include_value, flags, False)

        def handle_event_entry(**_kwargs):
            event = EntryEvent(self._to_object, **_kwargs)
            if event.event_type == EntryEventType.added:
                added(event)
            elif event.event_type == EntryEventType.removed:
                removed(event)
            elif event.event_type == EntryEventType.updated:
                updated(event)
            elif event.event_type == EntryEventType.evicted:
                evicted(event)
            elif event.event_type == EntryEventType.evict_all:
                evict_all(event)
            elif event.event_type == EntryEventType.clear_all:
                clear_all(event)
            elif event.event_type == EntryEventType.merged:
                merged(event)
            elif event.event_type == EntryEventType.expired:
                expired(event)

        return self._start_listening(request,
                                     lambda m: map_add_entry_listener_codec.handle(m,
                                                                                   handle_event_entry),
                                     lambda r: map_add_entry_listener_codec.decode_response(r)[
                                         'response'])