package eu.pretix.pretixdroid.ui;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.text.InputType;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import org.joda.time.LocalDate;
import org.joda.time.LocalTime;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import eu.pretix.libpretixsync.check.TicketCheckProvider;
import eu.pretix.libpretixsync.db.AbstractQuestion;
import eu.pretix.libpretixsync.db.Question;
import eu.pretix.libpretixsync.db.QuestionOption;
import eu.pretix.pretixdroid.R;

public class QuestionDialogHelper {
    public interface RetryHandler {
        public void retry(String secret, List<TicketCheckProvider.Answer> answers, boolean ignore_unpaid);
    }

    static class OptionAdapter extends ArrayAdapter<QuestionOption> {
        public OptionAdapter(Context context, List<QuestionOption> objects) {
            super(context, R.layout.spinneritem_simple, objects);
        }
    }

    public static void addError(Context ctx, Object f, TextView label, int strid) {
        if (f instanceof EditText) {
            ((EditText) f).setError(strid == 0 ? null : ctx.getString(strid));
        } else if (f instanceof List && ((List) f).get(0) instanceof EditText) {
            ((List<EditText>) f).get(1).setError(strid == 0 ? null : ctx.getString(strid));
        } else if (label != null) {
            label.setError(strid == 0 ? null : ctx.getString(strid));
        }

    }

    public static Dialog showDialog(final Activity ctx, final TicketCheckProvider.CheckResult res,
                                    final String secret, final RetryHandler retryHandler,
                                    final boolean ignore_unpaid) {
        LayoutInflater inflater = ctx.getLayoutInflater();
        final Map<Question, Object> fviews = new HashMap<>();
        final Map<Question, TextView> labels = new HashMap<>();
        final SimpleDateFormat hf = new SimpleDateFormat("HH:mm", Locale.US);
        final SimpleDateFormat wf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm", Locale.US);
        final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.US);

        View view = inflater.inflate(R.layout.dialog_questions, null);
        final LinearLayout llFormFields = view.findViewById(R.id.llFormFields);

        for (TicketCheckProvider.RequiredAnswer ra : res.getRequiredAnswers()) {
            TextView tv = new TextView(ctx);
            tv.setText(ra.getQuestion().getQuestion());
            llFormFields.addView(tv);
            labels.put(ra.getQuestion(), tv);

            switch (ra.getQuestion().getType()) {
                case S:
                    EditText fieldS = new EditText(ctx);
                    fieldS.setText(ra.getCurrentValue());
                    fieldS.setLines(1);
                    fieldS.setSingleLine(true);
                    fviews.put(ra.getQuestion(), fieldS);
                    llFormFields.addView(fieldS);
                    break;
                case T:
                    EditText fieldT = new EditText(ctx);
                    fieldT.setText(ra.getCurrentValue());
                    fieldT.setLines(2);
                    fviews.put(ra.getQuestion(), fieldT);
                    llFormFields.addView(fieldT);
                    break;
                case N:
                    EditText fieldN = new EditText(ctx);
                    fieldN.setText(ra.getCurrentValue());
                    fieldN.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL | InputType.TYPE_NUMBER_FLAG_SIGNED);
                    fieldN.setSingleLine(true);
                    fieldN.setLines(1);
                    fviews.put(ra.getQuestion(), fieldN);
                    llFormFields.addView(fieldN);
                    break;
                case B:
                    CheckBox fieldB = new CheckBox(ctx);
                    fieldB.setText(R.string.yes);
                    fieldB.setChecked("True".equals(ra.getCurrentValue()));
                    fviews.put(ra.getQuestion(), fieldB);
                    llFormFields.addView(fieldB);
                    break;
                case F:
                    break;
                case M:
                    List<CheckBox> fields = new ArrayList<>();
                    List<String> selected = Arrays.asList(ra.getCurrentValue().split(","));
                    for (QuestionOption opt : ra.getQuestion().getOptions()) {
                        CheckBox field = new CheckBox(ctx);
                        field.setText(opt.getValue());
                        field.setTag(opt.getServer_id());
                        if (selected.contains(opt.getServer_id().toString())) {
                            field.setChecked(true);
                        }
                        fields.add(field);
                        llFormFields.addView(field);
                    }
                    fviews.put(ra.getQuestion(), fields);
                    break;
                case C:
                    Spinner fieldC = new Spinner(ctx);
                    List<QuestionOption> opts = ra.getQuestion().getOptions();
                    QuestionOption emptyOpt = new QuestionOption();
                    emptyOpt.setServer_id(0L);
                    emptyOpt.setValue("");
                    opts.add(0, emptyOpt);
                    fieldC.setAdapter(new OptionAdapter(ctx, opts));
                    int i = 0;
                    for (QuestionOption opt : ra.getQuestion().getOptions()) {
                        if (opt.getServer_id().toString().equals(ra.getCurrentValue())) {
                            fieldC.setSelection(i);
                            break;
                        }
                        i++;
                    }
                    fviews.put(ra.getQuestion(), fieldC);
                    llFormFields.addView(fieldC);
                    break;
                case D:
                    DatePickerField fieldD = new DatePickerField(ctx);
                    if (ra.getCurrentValue().length() > 0) {
                        try {
                            fieldD.setValue(df.parse(ra.getCurrentValue()));
                        } catch (ParseException e) {
                            e.printStackTrace();
                        }
                    }
                    fviews.put(ra.getQuestion(), fieldD);
                    llFormFields.addView(fieldD);
                    break;
                case H:
                    TimePickerField fieldH = new TimePickerField(ctx);
                    fviews.put(ra.getQuestion(), fieldH);
                    try {
                        fieldH.setValue(LocalTime.fromDateFields(hf.parse(ra.getCurrentValue())));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                    llFormFields.addView(fieldH);
                    break;
                case W:
                    List<EditText> fieldsW = new ArrayList<>();
                    LinearLayout llInner = new LinearLayout(ctx);
                    llInner.setOrientation(LinearLayout.HORIZONTAL);

                    DatePickerField fieldWD = new DatePickerField(ctx);
                    fieldWD.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
                    fieldsW.add(fieldWD);
                    llInner.addView(fieldWD);

                    TimePickerField fieldWH = new TimePickerField(ctx);
                    fieldWH.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
                    fieldsW.add(fieldWH);
                    llInner.addView(fieldWH);

                    if (ra.getCurrentValue().length() > 0) {
                        try {
                            fieldWD.setValue(wf.parse(ra.getCurrentValue()));
                            fieldWH.setValue(LocalTime.fromDateFields(wf.parse(ra.getCurrentValue())));
                        } catch (ParseException e) {
                            e.printStackTrace();
                        }
                    }

                    fviews.put(ra.getQuestion(), fieldsW);
                    llFormFields.addView(llInner);
                    break;
            }
        }

        final Dialog dialog = new AlertDialog.Builder(ctx)
                .setView(view)
                .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                })
                .setPositiveButton(R.string.cont, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        // Dummy. We set this below since we don't want to auto-close tis
                    }
                }).create();

        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialogInterface) {

                Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
                button.setOnClickListener(new View.OnClickListener() {

                    @Override
                    public void onClick(View view) {
                        final List<TicketCheckProvider.Answer> answers = new ArrayList<>();
                        boolean has_errors = false;

                        for (TicketCheckProvider.RequiredAnswer ra : res.getRequiredAnswers()) {
                            String answer = "";
                            boolean empty = false;
                            Object field = fviews.get(ra.getQuestion());

                            switch (ra.getQuestion().getType()) {
                                case S:
                                case T:
                                    answer = ((EditText) field).getText().toString();
                                    empty = answer.trim().equals("");
                                    break;
                                case N:
                                    answer = ((EditText) field).getText().toString();
                                    empty = answer.trim().equals("");
                                    break;
                                case B:
                                    answer = ((CheckBox) field).isChecked() ? "True" : "False";
                                    empty = answer.equals("False");
                                    break;
                                case F:
                                    empty = true;
                                    break;
                                case M:
                                    empty = true;
                                    StringBuilder aw = new StringBuilder();
                                    for (CheckBox f : (List<CheckBox>) field) {
                                        if (f.isChecked()) {
                                            if (!empty) {
                                                aw.append(",");
                                            }
                                            aw.append(f.getTag());
                                            empty = false;
                                        }
                                    }
                                    answer = aw.toString();
                                    break;
                                case C:
                                    QuestionOption opt = ((QuestionOption) ((Spinner) field).getSelectedItem());
                                    if (opt.getServer_id() == 0) {
                                        empty = true;
                                    } else {
                                        answer = opt.getServer_id().toString();
                                    }
                                    break;
                                case D:
                                    empty = (((DatePickerField) field).getValue() == null);
                                    if (!empty) {
                                        answer = df.format(((DatePickerField) field).getValue().getTime());
                                    }
                                    break;
                                case H:
                                    empty = (((TimePickerField) field).getValue() == null);
                                    if (!empty) {
                                        answer = hf.format(((TimePickerField) field).getValue().toDateTimeToday().toDate());
                                    }
                                    break;
                                case W:
                                    List<EditText> fieldset = (List<EditText>) field;
                                    empty = (
                                            ((DatePickerField) fieldset.get(0)).getValue() == null
                                                    || ((TimePickerField) fieldset.get(1)).getValue() == null
                                    );
                                    if (!empty) {
                                        answer = wf.format(
                                                LocalDate.fromCalendarFields(((DatePickerField) fieldset.get(0)).getValue()).toDateTime(
                                                        ((TimePickerField) fieldset.get(1)).getValue()
                                                ).toDate()
                                        );
                                    }
                                    break;
                            }

                            if (empty && ra.getQuestion().isRequired()) {
                                has_errors = true;
                                addError(ctx, field, labels.get(ra.getQuestion()), R.string.question_input_required);
                            } else if (empty) {
                                answers.add(new TicketCheckProvider.Answer(ra.getQuestion(), ""));
                                addError(ctx, field, labels.get(ra.getQuestion()), 0);
                            } else {
                                try {
                                    ra.getQuestion().clean_answer(answer, ra.getQuestion().getOptions());
                                    addError(ctx, field, labels.get(ra.getQuestion()), 0);
                                } catch (AbstractQuestion.ValidationException e) {
                                    has_errors = true;
                                    addError(ctx, field, labels.get(ra.getQuestion()), R.string.question_input_invalid);
                                }
                                answers.add(new TicketCheckProvider.Answer(ra.getQuestion(), answer));
                            }
                        }
                        if (!has_errors) {
                            dialog.dismiss();
                            retryHandler.retry(secret, answers, ignore_unpaid);
                        } else {
                            Toast.makeText(ctx, R.string.question_validation_error, Toast.LENGTH_SHORT);
                        }
                    }
                });
            }
        });
        dialog.show();
        return dialog;
    }
}