/*
 * Copyright (C) 2016 Google Inc.
 *
 * 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.googlecode.android_scripting.facade.ui;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.text.method.PasswordTransformationMethod;
import android.widget.EditText;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.json.JSONArray;
import org.json.JSONException;

/**
 * Wrapper class for alert dialog running in separate thread.
 *
 */
class AlertDialogTask extends DialogTask {

  private final String mTitle;
  private final String mMessage;

  private final List<String> mItems;
  private final Set<Integer> mSelectedItems;
  private final Map<String, Object> mResultMap;
  private InputType mInputType;
  private int mEditInputType = 0;

  private String mPositiveButtonText;
  private String mNegativeButtonText;
  private String mNeutralButtonText;

  private EditText mEditText;
  private String mDefaultText;

  private enum InputType {
    DEFAULT, MENU, SINGLE_CHOICE, MULTI_CHOICE, PLAIN_TEXT, PASSWORD;
  }

  public AlertDialogTask(String title, String message) {
    mTitle = title;
    mMessage = message;
    mInputType = InputType.DEFAULT;
    mItems = new ArrayList<String>();
    mSelectedItems = new TreeSet<Integer>();
    mResultMap = new HashMap<String, Object>();
  }

  public void setPositiveButtonText(String text) {
    mPositiveButtonText = text;
  }

  public void setNegativeButtonText(String text) {
    mNegativeButtonText = text;
  }

  public void setNeutralButtonText(String text) {
    mNeutralButtonText = text;
  }

  /**
   * Set list items.
   *
   * @param items
   */
  public void setItems(JSONArray items) {
    mItems.clear();
    for (int i = 0; i < items.length(); i++) {
      try {
        mItems.add(items.getString(i));
      } catch (JSONException e) {
        throw new RuntimeException(e);
      }
    }
    mInputType = InputType.MENU;
  }

  /**
   * Set single choice items.
   *
   * @param items
   *          a list of items as {@link String}s to display
   * @param selected
   *          the index of the item that is selected by default
   */
  public void setSingleChoiceItems(JSONArray items, int selected) {
    setItems(items);
    mSelectedItems.clear();
    mSelectedItems.add(selected);
    mInputType = InputType.SINGLE_CHOICE;
  }

  /**
   * Set multi choice items.
   *
   * @param items
   *          a list of items as {@link String}s to display
   * @param selected
   *          a list of indices for items that should be selected by default
   * @throws JSONException
   */
  public void setMultiChoiceItems(JSONArray items, JSONArray selected) throws JSONException {
    setItems(items);
    mSelectedItems.clear();
    if (selected != null) {
      for (int i = 0; i < selected.length(); i++) {
        mSelectedItems.add(selected.getInt(i));
      }
    }
    mInputType = InputType.MULTI_CHOICE;
  }

  /**
   * Returns the list of selected items.
   */
  public Set<Integer> getSelectedItems() {
    return mSelectedItems;
  }

  public void setTextInput(String defaultText) {
    mDefaultText = defaultText;
    mInputType = InputType.PLAIN_TEXT;
    setEditInputType("text");
  }

  public void setEditInputType(String editInputType) {
    String[] list = editInputType.split("\\|");
    Map<String, Integer> types = ViewInflater.getInputTypes();
    mEditInputType = 0;
    for (String flag : list) {
      Integer v = types.get(flag.trim());
      if (v != null) {
        mEditInputType |= v;
      }
    }
    if (mEditInputType == 0) {
      mEditInputType = android.text.InputType.TYPE_CLASS_TEXT;
    }
  }

  public void setPasswordInput() {
    mInputType = InputType.PASSWORD;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    if (mTitle != null) {
      builder.setTitle(mTitle);
    }
    // Can't display both a message and items. We'll elect to show the items instead.
    if (mMessage != null && mItems.isEmpty()) {
      builder.setMessage(mMessage);
    }
    switch (mInputType) {
    // Add single choice menu items to dialog.
    case SINGLE_CHOICE:
      builder.setSingleChoiceItems(getItemsAsCharSequenceArray(), mSelectedItems.iterator().next(),
          new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int item) {
              mSelectedItems.clear();
              mSelectedItems.add(item);
            }
          });
      break;
    // Add multiple choice items to the dialog.
    case MULTI_CHOICE:
      boolean[] selectedItems = new boolean[mItems.size()];
      for (int i : mSelectedItems) {
        selectedItems[i] = true;
      }
      builder.setMultiChoiceItems(getItemsAsCharSequenceArray(), selectedItems,
          new DialogInterface.OnMultiChoiceClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int item, boolean isChecked) {
              if (isChecked) {
                mSelectedItems.add(item);
              } else {
                mSelectedItems.remove(item);
              }
            }
          });
      break;
    // Add standard, menu-like, items to dialog.
    case MENU:
      builder.setItems(getItemsAsCharSequenceArray(), new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int item) {
          Map<String, Integer> result = new HashMap<String, Integer>();
          result.put("item", item);
          dismissDialog();
          setResult(result);
        }
      });
      break;
    case PLAIN_TEXT:
      mEditText = new EditText(getActivity());
      if (mDefaultText != null) {
        mEditText.setText(mDefaultText);
      }
      mEditText.setInputType(mEditInputType);
      builder.setView(mEditText);
      break;
    case PASSWORD:
      mEditText = new EditText(getActivity());
      mEditText.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
      mEditText.setTransformationMethod(new PasswordTransformationMethod());
      builder.setView(mEditText);
      break;
    default:
      // No input type specified.
    }
    configureButtons(builder, getActivity());
    addOnCancelListener(builder, getActivity());
    mDialog = builder.show();
    mShowLatch.countDown();
  }

  private CharSequence[] getItemsAsCharSequenceArray() {
    return mItems.toArray(new CharSequence[mItems.size()]);
  }

  private Builder addOnCancelListener(final AlertDialog.Builder builder, final Activity activity) {
    return builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
      @Override
      public void onCancel(DialogInterface dialog) {
        mResultMap.put("canceled", true);
        setResult();
      }
    });
  }

  private void configureButtons(final AlertDialog.Builder builder, final Activity activity) {
    DialogInterface.OnClickListener buttonListener = new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog, int which) {
        switch (which) {
        case DialogInterface.BUTTON_POSITIVE:
          mResultMap.put("which", "positive");
          break;
        case DialogInterface.BUTTON_NEGATIVE:
          mResultMap.put("which", "negative");
          break;
        case DialogInterface.BUTTON_NEUTRAL:
          mResultMap.put("which", "neutral");

          break;
        }
        setResult();
      }
    };
    if (mNegativeButtonText != null) {
      builder.setNegativeButton(mNegativeButtonText, buttonListener);
    }
    if (mPositiveButtonText != null) {
      builder.setPositiveButton(mPositiveButtonText, buttonListener);
    }
    if (mNeutralButtonText != null) {
      builder.setNeutralButton(mNeutralButtonText, buttonListener);
    }
  }

  private void setResult() {
    dismissDialog();
    if (mInputType == InputType.PLAIN_TEXT || mInputType == InputType.PASSWORD) {
      mResultMap.put("value", mEditText.getText().toString());
    }
    setResult(mResultMap);
  }

}
