/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.dialer.voicemail.listui.error;

import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.VoicemailContract.Status;
import android.support.annotation.Nullable;
import android.support.v4.os.BuildCompat;
import android.telecom.PhoneAccountHandle;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.android.dialer.common.LogUtil;
import com.android.dialer.voicemailstatus.VoicemailStatusQuery;
import com.android.voicemail.VoicemailClient;
import com.android.voicemail.VoicemailComponent;

/** Structured data from {@link android.provider.VoicemailContract.Status} */
public class VoicemailStatus {

  public final String sourcePackage;
  public final String type;

  public final String phoneAccountComponentName;
  public final String phoneAccountId;

  @Nullable public final Uri settingsUri;
  @Nullable public final Uri voicemailAccessUri;

  public final int configurationState;
  public final int dataChannelState;
  public final int notificationChannelState;

  public final int quotaOccupied;
  public final int quotaTotal;

  // System status

  public final boolean isAirplaneMode;

  /** Wraps the row currently pointed by <code>statusCursor</code> */
  public VoicemailStatus(Context context, Cursor statusCursor) {
    sourcePackage = getString(statusCursor, VoicemailStatusQuery.SOURCE_PACKAGE_INDEX, "");

    settingsUri = getUri(statusCursor, VoicemailStatusQuery.SETTINGS_URI_INDEX);
    voicemailAccessUri = getUri(statusCursor, VoicemailStatusQuery.VOICEMAIL_ACCESS_URI_INDEX);

    if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
      type =
          getString(
              statusCursor, VoicemailStatusQuery.SOURCE_TYPE_INDEX, TelephonyManager.VVM_TYPE_OMTP);
      phoneAccountComponentName =
          getString(statusCursor, VoicemailStatusQuery.PHONE_ACCOUNT_COMPONENT_NAME, "");
      phoneAccountId = getString(statusCursor, VoicemailStatusQuery.PHONE_ACCOUNT_ID, "");
    } else {
      type = TelephonyManager.VVM_TYPE_OMTP;
      phoneAccountComponentName = "";
      phoneAccountId = "";
    }

    configurationState =
        getInt(
            statusCursor,
            VoicemailStatusQuery.CONFIGURATION_STATE_INDEX,
            Status.CONFIGURATION_STATE_NOT_CONFIGURED);
    dataChannelState =
        getInt(
            statusCursor,
            VoicemailStatusQuery.DATA_CHANNEL_STATE_INDEX,
            Status.DATA_CHANNEL_STATE_NO_CONNECTION);

    /* Before O, the NOTIFICATION_CHANNEL_STATE in the voicemail status table for the system
     * visual voicemail client always correspond to the service state (cellular signal availability)
     * Tracking the state in the background is redundant because it will not be visible to the
     * user. It is much simpler to poll the status on the UI side. The result is injected back to
     * the status query result so the handling will be consistent with other voicemail clients.
     */
    if (BuildCompat.isAtLeastO() && sourcePackage.equals(context.getPackageName())) {
      notificationChannelState =
          getNotificationChannelStateFormTelephony(context, getPhoneAccountHandle());
    } else {
      notificationChannelState =
          getInt(
              statusCursor,
              VoicemailStatusQuery.NOTIFICATION_CHANNEL_STATE_INDEX,
              Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
    }
    isAirplaneMode =
        Settings.System.getInt(context.getContentResolver(), Global.AIRPLANE_MODE_ON, 0) != 0;
    quotaOccupied =
        getInt(statusCursor, VoicemailStatusQuery.QUOTA_OCCUPIED_INDEX, Status.QUOTA_UNAVAILABLE);
    quotaTotal =
        getInt(statusCursor, VoicemailStatusQuery.QUOTA_TOTAL_INDEX, Status.QUOTA_UNAVAILABLE);
  }

  private VoicemailStatus(Builder builder) {
    sourcePackage = builder.sourcePackage;
    phoneAccountComponentName = builder.phoneAccountComponentName;
    phoneAccountId = builder.phoneAccountId;
    type = builder.type;
    settingsUri = builder.settingsUri;
    voicemailAccessUri = builder.voicemailAccessUri;
    configurationState = builder.configurationState;
    dataChannelState = builder.dataChannelState;
    notificationChannelState = builder.notificationChannelState;
    quotaOccupied = builder.quotaOccupied;
    quotaTotal = builder.quotaTotal;
    isAirplaneMode = builder.isAirplaneMode;
  }

  @TargetApi(VERSION_CODES.O)
  private static int getNotificationChannelStateFormTelephony(
      Context context, PhoneAccountHandle phoneAccountHandle) {
    TelephonyManager telephonyManager =
        context
            .getSystemService(TelephonyManager.class)
            .createForPhoneAccountHandle(phoneAccountHandle);
    if (telephonyManager == null) {
      LogUtil.e("VoicemailStatus.constructor", "invalid PhoneAccountHandle");
      return Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION;
    } else {
      int state = telephonyManager.getServiceState().getState();
      if (state == ServiceState.STATE_IN_SERVICE) {
        return Status.NOTIFICATION_CHANNEL_STATE_OK;
      } else {
        return Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION;
      }
    }
  }

  static class Builder {

    private String sourcePackage = "";
    private String type = TelephonyManager.VVM_TYPE_OMTP;
    private String phoneAccountComponentName = "";
    private String phoneAccountId = "";

    @Nullable private Uri settingsUri;
    @Nullable private Uri voicemailAccessUri;

    private int configurationState = Status.CONFIGURATION_STATE_NOT_CONFIGURED;
    private int dataChannelState = Status.DATA_CHANNEL_STATE_NO_CONNECTION;
    private int notificationChannelState = Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION;

    private int quotaOccupied = Status.QUOTA_UNAVAILABLE;
    private int quotaTotal = Status.QUOTA_UNAVAILABLE;

    private boolean isAirplaneMode;

    public VoicemailStatus build() {
      return new VoicemailStatus(this);
    }

    public Builder setSourcePackage(String sourcePackage) {
      this.sourcePackage = sourcePackage;
      return this;
    }

    public Builder setType(String type) {
      this.type = type;
      return this;
    }

    public Builder setPhoneAccountComponentName(String name) {
      this.phoneAccountComponentName = name;
      return this;
    }

    public Builder setPhoneAccountId(String id) {
      this.phoneAccountId = id;
      return this;
    }

    public Builder setSettingsUri(Uri settingsUri) {
      this.settingsUri = settingsUri;
      return this;
    }

    public Builder setVoicemailAccessUri(Uri voicemailAccessUri) {
      this.voicemailAccessUri = voicemailAccessUri;
      return this;
    }

    public Builder setConfigurationState(int configurationState) {
      this.configurationState = configurationState;
      return this;
    }

    public Builder setDataChannelState(int dataChannelState) {
      this.dataChannelState = dataChannelState;
      return this;
    }

    public Builder setNotificationChannelState(int notificationChannelState) {
      this.notificationChannelState = notificationChannelState;
      return this;
    }

    public Builder setQuotaOccupied(int quotaOccupied) {
      this.quotaOccupied = quotaOccupied;
      return this;
    }

    public Builder setQuotaTotal(int quotaTotal) {
      this.quotaTotal = quotaTotal;
      return this;
    }

    public Builder setAirplaneMode(boolean isAirplaneMode) {
      this.isAirplaneMode = isAirplaneMode;
      return this;
    }
  }

  public boolean isActive(Context context) {
    VoicemailClient voicemailClient = VoicemailComponent.get(context).getVoicemailClient();
    if (context.getPackageName().equals(sourcePackage)) {
      if (!voicemailClient.isVoicemailModuleEnabled()) {
        LogUtil.i("VoicemailStatus.isActive", "module disabled");
        return false;
      }
      if (!voicemailClient.hasCarrierSupport(context, getPhoneAccountHandle())) {
        LogUtil.i("VoicemailStatus.isActive", "carrier not supported");
        return false;
      }
      if (!voicemailClient.isVoicemailEnabled(context, getPhoneAccountHandle())) {
        LogUtil.i("VoicemailStatus.isActive", "VVM disabled");
        return false;
      }
    }
    switch (configurationState) {
      case Status.CONFIGURATION_STATE_NOT_CONFIGURED:
      case Status.CONFIGURATION_STATE_DISABLED:
        return false;
      default:
        return true;
    }
  }

  @Override
  public String toString() {
    return "VoicemailStatus["
        + "sourcePackage: "
        + sourcePackage
        + ", type:"
        + type
        + ", settingsUri: "
        + settingsUri
        + ", voicemailAccessUri: "
        + voicemailAccessUri
        + ", configurationState: "
        + configurationState
        + ", dataChannelState: "
        + dataChannelState
        + ", notificationChannelState: "
        + notificationChannelState
        + ", quotaOccupied: "
        + quotaOccupied
        + ", quotaTotal: "
        + quotaTotal
        + ", isAirplaneMode: "
        + isAirplaneMode
        + "]";
  }

  @Nullable
  private static Uri getUri(Cursor cursor, int index) {
    if (cursor.getString(index) != null) {
      return Uri.parse(cursor.getString(index));
    }
    return null;
  }

  private static int getInt(Cursor cursor, int index, int defaultValue) {
    if (cursor.isNull(index)) {
      return defaultValue;
    }
    return cursor.getInt(index);
  }

  private static String getString(Cursor cursor, int index, String defaultValue) {
    if (cursor.isNull(index)) {
      return defaultValue;
    }
    return cursor.getString(index);
  }

  @Nullable
  public PhoneAccountHandle getPhoneAccountHandle() {
    if (TextUtils.isEmpty(phoneAccountComponentName) || TextUtils.isEmpty(phoneAccountId)) {
      return null;
    }
    ComponentName componentName = ComponentName.unflattenFromString(phoneAccountComponentName);
    if (componentName == null) {
      return null;
    }
    return new PhoneAccountHandle(componentName, phoneAccountId);
  }
}
