/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko.prompts;

import org.mozilla.gecko.AllCapsTextView;
import org.mozilla.gecko.util.GeckoEventResponder;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.widget.DateTimePicker;

import org.json.JSONArray;
import org.json.JSONObject;

import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Build;
import android.text.format.DateFormat;
import android.text.Html;
import android.text.InputType;
import android.text.TextUtils;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CheckedTextView;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

public class PromptInput {
    protected final String mLabel;
    protected final String mType;
    protected final String mId;
    protected final String mValue;
    protected View mView;
    public static final String LOGTAG = "GeckoPromptInput";

    public static class EditInput extends PromptInput {
        protected final String mHint;
        protected final boolean mAutofocus;
        public static final String INPUT_TYPE = "textbox";

        public EditInput(JSONObject object) {
            super(object);
            mHint = object.optString("hint");
            mAutofocus = object.optBoolean("autofocus");
        }

        public View getView(final Context context) throws UnsupportedOperationException {
            EditText input = new EditText(context);
            input.setInputType(InputType.TYPE_CLASS_TEXT);
            input.setText(mValue);

            if (!TextUtils.isEmpty(mHint)) {
                input.setHint(mHint);
            }

            if (mAutofocus) {
                input.setOnFocusChangeListener(new View.OnFocusChangeListener() {
                    @Override
                    public void onFocusChange(View v, boolean hasFocus) {
                        if (hasFocus) {
                            ((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(v, 0);
                        }
                    }
                });
                input.requestFocus();
            }

            mView = (View)input;
            return mView;
        }

        public String getValue() {
            EditText edit = (EditText)mView;
            return edit.getText().toString();
        }
    }

    public static class NumberInput extends EditInput {
        public static final String INPUT_TYPE = "number";
        public NumberInput(JSONObject obj) {
            super(obj);
        }

        public View getView(final Context context) throws UnsupportedOperationException {
            EditText input = (EditText) super.getView(context);
            input.setRawInputType(Configuration.KEYBOARD_12KEY);
            input.setInputType(InputType.TYPE_CLASS_NUMBER |
                               InputType.TYPE_NUMBER_FLAG_SIGNED);
            return input;
        }
    }

    public static class PasswordInput extends EditInput {
        public static final String INPUT_TYPE = "password";
        public PasswordInput(JSONObject obj) {
            super(obj);
        }

        public View getView(Context context) throws UnsupportedOperationException {
            EditText input = (EditText) super.getView(context);
            input.setInputType(InputType.TYPE_CLASS_TEXT |
                               InputType.TYPE_TEXT_VARIATION_PASSWORD |
                               InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
            return input;
        }

        public String getValue() {
            EditText edit = (EditText)mView;
            return edit.getText().toString();
        }
    }

    public static class CheckboxInput extends PromptInput {
        public static final String INPUT_TYPE = "checkbox";
        private boolean mChecked;

        public CheckboxInput(JSONObject obj) {
            super(obj);
            mChecked = obj.optBoolean("checked");
        }

        public View getView(Context context) throws UnsupportedOperationException {
            CheckBox checkbox = new CheckBox(context);
            checkbox.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
            checkbox.setText(mLabel);
            checkbox.setChecked(mChecked);
            mView = (View)checkbox;
            return mView;
        }

        public String getValue() {
            CheckBox checkbox = (CheckBox)mView;
            return checkbox.isChecked() ? "true" : "false";
        }
    }

    public static class DateTimeInput extends PromptInput {
        public static final String[] INPUT_TYPES = new String[] {
            "date",
            "week",
            "time",
            "datetime-local",
            "datetime",
            "month"
        };

        public DateTimeInput(JSONObject obj) {
            super(obj);
        }

        public View getView(Context context) throws UnsupportedOperationException {
            if (mType.equals("date")) {
                try {
                    DateTimePicker input = new DateTimePicker(context, "yyyy-MM-dd", mValue,
                                                              DateTimePicker.PickersState.DATE);
                    input.toggleCalendar(true);
                    mView = (View)input;
                } catch (UnsupportedOperationException ex) {
                    // We can't use our custom version of the DatePicker widget because the sdk is too old.
                    // But we can fallback on the native one.
                    DatePicker input = new DatePicker(context);
                    try {
                        if (!TextUtils.isEmpty(mValue)) {
                            GregorianCalendar calendar = new GregorianCalendar();
                            calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(mValue));
                            input.updateDate(calendar.get(Calendar.YEAR),
                                             calendar.get(Calendar.MONTH),
                                             calendar.get(Calendar.DAY_OF_MONTH));
                        }
                    } catch (Exception e) {
                        Log.e(LOGTAG, "error parsing format string: " + e);
                    }
                    mView = (View)input;
                }
            } else if (mType.equals("week")) {
                DateTimePicker input = new DateTimePicker(context, "yyyy-'W'ww", mValue,
                                                          DateTimePicker.PickersState.WEEK);
                mView = (View)input;
            } else if (mType.equals("time")) {
                TimePicker input = new TimePicker(context);
                input.setIs24HourView(DateFormat.is24HourFormat(context));

                GregorianCalendar calendar = new GregorianCalendar();
                if (!TextUtils.isEmpty(mValue)) {
                    try {
                        calendar.setTime(new SimpleDateFormat("HH:mm").parse(mValue));
                    } catch (Exception e) { }
                }
                input.setCurrentHour(calendar.get(GregorianCalendar.HOUR_OF_DAY));
                input.setCurrentMinute(calendar.get(GregorianCalendar.MINUTE));
                mView = (View)input;
            } else if (mType.equals("datetime-local") || mType.equals("datetime")) {
                DateTimePicker input = new DateTimePicker(context, "yyyy-MM-dd HH:mm", mValue,
                                                          DateTimePicker.PickersState.DATETIME);
                input.toggleCalendar(true);
                mView = (View)input;
            } else if (mType.equals("month")) {
                DateTimePicker input = new DateTimePicker(context, "yyyy-MM", mValue,
                                                          DateTimePicker.PickersState.MONTH);
                mView = (View)input;
            }
            return mView;
        }

        private static String formatDateString(String dateFormat, Calendar calendar) {
            return new SimpleDateFormat(dateFormat).format(calendar.getTime());
        }

        public String getValue() {
            if (Build.VERSION.SDK_INT < 11 && mType.equals("date")) {
                // We can't use the custom DateTimePicker with a sdk older than 11.
                // Fallback on the native DatePicker.
                DatePicker dp = (DatePicker)mView;
                GregorianCalendar calendar =
                    new GregorianCalendar(dp.getYear(),dp.getMonth(),dp.getDayOfMonth());
                return formatDateString("yyyy-MM-dd",calendar);
            } else if (mType.equals("time")) {
                TimePicker tp = (TimePicker)mView;
                GregorianCalendar calendar =
                    new GregorianCalendar(0,0,0,tp.getCurrentHour(),tp.getCurrentMinute());
                return formatDateString("HH:mm",calendar);
            } else {
                DateTimePicker dp = (DateTimePicker)mView;
                GregorianCalendar calendar = new GregorianCalendar();
                calendar.setTimeInMillis(dp.getTimeInMillis());
                if (mType.equals("date")) {
                    return formatDateString("yyyy-MM-dd",calendar);
                } else if (mType.equals("week")) {
                    return formatDateString("yyyy-'W'ww",calendar);
                } else if (mType.equals("datetime-local")) {
                    return formatDateString("yyyy-MM-dd HH:mm",calendar);
                } else if (mType.equals("datetime")) {
                    calendar.set(GregorianCalendar.ZONE_OFFSET,0);
                    calendar.setTimeInMillis(dp.getTimeInMillis());
                    return formatDateString("yyyy-MM-dd HH:mm",calendar);
                } else if (mType.equals("month")) {
                    return formatDateString("yyyy-MM",calendar);
                }
            }
            return super.getValue();
        }
    }

    public static class MenulistInput extends PromptInput {
        public static final String INPUT_TYPE = "menulist";
        private static String[] mListitems;
        private static int mSelected;

        public Spinner spinner;
        public AllCapsTextView textView;

        public MenulistInput(JSONObject obj) {
            super(obj);
            mListitems = Prompt.getStringArray(obj, "values");
            mSelected = obj.optInt("selected");
        }

        public View getView(final Context context) throws UnsupportedOperationException {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
                spinner = new Spinner(context);
            } else {
                spinner = new Spinner(context, Spinner.MODE_DIALOG);
            }
            try {
                if (mListitems.length > 0) {
                    ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, android.R.layout.simple_spinner_item, mListitems);
                    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

                    spinner.setAdapter(adapter);
                    spinner.setSelection(mSelected);
                }
            } catch(Exception ex) { }

            if (!TextUtils.isEmpty(mLabel)) {
                LinearLayout container = new LinearLayout(context);
                container.setOrientation(LinearLayout.VERTICAL);

                textView = new AllCapsTextView(context, null);
                textView.setText(mLabel);
                container.addView(textView);

                container.addView(spinner);
                return container;
            }

            return spinner;
        }

        public String getValue() {
            return Integer.toString(spinner.getSelectedItemPosition());
        }
    }

    public static class LabelInput extends PromptInput {
        public static final String INPUT_TYPE = "label";
        public LabelInput(JSONObject obj) {
            super(obj);
        }

        public View getView(Context context) throws UnsupportedOperationException {
            // not really an input, but a way to add labels and such to the dialog
            TextView view = new TextView(context);
            view.setText(Html.fromHtml(mLabel));
            mView = view;
            return mView;
        }

        public String getValue() {
            return "";
        }
    }

    public PromptInput(JSONObject obj) {
        mLabel = obj.optString("label");
        mType = obj.optString("type");
        String id = obj.optString("id");
        mId = TextUtils.isEmpty(id) ? mType : id;
        mValue = obj.optString("value");
    }

    public static PromptInput getInput(JSONObject obj) {
        String type = obj.optString("type");
        if (EditInput.INPUT_TYPE.equals(type)) {
            return new EditInput(obj);
        } else if (NumberInput.INPUT_TYPE.equals(type)) {
            return new NumberInput(obj);
        } else if (PasswordInput.INPUT_TYPE.equals(type)) {
            return new PasswordInput(obj);
        } else if (CheckboxInput.INPUT_TYPE.equals(type)) {
            return new CheckboxInput(obj);
        } else if (MenulistInput.INPUT_TYPE.equals(type)) {
            return new MenulistInput(obj);
        } else if (LabelInput.INPUT_TYPE.equals(type)) {
            return new LabelInput(obj);
        } else if (IconGridInput.INPUT_TYPE.equals(type)) {
            return new IconGridInput(obj);
        } else {
            for (String dtType : DateTimeInput.INPUT_TYPES) {
                if (dtType.equals(type)) {
                    return new DateTimeInput(obj);
                }
            }
        }
        return null;
    }

    public View getView(Context context) throws UnsupportedOperationException {
        return null;
    }

    public String getId() {
        return mId;
    }

    public String getValue() {
        return "";
    }

    public boolean getScrollable() {
        return false;
    }
}
