package com.tapjoy.easyapp;

import static com.tapjoy.easyapp.ConfigurationFragment.AUTO_CONNECT_KEY;
import static com.tapjoy.easyapp.ConfigurationFragment.DEFAULT_SDK_KEY;
import static com.tapjoy.easyapp.ConfigurationFragment.PREF_SDK_CURRENT_KEY;

import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.SwitchCompat;
import androidx.fragment.app.Fragment;

import com.tapjoy.TJAwardCurrencyListener;
import com.tapjoy.TJConnectListener;
import com.tapjoy.TJEarnedCurrencyListener;
import com.tapjoy.TJError;
import com.tapjoy.TJGetCurrencyBalanceListener;
import com.tapjoy.TJLogLevel;
import com.tapjoy.TJPlacement;
import com.tapjoy.TJEntryPoint;
import com.tapjoy.TJPlacementListener;
import com.tapjoy.TJSetCurrencyAmountRequiredListener;
import com.tapjoy.TJSetCurrencyBalanceListener;
import com.tapjoy.TJSpendCurrencyListener;
import com.tapjoy.Tapjoy;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;

public class MainFragment extends Fragment implements View.OnClickListener, TJGetCurrencyBalanceListener, TextWatcher {

    public static final String TAG = "TapjoyEasyApp";
    public static final String PREFERENCES = "easyapp";
    private static final String PLACEMENT_NAME_KEY = "placementName";
    private static final String DEBUG_KEY = "debugKey";

    // UI elements
    private String displayText = "";
    private Button getCurrencyBalanceButton;
    private Button buttonRequestPlacement;
    private Button buttonShowPlacement;
    private Button buttonClearPlacementName;
    private EditText placementNameInput;
    private TextView sdkVersion;
    private TextView outputTextView;
    private Button buttonConnect;
    private Button currentButton;
    private Spinner entryPointSpinner;
    private Spinner loggingLevelSpinner;
    private EditText currencyAmountInput;
    private EditText currencyIdInput;
    private EditText currencyRequiredInput;
    private EditText currencyBalanceInput;
    private EditText purchaseAmountInput;
    private EditText purchaseCodeInput;
    private SwitchCompat selfManagedSwitch;
    private LinearLayout selfManagedCurrencyIdLayout;
    private LinearLayout selfManagedCurrencyBalanceLayout;
    private LinearLayout selfManagedCurrencyRequiredLayout;

    // Tapjoy Placements
    private TJPlacement examplePlacement;
    private TJPlacement offerwallPlacement;

    private boolean earnedCurrency = false;
    private boolean autoConnect;
    private String inputCurrencyId;
    private int inputCurrencyBalance;
    private int inputCurrencyAmountRequired;
    private SharedPreferences preferences;
    private final DecimalFormat decimalFormat = new DecimalFormat("#.###");

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.main, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        preferences = requireActivity().getApplicationContext().getSharedPreferences(PREFERENCES, 0);
        Tapjoy.setLoggingLevel(getStoredLoggingLevel());

        setupUI(view);

        //Get latest tapjoySdkKey and autoConnect from cache
        autoConnect = preferences.getBoolean(AUTO_CONNECT_KEY, true);

        //Connect to the SDK if configured so
        if (autoConnect) {
            connectToTapjoy();
            buttonConnect.setVisibility(View.GONE);
        } else {
            updateTextInUI("Click Connect to Start");
        }

        currencyIdInput.addTextChangedListener(this);
        currencyBalanceInput.addTextChangedListener(this);
        currencyRequiredInput.addTextChangedListener(this);
    }

    /**
     * Attempts to connect to Tapjoy
     */
    private void connectToTapjoy() {
        String tapjoySDKKey = preferences.getString(PREF_SDK_CURRENT_KEY, DEFAULT_SDK_KEY);

        // OPTIONAL: For custom startup flags.
        Hashtable<String, Object> connectFlags = new Hashtable<String, Object>();
        // connectFlags.put(TapjoyConnectFlag.TJC_OPTION_LOGGING_LEVEL, TJLogLevel.DEBUG);

        // If you are not using Tapjoy Managed currency, you would set your own user ID here.
        // connectFlags.put(TapjoyConnectFlag.USER_ID, "A_UNIQUE_USER_ID");

        // Connect with the Tapjoy server.  Call this when the application first starts.
        // REPLACE THE SDK KEY WITH YOUR TAPJOY SDK Key.
        // String tapjoySDKKey = "u6SfEbh_TA-WMiGqgQ3W8QECyiQIURFEeKm0zbOggubusy-o5ZfXp33sTXaD";

        // NOTE: This is the only step required if you're an advertiser.
        Tapjoy.connect(requireActivity(), tapjoySDKKey, connectFlags, new TJConnectListener() {
            @Override
            public void onConnectSuccess() {
                MainFragment.this.onConnectSuccess();
                //Might be null in auto connect
                if (currentButton != null) {
                    setButtonEnabledInUI(currentButton, true);
                }
                buttonConnect.setVisibility(View.GONE);
                loggingLevelSpinner.setSelection(getLoggingLevel());
            }

            @Override
            public void onConnectFailure(int code, String message) {
                if (!autoConnect) buttonConnect.setEnabled(true);
                updateTextInUI("Tapjoy connect failed: " + message + " Error code: " + code);
            }

            @Override
            public void onConnectWarning(int code, String message) {
                updateTextInUI("Tapjoy connect succeeded with warning code: " + code + " " + message);
            }
        });
    }

    /**
     * Handles a successful connect to Tapjoy.
     * and sets up Tapjoy listeners
     */
    public void onConnectSuccess() {
        updateTextInUI("Tapjoy SDK connected");

        // NOTE:  The get/spend/award currency methods will only work if your virtual currency
        // is managed by Tapjoy.
        //
        // For NON-MANAGED virtual currency, Tapjoy.setUserID(String,TJSetUserIDListener)
        // must be called after requestTapjoyConnect.

        // Setup listener for Tapjoy currency callbacks
        Tapjoy.setEarnedCurrencyListener(new TJEarnedCurrencyListener() {
            @Override
            public void onEarnedCurrency(String currencyName, int amount) {
                earnedCurrency = true;
                updateTextInUI("You've just earned " + amount + " " + currencyName);
                showPopupMessage("You've just earned " + amount + " " + currencyName);
            }
        });
    }

    /**
     * Handles button clicks
     */
    public void onClick(View view) {
        int id = view.getId();
        if (view instanceof Button) {
            currentButton = ((Button) view);
            if (id == R.id.buttonConnect) {// Disable button
                // Disable button
                currentButton.setEnabled(false);
                // Connect to the SDK
                connectToTapjoy();
            } else if (id == R.id.buttonGetCurrency) {// Disable button
                currentButton.setEnabled(false);
                // Retrieve virtual currency balance from the server
                Tapjoy.getCurrencyBalance(MainFragment.this);
            } else if (id == R.id.buttonSpendCurrency) {// Disable button
                currentButton.setEnabled(false);
                // Spend Currency
                int amount = getCurrencyAmount();
                callSpendCurrency(amount);
            } else if (id == R.id.buttonAwardCurrency) {// Disable button
                currentButton.setEnabled(false);
                // Award Currency
                int amount = getCurrencyAmount();
                callAwardCurrency(amount);
            } else if (id == R.id.buttonShowOffers) {// Disable button
                currentButton.setEnabled(false);
                // Show Offers Placement
                callShowOffers();
            } else if (id == R.id.buttonRequestPlacement) {// Disable button
                currentButton.setEnabled(false);
                // Request Custom Placement
                requestPlacement();
            } else if (id == R.id.buttonShowPlacement) {// Disable button
                currentButton.setEnabled(false);
                // Show Custom Placement
                examplePlacement.showContent();
            } else if (id == R.id.buttonClearPlacementName) {
                placementNameInput.setText("");
            } else if (id == R.id.buttonPurchase) {
                double price = 0;
                String currencyInput = purchaseCodeInput.getText().toString();
                String purchaseInput = purchaseAmountInput.getText().toString();
                try {
                    price = Double.parseDouble(purchaseInput);
                } catch (NumberFormatException ignored) {}
                Tapjoy.trackPurchase(currencyInput, price);
                updateTextInUI("Sent track purchase: " + decimalFormat.format(price) + " " + currencyInput);
            } else if (id == R.id.selfManagedSwitch) {
                if (selfManagedSwitch.isChecked()){
                    selfManagedCurrencyIdLayout.setVisibility(View.VISIBLE);
                    selfManagedCurrencyBalanceLayout.setVisibility(View.VISIBLE);
                    selfManagedCurrencyRequiredLayout.setVisibility(View.VISIBLE);
                } else {
                    selfManagedCurrencyIdLayout.setVisibility(View.GONE);
                    selfManagedCurrencyBalanceLayout.setVisibility(View.GONE);
                    selfManagedCurrencyRequiredLayout.setVisibility(View.GONE);
                }
            }
        }
    }

    private int getCurrencyAmount() {
        int amount = -1;
        try {
            amount = Integer.parseInt(currencyAmountInput.getText().toString());
        } catch (NumberFormatException ignored) {}
        return amount;
    }

    /**
     * Constructs TJPlacement {@link MainFragment#examplePlacement} and
     * requests placement content from Tapjoy. The name of the placement is
     * pulled from {@link MainFragment#placementNameInput}.
     */
    private void requestPlacement() {
        // Grab placement name from input field
        String placementName = placementNameInput.getText().toString();

        // Construct TJPlacement
        examplePlacement = Tapjoy.getPlacement(placementName, new TJPlacementListener() {
            @Override
            public void onRequestSuccess(TJPlacement placement) {
                updateTextInUI("onRequestSuccess for placement " + placement.getName());

                if (!placement.isContentAvailable()) {
                    updateTextInUI("No content available for placement " + placement.getName());
                }

                setButtonEnabledInUI(buttonRequestPlacement, true);
            }

            @Override
            public void onRequestFailure(TJPlacement placement, TJError error) {
                setButtonEnabledInUI(buttonRequestPlacement, true);
                updateTextInUI("onRequestFailure for placement " + placement.getName() + " -- error: " + error.message);
            }

            @Override
            public void onContentReady(TJPlacement placement) {
                updateTextInUI("onContentReady for placement " + placement.getName());
                setButtonEnabledInUI(buttonShowPlacement, true);
            }

            @Override
            public void onContentShow(TJPlacement placement) {
                Log.i(TAG, "onContentShow for placement " + placement.getName());
                updateTextInUI("onContentShow for placement " + placement.getName());
            }

            @Override
            public void onContentDismiss(TJPlacement placement) {
                setButtonEnabledInUI(buttonRequestPlacement, true);
                setButtonEnabledInUI(buttonShowPlacement, false);

                Log.i(TAG, "onContentDismiss for placement " + placement.getName());
                updateTextInUI("onContentDismiss for placement " + placement.getName());
                // Best Practice: We recommend calling getCurrencyBalance as often as possible so the user's balance is always up-to-date.
                Tapjoy.getCurrencyBalance(MainFragment.this);
            }
        });

        if (selfManagedSwitch.isChecked()) {
            examplePlacement.setCurrencyBalance(inputCurrencyId, inputCurrencyBalance, new TJSetCurrencyBalanceListener() {
                @Override
                public void onSetCurrencyBalanceSuccess() {
                    Log.i(TAG, "Currency balance is set");
                }

                @Override
                public void onSetCurrencyBalanceFailure(int code, String error) {
                    Log.i(TAG, "Currency balance failed: " + error);
                }
            });
            examplePlacement.setCurrencyAmountRequired(inputCurrencyId, inputCurrencyAmountRequired, new TJSetCurrencyAmountRequiredListener() {
                @Override
                public void onSetCurrencyAmountRequiredSuccess() {
                    Log.i(TAG, "Currency required is set");
                }

                @Override
                public void onSetCurrencyAmountRequiredFailure(int code, String error) {
                    Log.i(TAG, "Currency required failed: " + error);
                }
            });
        }

        examplePlacement.setEntryPoint(TJEntryPoint.values()[entryPointSpinner.getSelectedItemPosition()]);
        updateTextInUI("Requesting placement content");
        examplePlacement.requestContent();
    }

    private void updateCurrencyDetails() {
        inputCurrencyId = currencyIdInput.getText().toString();
        String balance = currencyBalanceInput.getText().toString();
        String amountRequired = currencyRequiredInput.getText().toString();

        try {
            inputCurrencyBalance = Integer.parseInt(balance);
        } catch (NumberFormatException ex) {
            inputCurrencyBalance = -1;
        }
        try {
            inputCurrencyAmountRequired = Integer.parseInt(amountRequired);
        } catch (NumberFormatException ex) {
            inputCurrencyAmountRequired = -1;
        }
    }

    private void callShowOffers() {
        // Construct TJPlacement to show Offers web view from where users can download the latest offers for virtual currency.
        offerwallPlacement = Tapjoy.getPlacement("offerwall_unit", new TJPlacementListener() {
            @Override
            public void onRequestSuccess(TJPlacement placement) {
                updateTextInUI("onRequestSuccess for placement " + placement.getName());

                if (!placement.isContentAvailable()) {
                    updateTextInUI("No Offerwall content available");
                }

                setButtonEnabledInUI(currentButton, true);
            }

            @Override
            public void onRequestFailure(TJPlacement placement, TJError error) {
                setButtonEnabledInUI(currentButton, true);
                updateTextInUI("Offerwall error: " + error.message);
            }

            @Override
            public void onContentReady(TJPlacement placement) {
                Log.i(TAG, "onContentReady for placement " + placement.getName());

                updateTextInUI("Offerwall request success");
                placement.showContent();
            }

            @Override
            public void onContentShow(TJPlacement placement) {
                Log.i(TAG, "onContentShow for placement " + placement.getName());
                updateTextInUI("Offerwall Content Show");
            }

            @Override
            public void onContentDismiss(TJPlacement placement) {
                Log.i(TAG, "onContentDismiss for placement " + placement.getName());
                updateTextInUI("Offerwall Content Dismiss");
            }
        });

        offerwallPlacement.requestContent();
    }

    /**
     * Wrapper method to call {@link Tapjoy#spendCurrency(int, TJSpendCurrencyListener }
     */
    private void callSpendCurrency(int amount) {
        // Spend virtual currency
        Tapjoy.spendCurrency(amount, new TJSpendCurrencyListener() {
            @Override
            public void onSpendCurrencyResponse(String currencyName, int balance) {
                updateTextInUI(currencyName + ": " + balance);
                setButtonEnabledInUI(currentButton, true);
            }

            @Override
            public void onSpendCurrencyResponseFailure(String error) {
                updateTextInUI("spendCurrency error: " + error);
                setButtonEnabledInUI(currentButton, true);
            }
        });
    }

    /**
     * Wrapper method to call {@link Tapjoy#awardCurrency(int, TJAwardCurrencyListener }
     */
    private void callAwardCurrency(int amount) {
        // Award virtual currency
        Tapjoy.awardCurrency(amount, new TJAwardCurrencyListener() {
            @Override
            public void onAwardCurrencyResponseFailure(String error) {
                updateTextInUI("awardCurrency error: " + error);
                setButtonEnabledInUI(currentButton, true);
            }

            @Override
            public void onAwardCurrencyResponse(String currencyName, int balance) {
                updateTextInUI(currencyName + ": " + balance);
                setButtonEnabledInUI(currentButton, true);
            }
        });
    }

    //================================================================================
    // TapjoyListener Methods
    //================================================================================
    @Override
    public void onGetCurrencyBalanceResponse(String currencyName, int balance) {
        Log.i(TAG, "currencyName: " + currencyName);
        Log.i(TAG, "balance: " + balance);

        if (earnedCurrency) {
            updateTextInUI(displayText + "\n" + currencyName + ": " + balance);
            earnedCurrency = false;
        } else {
            updateTextInUI(currencyName + ": " + balance);
        }
        setButtonEnabledInUI(getCurrencyBalanceButton, true);
    }

    @Override
    public void onGetCurrencyBalanceResponseFailure(String error) {
        updateTextInUI("getCurrencyBalance error: " + error);
        setButtonEnabledInUI(getCurrencyBalanceButton, true);
    }

    /**
     * Grabs references to UI elements and sets up listeners
     */
    private void setupUI(View currentView) {

        // This button connects to the SDK
        buttonConnect = currentView.findViewById(R.id.buttonConnect);
        buttonConnect.setOnClickListener(this);

        // This button shows the offers page
        currentView.findViewById(R.id.buttonShowOffers).setOnClickListener(this);

        // This button retrieves the virtual currency info from the Tapjoy server
        getCurrencyBalanceButton = currentView.findViewById(R.id.buttonGetCurrency);
        getCurrencyBalanceButton.setOnClickListener(this);

        placementNameInput = currentView.findViewById(R.id.inputPlacementName);
        placementNameInput.setText(preferences.getString(PLACEMENT_NAME_KEY, "offerwall_unit"));


        placementNameInput.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                preferences.edit().putString(PLACEMENT_NAME_KEY, charSequence.toString()).apply();
            }

            @Override
            public void afterTextChanged(Editable editable) {
            }
        });

        // This spends virtual currency for this device
        currentView.findViewById(R.id.buttonSpendCurrency).setOnClickListener(this);

        // This awards virtual currency for this device
        currentView.findViewById(R.id.buttonAwardCurrency).setOnClickListener(this);

        // Placements
        buttonRequestPlacement = currentView.findViewById(R.id.buttonRequestPlacement);
        buttonRequestPlacement.setOnClickListener(this);

        buttonShowPlacement = currentView.findViewById(R.id.buttonShowPlacement);
        buttonShowPlacement.setOnClickListener(this);

        buttonClearPlacementName = currentView.findViewById(R.id.buttonClearPlacementName);
        buttonClearPlacementName.setOnClickListener(this);

        // Placement Entry Point
        entryPointSpinner = currentView.findViewById(R.id.entryPointSpinner);
        ArrayList<String> entryPointArrayList = new ArrayList<>();
        entryPointArrayList.add("Not Set");
        for (TJEntryPoint entryPoint : TJEntryPoint.values()) {
            if (entryPoint.getValue() != null) {
                entryPointArrayList.add(entryPoint.getValue());
            }
        }
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this.getContext(), android.R.layout.simple_spinner_item, entryPointArrayList.toArray(new String[0]));
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        entryPointSpinner.setAdapter(adapter);

        // Logging Level
        loggingLevelSpinner = currentView.findViewById(R.id.loggingLevelSpinner);
        List<String> loggingLevels = Arrays.asList("Error","Warning", "Info", "Debug");

        ArrayAdapter<String> loggingAdapter = new ArrayAdapter<>(this.getContext(), android.R.layout.simple_spinner_item, loggingLevels);
        loggingAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        loggingLevelSpinner.setAdapter(loggingAdapter);
        loggingLevelSpinner.setSelected(false);
        loggingLevelSpinner.setSelection(getLoggingLevel(), true);
        loggingLevelSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                setLoggingLevel(preferences, i);
            }

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

        // Purchases
        currentView.findViewById(R.id.buttonPurchase).setOnClickListener(this);

        currencyAmountInput = currentView.findViewById(R.id.inputCurrencyAmount);
        currencyIdInput = currentView.findViewById(R.id.inputCurrencyId);
        currencyRequiredInput = currentView.findViewById(R.id.inputCurrencyRequired);
        currencyBalanceInput = currentView.findViewById(R.id.inputCurrencyBalance);
        purchaseAmountInput = currentView.findViewById(R.id.purchaseAmountInput);
        purchaseCodeInput = currentView.findViewById(R.id.purchaseCodeInput);
        selfManagedSwitch = currentView.findViewById(R.id.selfManagedSwitch);
        selfManagedCurrencyIdLayout = currentView.findViewById(R.id.linearLayoutCurrencyId);
        selfManagedCurrencyBalanceLayout = currentView.findViewById(R.id.linearLayoutCurrencyBalance);
        selfManagedCurrencyRequiredLayout = currentView.findViewById(R.id.linearLayoutCurrencyRequired);
        selfManagedSwitch.setOnClickListener(this);
        outputTextView = currentView.findViewById(R.id.textViewOutput);
        sdkVersion = currentView.findViewById(R.id.sdkVersion);
        String sdkVersionString = "SDK Version:  " + Tapjoy.getVersion();
        sdkVersion.setText(sdkVersionString);
    }

    /**
     * Update the text view on the UI thread
     *
     * @param text text to display in UI
     */
    private void updateTextInUI(final String text) {
        displayText = text;

        requireActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (outputTextView != null) {
                    outputTextView.setText(text);
                }
            }
        });
    }

    /**
     * Re-enable button on the UI thread
     *
     * @param button  button to enable
     * @param enabled whether to enable the button or not
     */
    private void setButtonEnabledInUI(final Button button, final Boolean enabled) {
        requireActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                button.setEnabled(enabled);
            }
        });
    }

    /**
     * Helper function to show a Toast to the user
     *
     * @param text text that you want to display in the Toast
     */
    private void showPopupMessage(final String text) {
        requireActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast toast = Toast.makeText(requireActivity().getApplicationContext(), text, Toast.LENGTH_SHORT);
                //Starting from Android R this method is a no-op when called on text toasts.
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
                    toast.setGravity(Gravity.CENTER, 0, 0);
                }
                toast.show();
            }
        });
    }

    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
    @Override
    public void afterTextChanged(Editable editable) {
        updateCurrencyDetails();
    }

    public void setLoggingLevel(SharedPreferences preferences, int index) {
        Tapjoy.setLoggingLevel(TJLogLevel.valueOf(index));

        // Use the getter to ensure the log level is not overridden by the server.
        int level = getLoggingLevel();
        loggingLevelSpinner.setSelection(level);
        preferences.edit().putInt(DEBUG_KEY, level).apply();
    }

    private TJLogLevel getStoredLoggingLevel() {
        return TJLogLevel.valueOf(preferences.getInt(DEBUG_KEY, 0));
    }

    private int getLoggingLevel() {
        return Tapjoy.getLoggingLevel().getValue();
    }
}
