package mn.today.widget;

import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.CalendarContract;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.support.design.widget.TextInputLayout;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.SwitchCompat;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.TimePicker;

import java.util.Calendar;
import java.util.TimeZone;

import mn.today.CalendarUtils;
import mn.today.R;
import mn.today.ViewUtils;
import mn.today.content.CalendarCursor;

/**
 * Created by Tortuvshin Byambaa on 1/31/2017.
 */
@Deprecated
public class EventEditView extends RelativeLayout {

    private final TextInputLayout mTextInputTitle;
    private final EditText mEditTextTitle;
    private final SwitchCompat mSwitchAllDay;
    private final TextView mTextViewStartDate;
    private final TextView mTextViewStartTime;
    private final TextView mTextViewEndDate;
    private final TextView mTextViewEndTime;
    private final TextView mTextViewCalendar;
    private final int[] mColors;
    private final int mTransparentColor;
    private Event mEvent = Event.createInstance();
    private CalendarCursor mCursor;

    public EventEditView(Context context) {
        this(context, null);
    }

    public EventEditView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public EventEditView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        inflate(context, R.layout.event_edit_view, this);
        int horizontalPadding = context.getResources()
                .getDimensionPixelSize(R.dimen.horizontal_padding),
                verticalPadding = context.getResources()
                        .getDimensionPixelSize(R.dimen.vertical_padding);
        setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
        mTextInputTitle = (TextInputLayout) findViewById(R.id.text_input_title);
        mEditTextTitle = (EditText) findViewById(R.id.edit_text_title);
        mSwitchAllDay = (SwitchCompat) findViewById(R.id.switch_all_day);
        mTextViewStartDate = (TextView) findViewById(R.id.text_view_start_date);
        mTextViewStartTime = (TextView) findViewById(R.id.text_view_start_time);
        mTextViewEndDate = (TextView) findViewById(R.id.text_view_end_date);
        mTextViewEndTime = (TextView) findViewById(R.id.text_view_end_time);
        mTextViewCalendar = (TextView) findViewById(R.id.text_view_calendar);
        mTransparentColor = ContextCompat.getColor(context, android.R.color.transparent);
        if (isInEditMode()) {
            mColors = new int[]{mTransparentColor};
        } else {
            mColors = ViewUtils.getCalendarColors(context);
        }
        setupViews();
        setEvent(mEvent);
    }

    /**
     * Sets view model for this view
     * @param event    view model representing event to edit
     */
    public void setEvent(@NonNull Event event) {
        mEvent = event;
        mEditTextTitle.setText(event.title);
        mEditTextTitle.setSelection(mEditTextTitle.length());
        mSwitchAllDay.setChecked(event.isAllDay);
        setCalendarId(mEvent.calendarId);
        setDate(true);
        setDate(false);
        setTime(true);
        setTime(false);
    }

    /**
     * Gets view model representing event being edited
     * @return  view model representing editing event
     */
    @NonNull
    public Event getEvent() {
        return mEvent;
    }

    /**
     * Sets data source for calendars from {@link android.provider.CalendarContract.Calendars}
     * @param cursor    cursor to access list of calendars
     */
    public void swapCalendarSource(CalendarCursor cursor) {
        mCursor = cursor;
        mTextViewCalendar.setEnabled(mCursor != null && mCursor.getCount() > 0);
    }

    /**
     * Sets name of selected event calendar
     * @param calendarName    selected calendar name
     */
    public void setSelectedCalendar(String calendarName) {
        mTextViewCalendar.setText(calendarName);
    }

    private void setupViews() {
        mTextInputTitle.setErrorEnabled(true);
        mEditTextTitle.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                // no op
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                mEvent.title = s != null ? s.toString() : "";
                mTextInputTitle.setError(TextUtils.isEmpty(s) ?
                        getResources().getString(R.string.warning_empty_title) : null);
            }

            @Override
            public void afterTextChanged(Editable s) {
                // no op
            }
        });
        findViewById(R.id.text_view_all_day).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mSwitchAllDay.toggle();
            }
        });
        mSwitchAllDay.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (mEvent.isAllDay == isChecked) {
                    return;
                }
                mEvent.setIsAllDay(isChecked);
                if (isChecked) {
                    setDate(true);
                    setDate(false);
                    setTime(true);
                    setTime(false);
                }
            }
        });
        mTextViewStartDate.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                showDatePicker(true);
            }
        });
        mTextViewEndDate.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                showDatePicker(false);
            }
        });
        mTextViewStartTime.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                showTimePicker(true);
            }
        });
        mTextViewEndTime.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                showTimePicker(false);
            }
        });
        mTextViewCalendar.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                showCalendarPicker();
            }
        });
        mTextViewCalendar.setEnabled(false);
    }

    private void setDate(boolean startDate) {
        TextView label = startDate ? mTextViewStartDate : mTextViewEndDate;
        Calendar dateTime = startDate ? mEvent.localStart : mEvent.localEnd;
        label.setText(CalendarUtils.toDayString(getContext(), dateTime.getTimeInMillis()));
        ensureValidDates(startDate);
    }

    private void setTime(boolean startTime) {
        TextView label = startTime ? mTextViewStartTime : mTextViewEndTime;
        Calendar dateTime = startTime ? mEvent.localStart : mEvent.localEnd;
        label.setText(CalendarUtils.toTimeString(getContext(), dateTime.getTimeInMillis()));
        ensureValidTimes(startTime);
    }

    @VisibleForTesting
    void changeCalendar(int selection) {
        mCursor.moveToPosition(selection);
        mTextViewCalendar.setText(mCursor.getDisplayName());
        setCalendarId(mCursor.getId());
    }

    private void setCalendarId(long calendarId) {
        mEvent.calendarId = calendarId;
        if (calendarId == Event.NO_ID) {
            setBackgroundColor(mTransparentColor);
        } else {
            setBackgroundColor(mColors[(int) (Math.abs(mEvent.calendarId) % mColors.length)]);
        }
    }

    private void ensureValidDates(boolean startDateChanged) {
        if (startDateChanged) {
            if (mEvent.localStart.after(mEvent.localEnd)) {
                mEvent.localEnd.setTimeInMillis(mEvent.localStart.getTimeInMillis());
                setDate(false);
                setTime(false);
            }
        } else {
            if (mEvent.localEnd.before(mEvent.localStart)) {
                mEvent.localStart.setTimeInMillis(mEvent.localEnd.getTimeInMillis());
                setDate(true);
                setTime(true);
            }
        }
    }

    private void ensureValidTimes(boolean startTimeChanged) {
        if (startTimeChanged) {
            if (mEvent.localStart.after(mEvent.localEnd)) {
                mEvent.localEnd.setTimeInMillis(mEvent.localStart.getTimeInMillis());
                setTime(false);
            }
        } else {
            if (mEvent.localEnd.before(mEvent.localStart)) {
                mEvent.localStart.setTimeInMillis(mEvent.localEnd.getTimeInMillis());
                setTime(true);
            }
        }
    }

    private void showDatePicker(final boolean startDate) {
        final Calendar dateTime = startDate ? mEvent.localStart : mEvent.localEnd;
        new DatePickerDialog(getContext(),
                new DatePickerDialog.OnDateSetListener() {
                    @Override
                    public void onDateSet(DatePicker view, int year,
                                          int monthOfYear, int dayOfMonth) {
                        dateTime.set(year, monthOfYear, dayOfMonth);
                        mSwitchAllDay.setChecked(false);
                        setDate(startDate);
                    }
                },
                dateTime.get(Calendar.YEAR),
                dateTime.get(Calendar.MONTH),
                dateTime.get(Calendar.DAY_OF_MONTH))
                .show();
    }

    private void showTimePicker(final boolean startTime) {
        final Calendar dateTime = startTime ? mEvent.localStart : mEvent.localEnd;
        new TimePickerDialog(getContext(),
                new TimePickerDialog.OnTimeSetListener() {
                    @Override
                    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
                        dateTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
                        dateTime.set(Calendar.MINUTE, minute);
                        mSwitchAllDay.setChecked(false);
                        setTime(startTime);
                    }
                },
                dateTime.get(Calendar.HOUR_OF_DAY),
                dateTime.get(Calendar.MINUTE),
                false)
                .show();
    }

    private void showCalendarPicker() {
        new AlertDialog.Builder(getContext())
                .setCursor(mCursor, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        changeCalendar(which);
                    }
                }, CalendarContract.Calendars.CALENDAR_DISPLAY_NAME)
                .setNegativeButton(android.R.string.cancel, null)
                .create()
                .show();
    }

    /**
     * View model for {@link EventEditView}.
     * Event represented by this class is assumed to be in system timezone.
     */
    public static class Event implements Parcelable {

        public static final Creator<Event> CREATOR = new Creator<Event>() {
            @Override
            public Event createFromParcel(Parcel in) {
                return new Event(in);
            }

            @Override
            public Event[] newArray(int size) {
                return new Event[size];
            }
        };

        /**
         * Creates an instance of {@link Event} that starts at 'earliest' future time
         * @return  an {@link Event} instance
         */
        public static Event createInstance() {
            return new Event();
        }

        /**
         * Builder utility to build an {@link Event}
         */
        public static class Builder {
            private final Event event = new Event();

            /**
             * Sets event ID
             * @param id    event ID
             * @return  this instance (fluent API)
             */
            public Builder id(long id) {
                event.id = id;
                return this;
            }

            /**
             * Sets event calendar ID
             * @param calendarId    event calendar ID
             * @return  this instance (fluent API)
             */
            public Builder calendarId(long calendarId) {
                event.calendarId = calendarId;
                return this;
            }

            /**
             * Sets event title
             * @param title    event title
             * @return  this instance (fluent API)
             */
            public Builder title(String title) {
                event.title = title;
                return this;
            }

            /**
             * Sets event start date time
             * @param timeMillis    start date time in milliseconds
             * @return  this instance (fluent API)
             */
            public Builder start(long timeMillis) {
                event.localStart.setTimeInMillis(timeMillis);
                return this;
            }

            /**
             * Sets event end date time
             * @param timeMillis    end date time in milliseconds
             * @return  this instance (fluent API)
             */
            public Builder end(long timeMillis) {
                event.localEnd.setTimeInMillis(timeMillis);
                return this;
            }

            /**
             * Sets event all day status
             * @param isAllDay    true if event is all day, false otherwise
             * @return  this instance (fluent API)
             */
            public Builder allDay(boolean isAllDay) {
                event.isAllDay = isAllDay;
                return this;
            }

            /**
             * Creates the {@link Event} that has been built by this builder
             * @return  an {@link Event} instance
             */
            public Event build() {
                return event;
            }
        }

        private static final long NO_ID = -1;

        long id = NO_ID;
        long calendarId = NO_ID;
        String title;
        boolean isAllDay = false;
        final Calendar localStart = Calendar.getInstance();
        final Calendar localEnd = Calendar.getInstance();

        Event() {
            localStart.add(Calendar.HOUR_OF_DAY, 1);
            localStart.set(Calendar.MINUTE, 0);
            localEnd.add(Calendar.HOUR_OF_DAY, 2);
            localEnd.set(Calendar.MINUTE, 0);
        }

        Event(Parcel in) {
            id = in.readLong();
            calendarId = in.readLong();
            title = in.readString();
            isAllDay = in.readByte() != 0;
            localStart.setTimeInMillis(in.readLong());
            localEnd.setTimeInMillis(in.readLong());
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeLong(id);
            dest.writeLong(calendarId);
            dest.writeString(title);
            dest.writeByte((byte) (isAllDay ? 1 : 0));
            dest.writeLong(localStart.getTimeInMillis());
            dest.writeLong(localEnd.getTimeInMillis());
        }

        @Override
        public int describeContents() {
            return 0;
        }

        /**
         * Gets event ID
         * @return  event ID
         */
        public long getId() {
            return id;
        }

        /**
         * Checks if this instance has event ID
         * @return  true for existing events, false otherwise
         */
        public boolean hasId() {
            return id != NO_ID;
        }

        /**
         * Checks if this instance has calendar ID
         * @return  true if have calendar ID, false otherwise
         */
        public boolean hasCalendarId() {
            return calendarId != NO_ID;
        }

        /**
         * Checks if event has non-empty title
         * @return  true if has non-empty title, false otherwise
         */
        public boolean hasTitle() {
            return !TextUtils.isEmpty(title);
        }

        /**
         * Gets event title
         * @return  event title
         */
        public String getTitle() {
            return title;
        }

        /**
         * Gets event start date time
         * @return  start date time in local timezone, or midnight UTC if event is all day
         */
        public long getStartDateTime() {
            if (isAllDay) {
                return CalendarUtils.toUtcTimeZone(localStart.getTimeInMillis());
            } else {
                return localStart.getTimeInMillis();
            }
        }

        /**
         * Gets event end date time
         * @return  end date time in local timezone, or midnight UTC if event is all day
         */
        public long getEndDateTime() {
            if (isAllDay) {
                return CalendarUtils.toUtcTimeZone(localEnd.getTimeInMillis());
            } else {
                return localEnd.getTimeInMillis();
            }
        }

        /**
         * Checks if event is all day
         * @return  true if event is all day, false otherwise
         */
        public boolean isAllDay() {
            return isAllDay;
        }

        /**
         * Gets event timezone
         * @return  local system timezone ID, or UTC if event is all day
         */
        public String getTimeZone() {
            return isAllDay ? CalendarUtils.TIMEZONE_UTC : TimeZone.getDefault().getID();
        }

        /**
         * Gets event calendar ID
         * @return  event calendar ID
         */
        public long getCalendarId() {
            return calendarId;
        }

        void setIsAllDay(boolean isAllDay) {
            this.isAllDay = isAllDay;
            if (isAllDay) {
                localStart.set(Calendar.HOUR_OF_DAY, 0);
                localStart.set(Calendar.MINUTE, 0);
                localEnd.set(Calendar.HOUR_OF_DAY, 0);
                localEnd.set(Calendar.MINUTE, 0);
                if (localEnd.equals(localStart)) {
                    localEnd.add(Calendar.DAY_OF_MONTH, 1);
                }
            }
        }
    }
}