content: /*
   * Copyright (C) 2015 Actor LLC. <https://actor.im>
   */
  
  package im.actor.core.i18n;
  
  import com.google.j2objc.annotations.ObjectiveCName;
  
  import java.util.Arrays;
  import java.util.Comparator;
  import java.util.Date;
  import java.util.List;
  
  import im.actor.core.entity.ContentType;
  import im.actor.core.entity.Dialog;
  import im.actor.core.entity.Message;
  import im.actor.core.entity.Notification;
  import im.actor.core.entity.PeerType;
  import im.actor.core.entity.Sex;
  import im.actor.core.entity.User;
  import im.actor.core.entity.content.ServiceCallEnded;
  import im.actor.core.entity.content.ServiceCallMissed;
  import im.actor.core.entity.content.ServiceContent;
  import im.actor.core.entity.content.ServiceGroupAvatarChanged;
  import im.actor.core.entity.content.ServiceGroupCreated;
  import im.actor.core.entity.content.ServiceGroupTitleChanged;
  import im.actor.core.entity.content.ServiceGroupTopicChanged;
  import im.actor.core.entity.content.ServiceGroupAboutChanged;
  import im.actor.core.entity.content.ServiceGroupUserInvited;
  import im.actor.core.entity.content.ServiceGroupUserJoined;
  import im.actor.core.entity.content.ServiceGroupUserKicked;
  import im.actor.core.entity.content.ServiceGroupUserLeave;
  import im.actor.core.entity.content.ServiceUserRegistered;
  import im.actor.core.entity.content.TextContent;
  import im.actor.core.modules.Modules;
  import im.actor.core.network.RpcException;
  import im.actor.core.util.JavaUtil;
  import im.actor.core.viewmodel.UserPresence;
  import im.actor.runtime.Assets;
  import im.actor.runtime.Runtime;
  import im.actor.runtime.intl.IntlEngine;
  import im.actor.runtime.json.JSONException;
  
  public class I18nEngine extends IntlEngine {
  
      private static final String TAG = "I18nEngine";
  
      private static final String[] SUPPORTED_LOCALES = new String[]{"Ru", "Ar", "Zn", "Pt", "Es", "Fa"};
      private static final String[] FEMALE = new String[]{"female", "other"};
      private static final String[] MALE = new String[]{"male", "other"};
      private static final String[] DEFAULT = new String[]{"other"};
      private static final String[] YOU = new String[]{"you"};
  
      public static I18nEngine create(Modules modules) {
          String currentLocale = Runtime.getLocaleRuntime().getCurrentLocale();
  
          if (currentLocale != null) {
              if (JavaUtil.contains(SUPPORTED_LOCALES, currentLocale)) {
                  try {
                      return new I18nEngine(
                              modules,
                              Assets.loadAsset("AppText_" + currentLocale + ".json"),
                              Assets.loadAsset("AppText.json"));
                  } catch (JSONException e) {
                      e.printStackTrace();
                  }
              }
          }
          try {
              return new I18nEngine(
                      modules,
                      Assets.loadAsset("AppText.json"),
                      null);
          } catch (JSONException e) {
              // Shoud not happen
              throw new RuntimeException(e);
          }
      }
  
      private final Modules modules;
  
      @ObjectiveCName("initWithModules:withLocalization:withFallback:")
      public I18nEngine(Modules modules, String localization, String fallback) throws JSONException {
          super(localization, fallback);
          this.modules = modules;
      }
  
      @Override
      public String getAppName() {
          String appName = modules.getConfiguration().getCustomAppName();
          if (appName != null) {
              return appName;
          }
          return super.getAppName();
      }
  
  
      //
      // Typing
      //
  
      @ObjectiveCName("formatTyping")
      public String formatTyping() {
          return get("typing.simple");
      }
  
      @ObjectiveCName("formatTypingWithName:")
      public String formatTyping(String name) {
          return get("typing.user")
                  .replace("{user}", name);
      }
  
      @ObjectiveCName("formatTypingWithNames:")
      public String formatTyping(List<String> names) {
          if (names.size() == 1) {
              return formatTyping(names.get(0));
          }
          return get("typing.group.sequenced")
                  .replace("{users}", formatSequence(names));
      }
  
      @ObjectiveCName("formatTypingWithCount:")
      public String formatTyping(int count) {
          return get("typing.group.many")
                  .replace("{count}", "" + count);
      }
  
  
      //
      // Presence
      //
  
      @ObjectiveCName("formatPresence:withSex:")
      public String formatPresence(UserPresence value, Sex sex) {
          if (value == null) {
              return null;
          }
  
          String[] sexType = DEFAULT;
          if (sex == Sex.MALE) {
              sexType = MALE;
          } else if (sex == Sex.FEMALE) {
              sexType = FEMALE;
          }
  
          if (value.getState() == UserPresence.State.OFFLINE) {
  
              long currentTime = im.actor.runtime.Runtime.getCurrentSyncedTime() / 1000L;
              int delta = (int) (currentTime - value.getLastSeen());
              if (delta < 60) {
                  return get("presence.now", sexType);
              } else if (delta < 24 * 60 * 60) {
                  String time = formatTime(value.getLastSeen() * 1000L);
                  if (areSameDays(value.getLastSeen() * 1000L, new Date().getTime())) {
                      return get("presence.today", sexType)
                              .replace("{time}", time);
                  } else {
                      return get("presence.yesterday", sexType)
                              .replace("{time}", time);
                  }
              } else if (delta < 14 * 24 * 60 * 60) {
  
                  String time = formatTime(value.getLastSeen() * 1000L);
                  String date = formatDate(value.getLastSeen() * 1000L);
  
                  return get("presence.at_day_time", sexType)
                          .replace("{time}", time)
                          .replace("{date}", date);
  
              } else if (delta < 6 * 30 * 24 * 60 * 60) {
                  String date = formatDate(value.getLastSeen() * 1000L);
  
                  return get("presence.at_day", sexType)
                          .replace("{date}", date);
              } else {
                  return get("presence.offline", sexType);
              }
          } else if (value.getState() == UserPresence.State.ONLINE) {
              return get("presence.online", sexType);
          }
  
          return null;
      }
  
      @ObjectiveCName("formatGroupOnline:")
      public String formatGroupOnline(int count) {
          return getPlural("presence.members", count)
                  .replace("{count}", "" + count);
      }
  
  
      //
      // Group
      //
  
      /**
       * Formatting Group Members counter
       *
       * @param count number of members
       * @return formatted string like "12 members"
       */
      @ObjectiveCName("formatGroupMembers:")
      public String formatGroupMembers(int count) {
          return getPlural("groups.members", count)
                  .replace("{count}", "" + count);
      }
  
  
      //
      // Content
      //
  
      /**
       * Formatting Dialog List text. Deprecated: you need to manually format Content and append
       * performer if needed and highlight it
       *
       * @param dialog dialog to format
       * @return formatted content
       */
      @Deprecated
      @ObjectiveCName("formatDialogText:")
      public String formatDialogText(Dialog dialog) {
          // Detecting if dialog is empty
          if (dialog.getSenderId() == 0) {
              return "";
          } else {
              String contentText = formatContentText(dialog.getSenderId(),
                      dialog.getMessageType(), dialog.getText(), dialog.getRelatedUid(),
                      dialog.isChannel());
              if (dialog.getPeer().getPeerType() == PeerType.GROUP && !dialog.isChannel()) {
                  if (!isLargeDialogMessage(dialog.getMessageType())) {
                      return formatPerformerName(dialog.getSenderId()) + ": " + contentText;
                  } else {
                      return contentText;
                  }
              } else {
                  return contentText;
              }
          }
      }
  
      /**
       * If Dialog List message need to be wide in group chat as it is already includes performer
       * in it's body.
       *
       * @param contentType Type of Content
       * @return true if content is wide
       */
      @ObjectiveCName("isLargeDialogMessage:")
      public boolean isLargeDialogMessage(ContentType contentType) {
          switch (contentType) {
              case SERVICE:
              case SERVICE_AVATAR:
              case SERVICE_AVATAR_REMOVED:
              case SERVICE_CREATED:
              case SERVICE_TITLE:
              case SERVICE_LEAVE:
              case SERVICE_REGISTERED:
              case SERVICE_KICK:
              case SERVICE_ADD:
              case SERVICE_JOINED:
              case SERVICE_CALL_ENDED:
              case SERVICE_CALL_MISSED:
              case SERVICE_ABOUT:
              case SERVICE_TOPIC:
                  return true;
              default:
                  return false;
          }
      }
  
      /**
       * Formatting Pending notification text
       *
       * @param pendingNotification pending notification
       * @return formatted notification
       */
      @ObjectiveCName("formatNotificationText:")
      public String formatNotificationText(Notification pendingNotification) {
          return formatContentText(pendingNotification.getSender(),
                  pendingNotification.getContentDescription().getContentType(),
                  pendingNotification.getContentDescription().getText(),
                  pendingNotification.getContentDescription().getRelatedUser(),
                  pendingNotification.isChannel());
      }
  
      /**
       * Formatting content for Dialog List and Notifications
       *
       * @param senderId    sender of message (used in service messages)
       * @param contentType type of content
       * @param text        text of message
       * @param relatedUid  optional related uid
       * @return formatted content
       */
      @ObjectiveCName("formatContentTextWithSenderId:withContentType:withText:withRelatedUid:withIsChannel:")
      public String formatContentText(int senderId, ContentType contentType, String text, int relatedUid,
                                      boolean isChannel) {
  
          String groupKey = isChannel ? "channels" : "groups";
  
          switch (contentType) {
              case TEXT:
                  return text;
              case DOCUMENT:
                  if (text == null || text.length() == 0) {
                      return get("content.document");
                  }
                  return text;// File name
              case DOCUMENT_PHOTO:
                  return get("content.photo");
              case DOCUMENT_VIDEO:
                  return get("content.video");
              case DOCUMENT_AUDIO:
                  return get("content.audio");
              case CONTACT:
                  return get("content.contact");
              case LOCATION:
                  return get("content.location");
              case STICKER:
                  if (text != null && !"".equals(text)) {
                      return text + " " + get("content.sticker");
                  } else {
                      return get("content.sticker");
                  }
              case SERVICE:
                  return text;// Should be service message
              case SERVICE_REGISTERED:
                  return getTemplateNamed(senderId, "content.service.registered.compact")
                          .replace("{app_name}", getAppName());
              case SERVICE_CREATED:
                  return getTemplateNamed(senderId, "content.service." + groupKey + ".created");
              case SERVICE_ADD:
                  return getTemplateNamed(senderId, "content.service." + groupKey + ".invited")
                          .replace("{name_added}", getSubjectName(relatedUid));
              case SERVICE_LEAVE:
                  return getTemplateNamed(senderId, "content.service." + groupKey + ".left");
              case SERVICE_KICK:
                  return getTemplateNamed(senderId, "content.service." + groupKey + ".kicked")
                          .replace("{name_kicked}", getSubjectName(relatedUid));
              case SERVICE_AVATAR:
                  return getTemplateNamed(senderId, "content.service." + groupKey + ".avatar_changed");
              case SERVICE_AVATAR_REMOVED:
                  return getTemplateNamed(senderId, "content.service." + groupKey + ".avatar_removed");
              case SERVICE_TITLE:
                  return getTemplateNamed(senderId, "content.service." + groupKey + ".title_changed.compact");
              case SERVICE_TOPIC:
                  return getTemplateNamed(senderId, "content.service." + groupKey + ".topic_changed.compact");
              case SERVICE_ABOUT:
                  return getTemplateNamed(senderId, "content.service." + groupKey + ".about_changed.compact");
              case SERVICE_JOINED:
                  return getTemplateNamed(senderId, "content.service." + groupKey + ".joined");
              case SERVICE_CALL_ENDED:
                  return get("content.service.calls.ended");
              case SERVICE_CALL_MISSED:
                  return get("content.service.calls.missed");
              case NONE:
                  return "";
              default:
              case UNKNOWN_CONTENT:
                  return get("content.unsupported");
          }
      }
  
      /**
       * Formatting Service Content
       *
       * @param senderId sender of a message
       * @param content  content of a message
       * @return formatted message
       */
      @ObjectiveCName("formatFullServiceMessageWithSenderId:withContent:withIsChannel:")
      public String formatFullServiceMessage(int senderId, ServiceContent content, boolean isChannel) {
          String groupKey = isChannel ? "channels" : "groups";
          if (content instanceof ServiceUserRegistered) {
              return getTemplateNamed(senderId, "content.service.registered.full")
                      .replace("{app_name}", getAppName());
          } else if (content instanceof ServiceGroupCreated) {
              return getTemplateNamed(senderId, "content.service." + groupKey + ".created");
          } else if (content instanceof ServiceGroupUserInvited) {
              return getTemplateNamed(senderId, "content.service." + groupKey + ".invited")
                      .replace("{name_added}",
                              getSubjectName(((ServiceGroupUserInvited) content).getAddedUid()));
          } else if (content instanceof ServiceGroupUserKicked) {
              return getTemplateNamed(senderId, "content.service." + groupKey + ".kicked")
                      .replace("{name_kicked}",
                              getSubjectName(((ServiceGroupUserKicked) content).getKickedUid()));
          } else if (content instanceof ServiceGroupUserLeave) {
              return getTemplateNamed(senderId, "content.service." + groupKey + ".left");
          } else if (content instanceof ServiceGroupTitleChanged) {
              return getTemplateNamed(senderId, "content.service." + groupKey + ".title_changed.full")
                      .replace("{title}",
                              ((ServiceGroupTitleChanged) content).getNewTitle());
          } else if (content instanceof ServiceGroupTopicChanged) {
              return getTemplateNamed(senderId, "content.service." + groupKey + ".topic_changed.full")
                      .replace("{topic}",
                              ((ServiceGroupTopicChanged) content).getNewTopic());
          } else if (content instanceof ServiceGroupAboutChanged) {
              return getTemplateNamed(senderId, "content.service." + groupKey + ".about_changed.full")
                      .replace("{about}",
                              ((ServiceGroupAboutChanged) content).getNewAbout());
          } else if (content instanceof ServiceGroupAvatarChanged) {
              if (((ServiceGroupAvatarChanged) content).getNewAvatar() != null) {
                  return getTemplateNamed(senderId, "content.service." + groupKey + ".avatar_changed");
              } else {
                  return getTemplateNamed(senderId, "content.service." + groupKey + ".avatar_removed");
              }
          } else if (content instanceof ServiceGroupUserJoined) {
              return getTemplateNamed(senderId, "content.service." + groupKey + ".joined");
          } else if (content instanceof ServiceCallEnded) {
              return get("content.service.calls.ended");
          } else if (content instanceof ServiceCallMissed) {
              return get("content.service.calls.missed");
          }
  
          return content.getCompatText();
      }
  
  
      //
      // Formatting errors
      //
  
      @ObjectiveCName("formatErrorTextWithTag:")
      public String formatErrorText(String tag) {
          return get(Errors.mapError(tag));
      }
  
      @ObjectiveCName("formatErrorTextWithError:")
      public String formatErrorText(Object o) {
          if (o instanceof RpcException) {
              RpcException e = (RpcException) o;
              String res = Errors.mapError(e.getTag(), null);
              if (res != null) {
                  return get(res);
              } else {
                  if (e.getMessage().equals("")) {
                      return e.getTag();
                  } else {
                      return e.getMessage();
                  }
              }
          } else if (o instanceof Exception) {
              return ((Exception) o).getMessage();
          } else {
              return "" + o;
          }
      }
  
  
      //
      // Exporting messages
      //
  
      /**
       * Formatting messages for exporting
       *
       * @param messages messages to export
       * @return formatted text
       */
      @ObjectiveCName("formatMessagesExport:")
      public String formatMessagesExport(Message[] messages) {
          String text = "";
          Arrays.sort(messages, new Comparator<Message>() {
  
              int compare(long lhs, long rhs) {
                  return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1);
              }
  
              @Override
              public int compare(Message lhs, Message rhs) {
                  return compare(lhs.getEngineSort(), rhs.getEngineSort());
              }
          });
  
          if (messages.length == 1) {
              for (Message model : messages) {
                  if (!(model.getContent() instanceof TextContent)) {
                      continue;
                  }
                  text += ((TextContent) model.getContent()).getText();
              }
          } else {
              for (Message model : messages) {
                  if (!(model.getContent() instanceof TextContent)) {
                      continue;
                  }
                  if (text.length() > 0) {
                      text += "\n";
                  }
                  text += getUser(model.getSenderId()).getName() + ": ";
                  text += ((TextContent) model.getContent()).getText();
              }
          }
          return text;
      }
  
  
      //
      // Tools
      //
  
      private String getTemplateNamed(int senderId, String baseString) {
          String newString = getTemplate(senderId, baseString)
                  .replace("{name}", formatPerformerName(senderId));
  
          // term with 'you' in persian language should be appended with suffix
          if (getLocaleName().equals("Fa")) {
              if (senderId == modules.getAuthModule().myUid()) {
                  newString += "ید";
              }
          }
          return newString;
      }
  
      private String getTemplate(int senderId, String baseString) {
          if (senderId == modules.getAuthModule().myUid()) {
              return get(baseString, YOU);
          }
  
          User u = getUser(senderId);
          String[] sexType = DEFAULT;
          if (u.getSex() == Sex.MALE) {
              sexType = MALE;
          } else if (u.getSex() == Sex.FEMALE) {
              sexType = FEMALE;
          }
          return get(baseString, sexType);
      }
  
      @ObjectiveCName("formatPerformerNameWithUid:")
      public String formatPerformerName(int uid) {
          if (uid == modules.getAuthModule().myUid()) {
              return getYouVerb();
          } else {
              return getUser(uid).getName();
          }
      }
  
      @ObjectiveCName("getSubjectNameWithUid:")
      public String getSubjectName(int uid) {
          if (uid == modules.getAuthModule().myUid()) {
              return getTheeVerb();
          } else {
              User user = getUser(uid);
              return user != null ? user.getName() : "";
          }
      }
  
      private User getUser(int uid) {
          return modules.getUsersModule().getUsersStorage().getValue(uid);
      }
  }
  