package io.agora.agora_rtm;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nonnull;

import io.agora.rtm.ChannelAttributeOptions;
import io.agora.rtm.LocalInvitation;
import io.agora.rtm.RemoteInvitation;
import io.agora.rtm.RtmAttribute;
import io.agora.rtm.RtmCallManager;
import io.agora.rtm.RtmChannelAttribute;
import io.agora.rtm.RtmChannelMember;
import io.agora.rtm.RtmClient;
import io.agora.rtm.RtmMessage;
import io.agora.rtm.SendMessageOptions;

public class Extensions {
  public static String getString(Map<?, ?> map, String key) {
    if (map.containsKey(key)) {
      Object ret = map.get(key);
      if (ret instanceof String) {
        return (String) ret;
      }
    }
    return null;
  }

  public static int getInt(Map<?, ?> map, String key) {
    if (map.containsKey(key)) {
      Object ret = map.get(key);
      if (ret instanceof Number) {
        return ((Number) ret).intValue();
      }
    }
    return 0;
  }

  public static boolean getBoolean(Map<?, ?> map, String key) {
    if (map.containsKey(key)) {
      Object ret = map.get(key);
      if (ret instanceof Boolean) {
        return (Boolean) ret;
      }
    }
    return false;
  }

  public static Map<?, ?> getMap(Map<?, ?> map, String key) {
    if (map.containsKey(key)) {
      Object ret = map.get(key);
      if (ret instanceof Map) {
        return (Map<?, ?>) ret;
      }
    }
    return new HashMap<>();
  }

  public static List<?> getArray(Map<?, ?> map, String key) {
    if (map.containsKey(key)) {
      Object ret = map.get(key);
      if (ret instanceof List) {
        return (List<?>) ret;
      }
    }
    return new ArrayList<>();
  }

  public static RtmMessage mapToRtmMessage(Map<?, ?> map, @Nonnull RtmClient rtmClient) {
    Map<?, ?> message = getMap(map, "message");
    return rtmClient.createMessage(getString(message, "text"));
  }

  public static SendMessageOptions mapToSendMessageOptions(Map<?, ?> map) {
    return new SendMessageOptions() {{
      Map<?, ?> options = getMap(map, "options");
//      enableOfflineMessaging = getBoolean(options, "enableOfflineMessaging");
//      enableHistoricalMessaging = getBoolean(options, "enableHistoricalMessaging");
    }};
  }

  public static Set<String> mapToPeerIds(Map<?, ?> map) {
    return new HashSet<String>() {{
      List<?> peerIds = getArray(map, "peerIds");
      for (Object item : peerIds) {
        if (item instanceof String) {
          add((String) item);
        }
      }
    }};
  }

  public static List<RtmAttribute> mapToUserAttributes(Map<?, ?> map) {
    return new ArrayList<RtmAttribute>() {{
      List<?> attributes = getArray(map, "attributes");
      for (int i = 0; i < attributes.size(); i++) {
        Object attribute = attributes.get(i);
        if (attribute instanceof Map) {
          add(new RtmAttribute(getString((Map<?, ?>) attribute, "key"), getString((Map<?, ?>) attribute, "value")));
        }
      }
    }};
  }

  public static List<String> mapToAttributeKeys(Map<?, ?> map) {
    return new ArrayList<String>() {{
      List<?> attributeKeys = getArray(map, "attributeKeys");
      for (int i = 0; i < attributeKeys.size(); i++) {
        for (Object item : attributeKeys) {
          if (item instanceof String) {
            add((String) item);
          }
        }
      }
    }};
  }

  public static List<RtmChannelAttribute> mapToChannelAttributes(Map<?, ?> map) {
    return new ArrayList<RtmChannelAttribute>() {{
      List<?> attributes = getArray(map, "attributes");
      for (int i = 0; i < attributes.size(); i++) {
        Object attribute = attributes.get(i);
        if (attribute instanceof Map) {
          add(new RtmChannelAttribute(getString((Map<?, ?>) attribute, "key"), getString((Map<?, ?>) attribute, "value")));
        }
      }
    }};
  }

  public static ChannelAttributeOptions mapToChannelAttributeOptions(Map<?, ?> map) {
    return new ChannelAttributeOptions(getBoolean(getMap(map, "option"), "enableNotificationToChannelMembers"));
  }

  public static LocalInvitation mapToLocalInvitation(Map<?, ?> map, @Nonnull RtmCallManager rtmCallManager) {
    LocalInvitation ret = rtmCallManager.createLocalInvitation(getString(map, "calleeId"));
    ret.setContent(getString(map, "content"));
    ret.setChannelId(getString(map, "channelId"));
    return ret;
  }

  public static Map<String, Object> toMap(RtmMessage message) {
    return new HashMap<String, Object>() {{
      put("text", message.getText());
      put("messageType", message.getMessageType());
      put("serverReceivedTs", message.getServerReceivedTs());
      put("isOfflineMessage", message.isOfflineMessage());
    }};
  }

  public static Map<String, Object> toMap(RtmChannelMember member) {
    return new HashMap<String, Object>() {{
      put("userId", member.getUserId());
      put("channelId", member.getChannelId());
    }};
  }

  public static Map<String, Object> toMap(LocalInvitation localInvitation) {
    return new HashMap<String, Object>() {{
      put("calleeId", localInvitation.getCalleeId());
      put("content", localInvitation.getContent());
      put("channelId", localInvitation.getChannelId());
      put("response", localInvitation.getResponse());
      put("state", localInvitation.getState());
      put("hash", System.identityHashCode(localInvitation));
    }};
  }

  public static Map<String, Object> toMap(RemoteInvitation remoteInvitation) {
    return new HashMap<String, Object>() {{
      put("callerId", remoteInvitation.getCallerId());
      put("content", remoteInvitation.getContent());
      put("channelId", remoteInvitation.getChannelId());
      put("response", remoteInvitation.getResponse());
      put("state", remoteInvitation.getState());
      put("hash", System.identityHashCode(remoteInvitation));
    }};
  }

  public static Map<String, Object> toMap(RtmAttribute attribute) {
    return new HashMap<String, Object>() {{
      put("key", attribute.getKey());
      put("value", attribute.getValue());
    }};
  }

  public static Map<String, Object> toMap(RtmChannelAttribute attribute) {
    return new HashMap<String, Object>() {{
      put("key", attribute.getKey());
      put("value", attribute.getValue());
      put("lastUpdateUserId", attribute.getLastUpdateUserId());
      put("lastUpdateTs", attribute.getLastUpdateTs());
    }};
  }
}
