/**
 * File transfer intent service
 *
 * @author Philippe LEMORDANT
 */
public class FileTransferIntentService extends IntentService {

  private static final String LOGTAG = LogUtils.getTag(FileTransferIntentService.class.getName());
  private static final String[] PROJ_UNDELIVERED_FT = new String[] {FileTransferLog.FT_ID};

  private static final String SEL_UNDELIVERED_FTS =
      FileTransferLog.CHAT_ID + "=? AND " + FileTransferLog.EXPIRED_DELIVERY + "='1'";

  /** Constructor */
  public FileTransferIntentService() {
    super("FileTransferIntentService");
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);
    /*
     * We want this service to stop running if forced stop so return not sticky.
     */
    return START_NOT_STICKY;
  }

  @Override
  protected void onHandleIntent(Intent intent) {
    String action;
    if ((action = intent.getAction()) == null) {
      return;
    }
    String transferId = intent.getStringExtra(FileTransferIntent.EXTRA_TRANSFER_ID);
    if (transferId == null) {
      if (LogUtils.isActive) {
        Log.e(LOGTAG, "Cannot read transfer ID");
      }
      return;
    }
    if (LogUtils.isActive) {
      Log.d(LOGTAG, "onHandleIntent file transfer with ID ".concat(transferId));
    }
    switch (action) {
      case FileTransferIntent.ACTION_FILE_TRANSFER_DELIVERY_EXPIRED:
        handleUndeliveredFileTransfer(intent, transferId);
        break;
      case FileTransferIntent.ACTION_NEW_INVITATION:
        handleFileTransferInvitation(intent, transferId);
        break;
      case FileTransferIntent.ACTION_RESUME:
        handleFileTransferResume(intent, transferId);
        break;
      default:
        Log.e(LOGTAG, "Unknown action ".concat(action));
    }
  }

  private void handleFileTransferResume(Intent intent, String transferId) {
    FileTransferDAO ftDao = FileTransferDAO.getFileTransferDAO(this, transferId);
    if (ftDao != null) {
      if (LogUtils.isActive) {
        Log.d(LOGTAG, "onHandleIntent file transfer resume with ID ".concat(transferId));
      }
      if (Direction.INCOMING == ftDao.getDirection()) {
        startActivity(ReceiveFileTransfer.forgeResumeIntent(this, ftDao, intent));
      } else {
        startActivity(InitiateFileTransfer.forgeResumeIntent(this, ftDao, intent));
      }
    }
  }

  private void handleFileTransferInvitation(Intent intent, String transferId) {
    FileTransferDAO ftDao = FileTransferDAO.getFileTransferDAO(this, transferId);
    if (ftDao != null) {
      if (FileTransfer.State.REJECTED == ftDao.getState()) {
        Log.e(LOGTAG, "File transfer already rejected. Id=".concat(transferId));
        return;
      }
      if (LogUtils.isActive) {
        Log.d(
            LOGTAG,
            "File Transfer invitation filename="
                + ftDao.getFilename()
                + " size="
                + ftDao.getSize());
      }
      forwardFileTransferInvitationToUi(intent, ftDao);
    }
  }

  /**
   * Forward file transfer invitation to UI
   *
   * @param invitation Intent invitation
   * @param ftDao the file transfer data object
   */
  private void forwardFileTransferInvitationToUi(Intent invitation, FileTransferDAO ftDao) {
    ContactId contact = ftDao.getContact();
    if (ftDao.getContact() == null) {
      if (LogUtils.isActive) {
        Log.e(LOGTAG, "forwardFileTransferInvitationToUi failed: cannot parse contact");
      }
      return;
    }
    Intent intent = ReceiveFileTransfer.forgeInvitationIntent(this, ftDao, invitation);
    /*
     * If the PendingIntent has the same operation, action, data, categories, components, and
     * flags it will be replaced. Invitation should be notified individually so we use a random
     * generator to provide a unique request code and reuse it for the notification.
     */
    int uniqueId = Utils.getUniqueIdForPendingIntent();
    PendingIntent pi =
        PendingIntent.getActivity(this, uniqueId, intent, PendingIntent.FLAG_ONE_SHOT);

    String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact);
    String title = getString(R.string.title_recv_file_transfer);
    String message = getString(R.string.label_from_args, displayName);

    /* Send notification */
    NotificationManager notificationManager =
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    Notification notif = buildNotification(pi, title, message);
    notificationManager.notify(uniqueId, notif);
    TalkList.notifyNewConversationEvent(this, FileTransferIntent.ACTION_NEW_INVITATION);
  }

  private void handleUndeliveredFileTransfer(Intent intent, String transferId) {
    ContactId contact = intent.getParcelableExtra(FileTransferIntent.EXTRA_CONTACT);
    if (contact == null) {
      if (LogUtils.isActive) {
        Log.e(LOGTAG, "Cannot read contact for ftId=".concat(transferId));
      }
      return;
    }
    if (LogUtils.isActive) {
      Log.d(LOGTAG, "Undelivered file transfer ID=" + transferId + " for contact " + contact);
    }
    forwardUndeliveredFileTransferToUi(intent, contact);
  }

  private void forwardUndeliveredFileTransferToUi(Intent undeliveredIntent, ContactId contact) {
    Intent intent = OneToOneTalkView.forgeIntentOnStackEvent(this, contact, undeliveredIntent);
    ChatPendingIntentManager pendingIntentmanager =
        ChatPendingIntentManager.getChatPendingIntentManager(this);
    Integer uniqueId = pendingIntentmanager.tryContinueChatConversation(intent, contact.toString());
    if (uniqueId != null) {
      PendingIntent contentIntent =
          PendingIntent.getActivity(this, uniqueId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
      String displayName = RcsContactUtil.getInstance(this).getDisplayName(contact);
      String title = getString(R.string.title_undelivered_filetransfer);
      String msg = getString(R.string.label_undelivered_filetransfer, displayName);
      Notification notif = buildNotification(contentIntent, title, msg);
      pendingIntentmanager.postNotification(uniqueId, notif);
    }
  }

  /**
   * Generate a notification
   *
   * @param pendingIntent pending intent
   * @param title title
   * @param message message
   * @return the notification
   */
  private Notification buildNotification(
      PendingIntent pendingIntent, String title, String message) {
    NotificationCompat.Builder notif = new NotificationCompat.Builder(this);
    notif.setContentIntent(pendingIntent);
    notif.setSmallIcon(R.drawable.ri_notif_file_transfer_icon);
    notif.setWhen(System.currentTimeMillis());
    notif.setAutoCancel(true);
    notif.setOnlyAlertOnce(true);
    notif.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
    notif.setDefaults(Notification.DEFAULT_VIBRATE);
    notif.setContentTitle(title);
    notif.setContentText(message);
    return notif.build();
  }

  /**
   * Get set of undelivered file transfers
   *
   * @param ctx The context
   * @param contact The contact
   * @return set of undelivered file transfers
   */
  public static Set<String> getUndelivered(Context ctx, ContactId contact) {
    Set<String> ids = new HashSet<>();
    Cursor cursor = null;
    try {
      cursor =
          ctx.getContentResolver()
              .query(
                  FileTransferLog.CONTENT_URI,
                  PROJ_UNDELIVERED_FT,
                  SEL_UNDELIVERED_FTS,
                  new String[] {contact.toString()},
                  null);
      if (cursor == null) {
        throw new SQLException("Cannot query undelivered file transfers for contact=" + contact);
      }
      if (!cursor.moveToFirst()) {
        return ids;
      }
      int idColumnIdx = cursor.getColumnIndexOrThrow(FileTransferLog.FT_ID);
      do {
        ids.add(cursor.getString(idColumnIdx));
      } while (cursor.moveToNext());
      return ids;

    } finally {
      if (cursor != null) {
        cursor.close();
      }
    }
  }
}
/**
 * Refresh capabilities of a given contact
 *
 * @author Jean-Marc AUFFRET
 */
public class RequestCapabilities extends RcsActivity {

  private final Handler mHandler = new Handler();

  private MyCapabilitiesListener mCapabilitiesListener = new MyCapabilitiesListener();

  private static final String EXTENSION_SEPARATOR = "\n";

  private static final String LOGTAG = LogUtils.getTag(RequestCapabilities.class.getSimpleName());

  /** Spinner for contact selection */
  private Spinner mSpinner;

  private OnClickListener mBtnRefreshListener;

  private OnItemSelectedListener mListenerContact;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    initialize();
    /* Set layout */
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    setContentView(R.layout.capabilities_request);

    /* Set the contact selector */
    mSpinner = (Spinner) findViewById(R.id.contact);
    mSpinner.setAdapter(ContactListAdapter.createContactListAdapter(this));
    mSpinner.setOnItemSelectedListener(mListenerContact);

    /* Set button callback */
    Button refreshBtn = (Button) findViewById(R.id.refresh_btn);
    refreshBtn.setOnClickListener(mBtnRefreshListener);

    /* Update refresh button */
    if (mSpinner.getAdapter().getCount() == 0) {
      // Disable button if no contact available
      refreshBtn.setEnabled(false);
    } else {
      refreshBtn.setEnabled(true);
    }

    /* Register to API connection manager */
    if (!isServiceConnected(RcsServiceName.CAPABILITY)) {
      showMessageThenExit(R.string.label_service_not_available);
      return;
    }
    startMonitorServices(RcsServiceName.CAPABILITY);
    try {
      getCapabilityApi().addCapabilitiesListener(mCapabilitiesListener);

    } catch (RcsServiceException e) {
      showExceptionThenExit(e);
    }
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    if (isServiceConnected(RcsServiceName.CAPABILITY)) {
      // Remove image sharing listener
      try {
        getCapabilityApi().removeCapabilitiesListener(mCapabilitiesListener);

      } catch (RcsServiceException e) {
        Log.w(LOGTAG, ExceptionUtil.getFullStackTrace(e));
      }
    }
  }

  /** Capabilities event listener */
  private class MyCapabilitiesListener extends CapabilitiesListener {
    /**
     * Callback called when new capabilities are received for a given contact
     *
     * @param contact Contact
     * @param capabilities Capabilities
     */
    public void onCapabilitiesReceived(final ContactId contact, final Capabilities capabilities) {
      if (LogUtils.isActive) {
        Log.d(LOGTAG, "onCapabilitiesReceived " + contact);
      }
      final ContactId selectedContact = getSelectedContact();
      if (!contact.equals(selectedContact)) {
        // Discard capabilities if not for selected contact
        return;
      }
      mHandler.post(
          new Runnable() {
            public void run() {
              // Check if this intent concerns the current selected
              // contact
              if (contact.equals(selectedContact)) {
                // Update UI
                displayCapabilities(capabilities);
              }
            }
          });
    }
  }

  /**
   * Returns the selected contact
   *
   * @return Contact
   */
  private ContactId getSelectedContact() {
    // get selected phone number
    ContactListAdapter adapter = (ContactListAdapter) mSpinner.getAdapter();
    return ContactUtil.formatContact(adapter.getSelectedNumber(mSpinner.getSelectedView()));
  }

  private void updateCapabilities(ContactId contact) {
    // Display info
    Utils.displayLongToast(
        RequestCapabilities.this, getString(R.string.label_request_in_background, contact));
    try {
      Set<ContactId> contactSet = new HashSet<>();
      contactSet.add(contact);
      getCapabilityApi().requestContactCapabilities(contactSet);

    } catch (RcsServiceException e) {
      showExceptionThenExit(e);
    }
  }

  private void displayCapabilities(Capabilities capabilities) {
    CheckBox imageCSh = (CheckBox) findViewById(R.id.image_sharing);
    CheckBox videoCSh = (CheckBox) findViewById(R.id.video_sharing);
    CheckBox ft = (CheckBox) findViewById(R.id.file_transfer);
    CheckBox im = (CheckBox) findViewById(R.id.im);
    CheckBox geoloc = (CheckBox) findViewById(R.id.geoloc_push);
    TextView extensions = (TextView) findViewById(R.id.extensions);
    TextView timestamp = (TextView) findViewById(R.id.last_refresh);
    CheckBox automata = (CheckBox) findViewById(R.id.automata);

    if (capabilities != null) {
      // Set capabilities
      imageCSh.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_IMAGE_SHARING));
      videoCSh.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_VIDEO_SHARING));
      ft.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_FILE_TRANSFER));
      im.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_IM));
      geoloc.setChecked(capabilities.hasCapabilities(Capabilities.CAPABILITY_GEOLOC_PUSH));
    }
    // Set extensions
    extensions.setVisibility(View.VISIBLE);
    extensions.setText(getExtensions(capabilities));
    automata.setChecked((capabilities != null) && capabilities.isAutomata());
    timestamp.setText(
        (capabilities != null)
            ? DateUtils.getRelativeTimeSpanString(
                capabilities.getTimestamp(),
                System.currentTimeMillis(),
                DateUtils.MINUTE_IN_MILLIS,
                DateUtils.FORMAT_ABBREV_RELATIVE)
            : "");
  }

  /* package private */ static String getExtensions(Capabilities capabilities) {
    if (capabilities == null || capabilities.getSupportedExtensions().isEmpty()) {
      return "";
    }
    StringBuilder extensions = new StringBuilder();
    for (String capability : capabilities.getSupportedExtensions()) {
      extensions.append(EXTENSION_SEPARATOR).append(capability);
    }
    return extensions.substring(EXTENSION_SEPARATOR.length());
  }

  private void initialize() {
    mBtnRefreshListener =
        new OnClickListener() {
          public void onClick(View v) {
            // Check if the service is available
            try {
              if (!getCapabilityApi().isServiceRegistered()) {
                showMessage(R.string.error_not_registered);
                return;
              }
            } catch (RcsServiceException e) {
              showExceptionThenExit(e);
              return;
            }
            ContactId contact = getSelectedContact();
            if (contact != null) {
              updateCapabilities(contact);
            }
          }
        };

    mListenerContact =
        new OnItemSelectedListener() {
          @Override
          public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            CapabilityService capabilityApi = getCapabilityApi();
            try {
              // Get selected contact
              ContactId contactId = getSelectedContact();

              // Get current capabilities
              Capabilities currentCapabilities = capabilityApi.getContactCapabilities(contactId);
              // Display default capabilities
              displayCapabilities(currentCapabilities);
              if (currentCapabilities == null) {
                Utils.displayLongToast(
                    RequestCapabilities.this,
                    getString(R.string.label_no_capabilities, contactId.toString()));
              }
            } catch (RcsServiceException e) {
              showExceptionThenExit(e);
            }
          }

          @Override
          public void onNothingSelected(AdapterView<?> arg0) {}
        };
  }
}