/*
 * 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.afwsamples.testdpc.common.keyvaluepair;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.afwsamples.testdpc.R;
import com.afwsamples.testdpc.common.StringArrayTypeInputAdapter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * A dialog asking user to input key value pair. See {@link DialogType} for supported types. Result
 * will be returned through onActivityResult.
 */
public class KeyValuePairDialogFragment extends DialogFragment {
  static final String ARG_DIALOG_TYPE = "dialog_type";
  static final String ARG_CAN_EDIT_KEY = "can_edit_key";
  static final String ARG_KEY = "key";
  static final String ARG_INITIAL_VALUE = "initial_value";
  static final String ARG_SUPPORTED_TYPE = "supported_type";
  static final String ARG_APP_NAME = "app_name";

  public static final String RESULT_KEY = "key";
  public static final String RESULT_VALUE = "value";
  public static final String RESULT_TYPE = "type";

  private EditText mKeyView;
  private Switch mBoolView;
  private EditText mIntView;
  private EditText mStringView;
  private View mStringArrayContainer;
  private RecyclerView mStringArrayList;
  private Button mConfigureBundle;
  private Button mConfigureBundleArray;
  private View[] mValueViews;
  private Spinner mTypeSpinner;

  private StringArrayTypeInputAdapter mStringArrayAdapter;
  private Set<Integer> mSupportedTypes;
  private int mDialogType;
  private String mAppName;

  public interface DialogType {
    int BOOL_TYPE = 0;
    int INT_TYPE = 1;
    int STRING_TYPE = 2;
    int STRING_ARRAY_TYPE = 3;
    int BUNDLE_TYPE = 4;
    int BUNDLE_ARRAY_TYPE = 5;
  }

  private static final String[] TYPE_DISPLAY_STRING_ARRAY =
      new String[] {"Boolean", "Integer", "String", "String[]", "Bundle", "Bundle[]"};

  private final BundleButtonOnClickListener bundleButtonOnClickListener =
      new BundleButtonOnClickListener();
  private final BundleArrayButtonOnClickListener bundleArrayButtonOnClickListener =
      new BundleArrayButtonOnClickListener();

  public static KeyValuePairDialogFragment newInstance(
      int dialogType,
      boolean canEditKey,
      String key,
      Object value,
      int[] supportedType,
      String appName) {
    KeyValuePairDialogFragment fragment = new KeyValuePairDialogFragment();
    Bundle argument = new Bundle();
    argument.putInt(ARG_DIALOG_TYPE, dialogType);
    argument.putBoolean(ARG_CAN_EDIT_KEY, canEditKey);
    argument.putString(ARG_KEY, key);
    if (value != null) {
      switch (dialogType) {
        case DialogType.BOOL_TYPE:
          argument.putBoolean(ARG_INITIAL_VALUE, (boolean) value);
          break;
        case DialogType.INT_TYPE:
          argument.putInt(ARG_INITIAL_VALUE, (int) value);
          break;
        case DialogType.STRING_TYPE:
          argument.putString(ARG_INITIAL_VALUE, (String) value);
          break;
        case DialogType.STRING_ARRAY_TYPE:
          argument.putStringArray(ARG_INITIAL_VALUE, (String[]) value);
          break;
        case DialogType.BUNDLE_TYPE:
          argument.putBundle(ARG_INITIAL_VALUE, (Bundle) value);
          break;
        case DialogType.BUNDLE_ARRAY_TYPE:
          argument.putParcelableArray(ARG_INITIAL_VALUE, (Bundle[]) value);
          break;
      }
    }
    argument.putIntArray(ARG_SUPPORTED_TYPE, supportedType);
    argument.putString(ARG_APP_NAME, appName);
    fragment.setArguments(argument);
    return fragment;
  }

  @Override
  public Dialog onCreateDialog(Bundle savedInstanceState) {
    LayoutInflater inflater = LayoutInflater.from(getActivity());
    View rootView = inflater.inflate(R.layout.basic_key_value_pair, null);
    mKeyView = (EditText) rootView.findViewById(R.id.key);
    mBoolView = (Switch) rootView.findViewById(R.id.value_bool);
    mIntView = (EditText) rootView.findViewById(R.id.value_int);
    mStringView = (EditText) rootView.findViewById(R.id.value_string);
    mStringArrayContainer = rootView.findViewById(R.id.value_str_array_container);
    mStringArrayList = (RecyclerView) rootView.findViewById(R.id.value_str_array_list);
    mConfigureBundle = (Button) rootView.findViewById(R.id.configure_bundle);
    mConfigureBundleArray = (Button) rootView.findViewById(R.id.configure_bundle_array);
    // The order is based on TYPE_DISPLAY_STRING_ARRAY.
    mValueViews =
        new View[] {
          mBoolView,
          mIntView,
          mStringView,
          mStringArrayContainer,
          mConfigureBundle,
          mConfigureBundleArray
        };
    mTypeSpinner = (Spinner) rootView.findViewById(R.id.type_spinner);
    mConfigureBundle.setOnClickListener(bundleButtonOnClickListener);
    mConfigureBundleArray.setOnClickListener(bundleArrayButtonOnClickListener);

    configureStringUi();
    configureTypeSpinner();
    configureUsingArguments();
    return createAlertDialog(rootView);
  }

  private AlertDialog createAlertDialog(View view) {
    final AlertDialog dialog =
        new AlertDialog.Builder(getActivity())
            .setView(view)
            .setCancelable(true)
            .setPositiveButton(R.string.restriction_save_label, null)
            .setNegativeButton(R.string.restriction_cancel_label, null)
            .create();
    dialog.setOnShowListener(
        new DialogInterface.OnShowListener() {
          @Override
          public void onShow(DialogInterface dialogInterface) {
            dialog
                .getButton(AlertDialog.BUTTON_POSITIVE)
                .setOnClickListener(
                    new OnClickListener() {
                      @Override
                      public void onClick(View view) {
                        String key = mKeyView.getText().toString();
                        if (TextUtils.isEmpty(key)) {
                          showToast(R.string.key_empty_error);
                          return;
                        }
                        Intent result = new Intent();
                        result.putExtra(RESULT_TYPE, mDialogType);
                        result.putExtra(RESULT_KEY, key);
                        boolean valid = putValueFromUiToResultIntent(result);
                        if (valid) {
                          getTargetFragment()
                              .onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, result);
                          dialog.dismiss();
                        } else {
                          showToast(R.string.value_not_valid);
                        }
                      }
                    });
          }
        });
    return dialog;
  }

  private boolean putValueFromUiToResultIntent(Intent result) {
    switch (mDialogType) {
      case DialogType.BOOL_TYPE:
        result.putExtra(RESULT_VALUE, mBoolView.isChecked());
        return true;
      case DialogType.INT_TYPE:
        try {
          result.putExtra(RESULT_VALUE, Integer.valueOf(mIntView.getText().toString()));
        } catch (NumberFormatException ex) {
          return false;
        }
        return true;
      case DialogType.STRING_TYPE:
        result.putExtra(RESULT_VALUE, mStringView.getText().toString());
        return true;
      case DialogType.STRING_ARRAY_TYPE:
        result.putExtra(RESULT_VALUE, mStringArrayAdapter.getStringList().toArray(new String[0]));
        return true;
      case DialogType.BUNDLE_TYPE:
        Bundle initialBundle = getArguments().getBundle(ARG_INITIAL_VALUE);
        if (initialBundle == null) {
          initialBundle = new Bundle();
        }
        result.putExtra(RESULT_VALUE, initialBundle);
        return true;
      case DialogType.BUNDLE_ARRAY_TYPE:
        Parcelable[] initialBundleArray = getArguments().getParcelableArray(ARG_INITIAL_VALUE);
        if (initialBundleArray == null) {
          initialBundleArray = new Bundle[0];
        }
        result.putExtra(RESULT_VALUE, initialBundleArray);
        return true;
    }
    return false;
  }

  private void configureTypeSpinner() {
    ArrayAdapter<String> typeArrayAdapter =
        new ArrayAdapter<String>(
            getActivity(), android.R.layout.simple_spinner_item, TYPE_DISPLAY_STRING_ARRAY) {
          @Override
          public boolean isEnabled(int position) {
            return mSupportedTypes.contains(position);
          }

          @Override
          public View getDropDownView(int position, View convertView, ViewGroup parent) {
            TextView textView = (TextView) super.getDropDownView(position, convertView, parent);
            if (isEnabled(position)) {
              textView.setTextColor(Color.BLACK);
            } else {
              textView.setTextColor(Color.LTGRAY);
            }
            return textView;
          }
        };
    typeArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    mTypeSpinner.setAdapter(typeArrayAdapter);
    mTypeSpinner.setOnItemSelectedListener(
        new AdapterView.OnItemSelectedListener() {
          @Override
          public void onItemSelected(AdapterView<?> adapterView, View view, int pos, long l) {
            setDialogType(pos);
          }

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

  private void configureUsingArguments() {
    Bundle arguments = getArguments();
    configureKeyView(arguments.getBoolean(ARG_CAN_EDIT_KEY), arguments.getString(ARG_KEY));
    setSupportedType(arguments.getIntArray(ARG_SUPPORTED_TYPE));
    setDialogType(arguments.getInt(ARG_DIALOG_TYPE));
    populateInitialValue(arguments.get(ARG_INITIAL_VALUE));
    mAppName = arguments.getString(ARG_APP_NAME);
  }

  private void configureKeyView(boolean canEditKeyView, String key) {
    mKeyView.setEnabled(canEditKeyView);
    mKeyView.setText(key);
  }

  private void setSupportedType(int[] supportedTypes) {
    Set<Integer> supportedTypeSet = new HashSet<>();
    for (Integer supportType : supportedTypes) {
      supportedTypeSet.add(supportType);
    }
    mSupportedTypes = supportedTypeSet;
  }

  private void configureStringUi() {
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
    linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    mStringArrayList.setLayoutManager(linearLayoutManager);
    mStringArrayAdapter = new StringArrayTypeInputAdapter();
    mStringArrayList.setAdapter(mStringArrayAdapter);
    mStringArrayContainer
        .findViewById(R.id.add_new_row)
        .setOnClickListener(
            new OnClickListener() {
              @Override
              public void onClick(View view) {
                mStringArrayAdapter.getStringList().add("");
                mStringArrayAdapter.notifyItemInserted(mStringArrayAdapter.getItemCount());
              }
            });
  }

  private void setDialogType(int type) {
    mDialogType = type;
    mTypeSpinner.setSelection(type);
    for (int i = 0; i < mValueViews.length; i++) {
      if (mDialogType == i) {
        mValueViews[i].setVisibility(View.VISIBLE);
      } else {
        mValueViews[i].setVisibility(View.GONE);
      }
    }
  }

  private void populateInitialValue(final Object initialValue) {
    if (initialValue == null) {
      return;
    }
    switch (mDialogType) {
      case DialogType.BOOL_TYPE:
        mBoolView.setChecked((boolean) initialValue);
        break;
      case DialogType.INT_TYPE:
        mIntView.setText(String.valueOf((int) initialValue));
        break;
      case DialogType.STRING_TYPE:
        mStringView.setText((String) initialValue);
        break;
      case DialogType.STRING_ARRAY_TYPE:
        mStringArrayAdapter.setStringList(new ArrayList<>(Arrays.asList((String[]) initialValue)));
        break;
      case DialogType.BUNDLE_TYPE:
        bundleButtonOnClickListener.setBundle((Bundle) initialValue);
        break;
      case DialogType.BUNDLE_ARRAY_TYPE:
        bundleArrayButtonOnClickListener.setBundleArray((Bundle[]) initialValue);
        break;
    }
  }

  private void showFragmentForFurtherInput(Fragment fragment) {
    getFragmentManager()
        .beginTransaction()
        .addToBackStack(KeyValuePairDialogFragment.class.getName())
        .hide(getTargetFragment())
        .add(R.id.container, fragment)
        .commit();
    fragment.setTargetFragment(getTargetFragment(), getTargetRequestCode());
    KeyValuePairDialogFragment.this.dismiss();
  }

  private void showToast(@StringRes int stringResId) {
    Toast.makeText(getActivity(), stringResId, Toast.LENGTH_LONG).show();
  }

  private class BundleButtonOnClickListener implements OnClickListener {
    private Bundle mBundle;

    public void setBundle(Bundle bundle) {
      mBundle = bundle;
    }

    @Override
    public void onClick(View view) {
      String key = mKeyView.getText().toString();
      if (TextUtils.isEmpty(key)) {
        showToast(R.string.key_empty_error);
        return;
      }
      Fragment fragment = KeyValueBundleFragment.newInstance(key, mBundle, mAppName);
      showFragmentForFurtherInput(fragment);
    }
  }

  private class BundleArrayButtonOnClickListener implements OnClickListener {
    private Bundle[] mBundles;

    public void setBundleArray(Bundle[] bundles) {
      mBundles = bundles;
    }

    @Override
    public void onClick(View view) {
      String key = mKeyView.getText().toString();
      if (TextUtils.isEmpty(key)) {
        showToast(R.string.key_empty_error);
        return;
      }
      Fragment fragment = KeyValueBundleArrayFragment.newInstance(key, mBundles, mAppName);
      showFragmentForFurtherInput(fragment);
    }
  }
}
