/*
 * Copyright (C) 2009 University of Washington
 *
 * 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 org.odk.collect.android.widgets;

import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Build;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.CalendarView;
import android.widget.DatePicker;
import android.widget.TableLayout;
import android.widget.TextView;
import android.widget.TimePicker;

import org.javarosa.core.model.data.DateTimeData;
import org.javarosa.core.model.data.IAnswerData;
import org.javarosa.form.api.FormEntryPrompt;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.odk.collect.android.R;
import org.odk.collect.android.utilities.DateFormatter;

import java.util.Calendar;
import java.util.Date;

/**
 * Displays a DatePicker widget. DateWidget handles leap years and does not allow dates that do not exist.
 *
 * @author Carl Hartung (carlhartung@gmail.com)
 * @author Yaw Anokwa (yanokwa@gmail.com)
 */
public class DateTimeWidget extends QuestionWidget {

  private LocalDate localDate;
  private LocalTime localTime;
  private TextView datePickerField;
  private TextView timePickerField;

  /**
   * @param context
   *     the Context in which the Views will be inflated.
   * @param prompt
   *     the prompt containing data about the question.
   */
  public DateTimeWidget(Context context, FormEntryPrompt prompt) {
    super(context, prompt);

    localDate = null;
    localTime = null;

    final View view = LayoutInflater.from(context).inflate(R.layout.date_time_picker_field, this, false);
    TableLayout.LayoutParams params = new TableLayout.LayoutParams();
    params.setMargins(getResources().getDimensionPixelSize(R.dimen.edittext_padding_start), 0, 0, 0);
    view.setLayoutParams(params);
    datePickerField = (TextView) view.findViewById(R.id.date_picker_field);
    timePickerField = (TextView) view.findViewById(R.id.time_picker_field);

    datePickerField.setClickable(false);
    timePickerField.setClickable(false);
    datePickerField.setFocusable(false);
    timePickerField.setFocusable(false);

    final boolean readOnly = prompt.isReadOnly();
    if (readOnly) {
      datePickerField.setBackground(null);
      timePickerField.setBackground(null);
    } else {
      datePickerField.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(final View v) {
          showDatePickerDialog();
        }
      });
      timePickerField.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(final View v) {
          showTimePickerDialog();
        }
      });
    }

    setGravity(Gravity.START);

    // If there's an answer, use it.
    setAnswer();
    addView(view);
  }

  private void showDatePickerDialog() {
    final Calendar now = Calendar.getInstance();
    final Calendar defaultDate = Calendar.getInstance();

    if (localDate != null) {
      defaultDate.set(Calendar.MONTH, localDate.getMonthOfYear());
      defaultDate.set(Calendar.DAY_OF_MONTH, localDate.getDayOfMonth());
      defaultDate.set(Calendar.YEAR, localDate.getYear());
    } else {
      defaultDate.setTime(now.getTime());
    }

    final int yearNow = defaultDate.get(Calendar.YEAR);
    final int monthNow = defaultDate.get(Calendar.MONTH); // Note: zero based!
    final int dayNow = defaultDate.get(Calendar.DAY_OF_MONTH);

    final DatePickerDialog dialog = new DatePickerDialog(getContext(), new DatePickerDialog.OnDateSetListener() {

      @Override
      public void onDateSet(final DatePicker view, final int year, final int monthOfYear, final int dayOfMonth) {
        final Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        calendar.set(Calendar.MONTH, monthOfYear);
        calendar.set(Calendar.YEAR, year);

        if (localDate == null) {
          localDate = LocalDate.now();
        }
        localDate = localDate.withDayOfMonth(dayOfMonth).withMonthOfYear(monthOfYear).withYear(year);
        formatDate(localDate.toDate().getTime());
      }
    }, yearNow, monthNow, dayNow);

    dialog.setButton(DialogInterface.BUTTON_NEUTRAL, getContext().getString(R.string.date_picker_clear),
        new DialogInterface.OnClickListener() {
          @Override
          public void onClick(final DialogInterface dialog, final int which) {
            if (which == DialogInterface.BUTTON_NEUTRAL) {
              datePickerField.setText("");
              localDate = null;
            }
          }
        });

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
      final CalendarView cal = dialog.getDatePicker().getCalendarView();
      if (cal != null) {
        final long current = cal.getDate();
        cal.setDate(cal.getMaxDate(), false, true);
        cal.setDate(current, false, true);
      }
    }

    dialog.show();
  }

  private void showTimePickerDialog() {
    TimePickerDialog.OnTimeSetListener listener = new TimePickerDialog.OnTimeSetListener() {
      @Override
      public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        localTime = new LocalTime().withHourOfDay(hourOfDay).withMinuteOfHour(minute);
        final DateTime dateTime = DateTime.now().withTime(localTime);
        formatTime(dateTime.getMillis());
      }
    };

    boolean is24Hour = DateFormatter.is24Hour(getContext());

    LocalTime dialogTime;

    if (localTime != null) {
      dialogTime = new LocalTime(localTime);
    } else {
      dialogTime = LocalTime.now();
    }

    TimePickerDialog mTimePicker = new TimePickerDialog(getContext(), listener, dialogTime.getHourOfDay(), dialogTime
        .getMinuteOfHour(), is24Hour);

    mTimePicker.setButton(DialogInterface.BUTTON_NEUTRAL, getContext().getString(R.string.date_picker_clear),
        new DialogInterface.OnClickListener() {
          @Override
          public void onClick(final DialogInterface dialog, final int which) {
            if (which == DialogInterface.BUTTON_NEUTRAL) {
              timePickerField.setText("");
              localTime = null;
            }
          }
        });

    mTimePicker.show();
  }

  private void formatDate(final long date) {
    datePickerField.setText(DateFormatter.formatDatePickerOutput(getContext(), date));
  }

  private void formatTime(final long time) {
    timePickerField.setText(DateFormatter.formatTimePickerOutput(getContext(), time));
  }

  private void setAnswer() {
    if (mPrompt.getAnswerValue() != null) {
      DateTime ldt = new DateTime(((Date) mPrompt.getAnswerValue().getValue()).getTime());
      if (mPrompt.isReadOnly()) {
        datePickerField.setText(DateFormatter.formatDateTime(getContext(), ldt.getMillis()));
        timePickerField.setVisibility(GONE);
      } else {
        localDate = ldt.toLocalDate();
        localTime = ldt.toLocalTime();
        formatTime(ldt.getMillis());
        formatDate(ldt.getMillis());
      }
    } else {
      clearAnswer();
    }
  }


  /**
   * Resets date to today.
   */
  @Override
  public void clearAnswer() {
    localDate = null;
    localTime = null;
    datePickerField.setText("");
    timePickerField.setText("");
  }


  @Override
  public IAnswerData getAnswer() {
    clearFocus();
    if (localDate == null && localTime == null) {
      return null;
    }
    if (localDate == null) {
      localDate = LocalDate.now();
    }
    if (localTime == null) {
      localTime = LocalTime.now();
    }
    final DateTime dateTime = new DateTime().withTime(localTime).withDate(localDate);
    return new DateTimeData(dateTime.toDate());
  }

  @Override
  public void setFocus(Context context) {
    // Hide the soft keyboard if it's showing.
    InputMethodManager inputManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
    inputManager.hideSoftInputFromWindow(this.getWindowToken(), 0);
  }

  @Override
  public void setOnLongClickListener(OnLongClickListener l) {
    datePickerField.setOnLongClickListener(l);
    timePickerField.setOnLongClickListener(l);
  }

  @Override
  public void cancelLongPress() {
    super.cancelLongPress();
    datePickerField.cancelLongPress();
    timePickerField.cancelLongPress();
  }
}
