content: /*
   * Copyright (C) 2014 AChep@xda <artemchep@gmail.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   * MA  02110-1301, USA.
   */
  package com.achep.base.ui.fragments.dialogs;
  
  import android.app.Dialog;
  import android.content.ActivityNotFoundException;
  import android.content.Context;
  import android.content.Intent;
  import android.os.Bundle;
  import androidx.annotation.LayoutRes;
  import androidx.annotation.NonNull;
  import android.util.Log;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.widget.AdapterView;
  import android.widget.FrameLayout;
  import android.widget.ImageView;
  import android.widget.ProgressBar;
  
  import com.achep.acdisplay.R;
  import com.achep.base.AppHeap;
  import com.achep.base.billing.Bitcoin;
  import com.achep.base.billing.PayPal;
  import com.achep.base.billing.SkuUi;
  import com.achep.base.interfaces.IActivityBase;
  import com.achep.base.interfaces.IConfiguration;
  import com.achep.base.ui.adapters.BetterArrayAdapter;
  import com.achep.base.ui.widgets.HeaderGridView;
  import com.achep.base.ui.widgets.TextView;
  import com.achep.base.utils.CoinUtils;
  import com.achep.base.utils.RippleUtils;
  import com.achep.base.utils.ToastUtils;
  import com.achep.base.utils.ViewUtils;
  import com.afollestad.materialdialogs.DialogAction;
  import com.afollestad.materialdialogs.MaterialDialog;
  
  import org.solovyev.android.checkout.ActivityCheckout;
  import org.solovyev.android.checkout.BillingRequests;
  import org.solovyev.android.checkout.Checkout;
  import org.solovyev.android.checkout.Inventory;
  import org.solovyev.android.checkout.ProductTypes;
  import org.solovyev.android.checkout.Purchase;
  import org.solovyev.android.checkout.RequestListener;
  import org.solovyev.android.checkout.ResponseCodes;
  import org.solovyev.android.checkout.Sku;
  
  import java.util.Comparator;
  
  import static com.achep.base.Build.DEBUG;
  
  /**
   * @author Artem Chepurnoy
   */
  public class DonateDialog extends DialogFragment {
  
      private static final String TAG = "DonateFragment";
  
      private static final int SCREEN_LOADING = 1;
      private static final int SCREEN_INVENTORY = 2;
      private static final int SCREEN_EMPTY_VIEW = 4;
  
      @NonNull
      private final InventoryLoadedListener mInventoryLoadedListener = new InventoryLoadedListener();
      @NonNull
      private final PurchaseListener mPurchaseListener = new PurchaseListener();
      private ActivityCheckout mCheckout;
      private SkusAdapter mAdapter;
      private Inventory mInventory;
  
      private ProgressBar mProgressBar;
      private TextView mEmptyView;
  
      @Override
      public void onAttach(Context context) {
          super.onAttach(context);
          if (context instanceof IActivityBase) {
              IActivityBase ma = (IActivityBase) context;
              mCheckout = ma.getCheckout();
  
              if (mCheckout == null) {
                  String message = "You must call #requestCheckout() on the activity before!";
                  throw new RuntimeException(message);
              }
  
              return; // don't crash
          }
  
          throw new RuntimeException("Host activity must be an instance of IActivityBase.class!");
      }
  
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          mInventory = mCheckout.loadInventory();
      }
  
      @NonNull
      @Override
      public Dialog onCreateDialog(Bundle savedInstanceState) {
          MaterialDialog md = initDialog();
          LayoutInflater inflater = (LayoutInflater) getActivity()
                  .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
          FrameLayout frameLayout = new FrameLayout(getActivity()); //
  
          // Init description message.
          TextView textView = (TextView) inflater.inflate(R.layout.dialog_message, frameLayout, false);
          textView.setText(R.string.donate_dialog_message);
          textView.setPadding(0, textView.getPaddingTop(), 0, textView.getPaddingBottom() / 2);
  
          // Init view with error view and progressbar-s.
          View phView = inflater.inflate(R.layout.dialog_donate_placeholder, frameLayout, false);
          mProgressBar = (ProgressBar) phView.findViewById(R.id.progress);
          mEmptyView = (TextView) phView.findViewById(R.id.empty);
          mEmptyView.setText(R.string.donate_billing_not_supported);
  
          assert md.getCustomView() != null;
          HeaderGridView gv = (HeaderGridView) md.getCustomView().findViewById(R.id.grid);
          gv.addHeaderView(textView, null, false);
          gv.addHeaderView(phView, null, false);
          gv.setAdapter(mAdapter = new SkusAdapter(getActivity(), R.layout.sku));
          gv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
              @Override
              public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                  SkuUi skuUi = (SkuUi) parent.getAdapter().getItem(position);
                  purchase(skuUi.sku);
              }
          });
  
          return md;
      }
  
      @NonNull
      private MaterialDialog initDialog() {
          MaterialDialog.Builder builder = new MaterialDialog.Builder(getActivity())
                  .iconRes(R.drawable.ic_gift_white_24dp)
                  .title(R.string.donate_dialog_title)
                  .customView(R.layout.dialog_donate, false)
                  .neutralText(R.string.close);
  
          final IConfiguration configuration = AppHeap.getInstance().getConfiguration();
          final boolean hasApl = configuration.getBilling().hasAlternativePaymentMethods();
          if (!hasApl) return builder.build();
  
          final Bitcoin btc = new Bitcoin();
          final PayPal pp = new PayPal();
  
          MaterialDialog.SingleButtonCallback callback = new MaterialDialog.SingleButtonCallback() {
              @Override
              public void onClick(@NonNull MaterialDialog materialDialog,
                                  @NonNull DialogAction dialogAction) {
                  if (dialogAction == DialogAction.POSITIVE) {
                      startPaymentIntentWithWarningAlertDialog(CoinUtils.getPaymentIntent(btc));
                  } else if (dialogAction == DialogAction.NEGATIVE) {
                      startPaymentIntentWithWarningAlertDialog(CoinUtils.getPaymentIntent(pp));
                  } else if (dialogAction == DialogAction.NEUTRAL) {
                      dismiss();
                  }
              }
          };
          return builder
                  .positiveText(btc.getNameResource())
                  .negativeText(pp.getNameResource())
                  .onPositive(callback)
                  .onNegative(callback)
                  .onNeutral(callback)
                  .autoDismiss(false)
                  .build();
      }
  
      @Override
      public void onStart() {
          super.onStart();
          mCheckout.createPurchaseFlow(mPurchaseListener);
          reloadInventory();
      }
  
      @Override
      public void onStop() {
          mCheckout.destroyPurchaseFlow();
          super.onStop();
      }
  
      /**
       * Shows a warning alert dialog to note, that those methods
       * may suck hard and nobody will care about it.<br/>
       * Starts an intent if user is agree with it.
       */
      private void startPaymentIntentWithWarningAlertDialog(final Intent intent) {
          new MaterialDialog.Builder(getActivity())
                  .content(R.string.donate_alert_no_responsibility)
                  .negativeText(android.R.string.cancel)
                  .positiveText(android.R.string.ok)
                  .onPositive(new MaterialDialog.SingleButtonCallback() {
                      @Override
                      public void onClick(@NonNull MaterialDialog materialDialog,
                                          @NonNull DialogAction dialogAction) {
                          try {
                              startActivity(intent);
                              dismiss(); // Dismiss main fragment
                          } catch (ActivityNotFoundException e) { /* hell no */ }
                      }
                  })
                  .build()
                  .show();
      }
  
      private void refreshUi(int visibility) {
          ViewUtils.setVisible(mProgressBar, visibility == SCREEN_LOADING);
          ViewUtils.setVisible(mEmptyView, visibility == SCREEN_EMPTY_VIEW);
      }
  
      private void reloadInventory() {
          // Set `loading` state.
          refreshUi(SCREEN_LOADING);
          // Reload the inventory.
          mInventory
                  .load()
                  .whenLoaded(mInventoryLoadedListener);
      }
  
      private void purchase(@NonNull final Sku sku) {
          if (DEBUG) Log.d(TAG, "Purchasing " + sku.toString() + "...");
          mCheckout.whenReady(new Checkout.ListenerAdapter() {
              @Override
              public void onReady(@NonNull BillingRequests requests) {
                  requests.purchase(sku, null, mCheckout.getPurchaseFlow());
              }
          });
      }
  
      /**
       * @author Artem Chepurnoy
       */
      private class InventoryLoadedListener implements Inventory.Listener {
  
          @Override
          public void onLoaded(@NonNull Inventory.Products products) {
              final Inventory.Product product = products.get(ProductTypes.IN_APP);
              mAdapter.setNotifyOnChange(false);
              mAdapter.clear();
  
              if (product.supported) {
                  for (Sku sku : product.getSkus()) {
                      final Purchase purchase = product.getPurchaseInState(sku, Purchase.State.PURCHASED);
                      final SkuUi skuUi = new MySkuUi(sku, purchase != null);
                      mAdapter.add(skuUi);
                  }
  
                  // Sort items by prices.
                  mAdapter.sort(new Comparator<SkuUi>() {
                      @Override
                      public int compare(@NonNull SkuUi l, @NonNull SkuUi r) {
                          return (int) (l.sku.detailedPrice.amount - r.sku.detailedPrice.amount);
                      }
                  });
  
                  // Show the inventory.
                  refreshUi(SCREEN_INVENTORY);
              } else refreshUi(SCREEN_EMPTY_VIEW);
  
              mAdapter.notifyDataSetChanged();
          }
      }
  
      /**
       * @author Artem Chepurnoy
       */
      private abstract class BaseRequestListener<T> implements RequestListener<T> {
  
          @Override
          public void onError(int response, @NonNull Exception e) {
              ToastUtils.showShort(getActivity(), e.getLocalizedMessage());
          }
      }
  
      /**
       * @author Artem Chepurnoy
       */
      private final class PurchaseListener extends BaseRequestListener<Purchase> {
  
          @Override
          public void onSuccess(@NonNull Purchase purchase) {
              onPurchased(false);
          }
  
          @Override
          public void onError(int response, @NonNull Exception e) {
              switch (response) {
                  case ResponseCodes.ITEM_ALREADY_OWNED:
                      onPurchased(true);
                      break;
                  default:
                      super.onError(response, e);
              }
          }
  
          private void onPurchased(boolean alreadyOwned) {
              ToastUtils.showLong(getActivity(), R.string.donate_thanks);
  
              if (alreadyOwned) {
                  // Nothing has changed, so we don't need
                  // to reload the inventory.
                  return;
              }
  
              reloadInventory();
          }
      }
  
      /**
       * @author Artem Chepurnoy
       */
      private static final class SkusAdapter extends BetterArrayAdapter<SkuUi> {
  
          /**
           * @author Artem Chepurnoy
           */
          private static final class ViewHolder extends BetterArrayAdapter.ViewHolder {
  
              @NonNull
              private final android.widget.TextView description;
  
              @NonNull
              private final android.widget.TextView price;
  
              @NonNull
              private final android.widget.TextView currency;
  
              @NonNull
              private final ImageView done;
  
              public ViewHolder(@NonNull View view) {
                  super(view);
                  description = (android.widget.TextView) view.findViewById(R.id.description);
                  View layout = view.findViewById(R.id.cost);
                  price = (android.widget.TextView) layout.findViewById(R.id.price);
                  currency = (android.widget.TextView) layout.findViewById(R.id.currency);
                  done = (ImageView) layout.findViewById(R.id.done);
              }
  
          }
  
          public SkusAdapter(@NonNull Context context, @LayoutRes int layoutRes) {
              super(context, layoutRes);
          }
  
          @NonNull
          @Override
          public BetterArrayAdapter.ViewHolder onCreateViewHolder(@NonNull View view) {
              return new ViewHolder(view);
          }
  
          @Override
          public void onBindViewHolder(@NonNull BetterArrayAdapter.ViewHolder viewHolder, int i) {
              bindItem((ViewHolder) viewHolder, getItem(i));
          }
  
          private static void bindItem(@NonNull ViewHolder holder, @NonNull SkuUi skuUi) {
              RippleUtils.makeFor(holder.view, true);
              holder.description.setText(skuUi.getDescription());
  
              int visibility;
              if (skuUi.isPurchased()) {
                  visibility = View.GONE;
                  holder.done.setVisibility(View.VISIBLE);
              } else {
                  visibility = View.VISIBLE;
                  holder.price.setText(skuUi.getPriceAmount());
                  holder.currency.setText(skuUi.getPriceCurrency());
                  holder.done.setVisibility(View.GONE);
              }
  
              holder.price.setVisibility(visibility);
              holder.currency.setVisibility(visibility);
          }
      }
  
      /**
       * @author Artem Chepurnoy
       */
      private static class MySkuUi extends SkuUi {
  
          public MySkuUi(@NonNull Sku sku, boolean isPurchased) {
              super(sku, isPurchased);
          }
  
          @NonNull
          @Override
          protected String onCreateDescription(@NonNull Sku sku) {
              /*
               * Those are highly app specific and should probably be
               * moved.
               */
              String prefix = "donation_";
              if (sku.id.startsWith(prefix)) {
                  int[] data = new int[]{
                          1, R.string.donation_1,
                          4, R.string.donation_4,
                          10, R.string.donation_10,
                          20, R.string.donation_20,
                          50, R.string.donation_50,
                          99, R.string.donation_99,
                  };
  
                  int price = Integer.parseInt(sku.id.substring(prefix.length()));
                  for (int i = 0; i < data.length; i += 2) {
                      if (price == data[i]) {
                          Context context = AppHeap.getContext();
                          return context.getString(data[i + 1]);
                      }
                  }
              }
  
              Log.wtf(TAG, "Alien sku found!");
              return "Alien sku found!";
          }
      }
  }
  