package com.genesyslab.mobile.android.widgets;

import android.content.Context;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.text.format.Time;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TimeFormatException;
import android.view.View;
import android.widget.NumberPicker;

import com.genesyslab.mobile.android.sample.R;

import java.util.Arrays;

/**
 * Created by stau on 10/07/2014.
 */
public class DateTimePreference extends DialogPreference
{
    final private static String TAG = "DateTimePreference";

    private String currentDT;
    private NumberPicker datePicker;
    private NumberPicker hourPicker;
    private NumberPicker minutePicker;
    private NumberPicker periodPicker;
    private int maxDays = 28;
    final private static String[] defaultPeriods = {"AM", "PM"};
    final private static String DAY_OF_MONTH_FORMAT = "%a %b %d";
    final private static String ISO8601_FORMAT = "%FT%T.000Z";
    final private static String FRIENDLY_FORMAT = "%a %b %e %I:%M %p";
    final private static String DEFAULT_VALUE = "0000-00-00T00:00:00.000Z";

    private class TwoDigitFormatter implements NumberPicker.Formatter{
		public String format(int value){
            return String.format("%02d", value);
        }
    }

    public DateTimePreference(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        setDialogLayoutResource(R.layout.datetime_dialog);
        setPositiveButtonText(android.R.string.ok);
        setNegativeButtonText(android.R.string.cancel);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DateTimePreference);
        final int N = a.getIndexCount();
        for(int i = 0; i < N; ++i)
        {
            int attr = a.getIndex(i);
            switch(attr)
            {
                case R.styleable.DateTimePreference_maxDays:
                    maxDays = Math.max(1, a.getInt(attr, 28));
                    break;
            }
        }
        a.recycle();
    }

    @Override
    protected void onBindDialogView(View view)
    {
        super.onBindDialogView(view);

        // Initialize Picker elements
        // Note that dates will not be initialized
        datePicker = (NumberPicker)view.findViewById(R.id.datePicker);
        populateDates();
        datePicker.setMaxValue(maxDays - 1);
        datePicker.setMinValue(0);
        datePicker.setWrapSelectorWheel(false);
        datePicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);

        TwoDigitFormatter twoDigitFormatter = new TwoDigitFormatter();
        hourPicker = (NumberPicker)view.findViewById(R.id.hourPicker);
        hourPicker.setMaxValue(12);
        hourPicker.setMinValue(1);
        hourPicker.setFormatter(twoDigitFormatter);
        hourPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
        minutePicker = (NumberPicker)view.findViewById(R.id.minutePicker);
        minutePicker.setMaxValue(59);
        minutePicker.setMinValue(0);
        minutePicker.setFormatter(twoDigitFormatter);
        minutePicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
        periodPicker = (NumberPicker)view.findViewById(R.id.periodPicker);
        periodPicker.setMaxValue(1);
        periodPicker.setMinValue(0);
        periodPicker.setWrapSelectorWheel(false);
        periodPicker.setDisplayedValues(defaultPeriods);
        periodPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);

        setPickers();
    }

    public void populateDates()
    {
        String[] displayedDates = new String[maxDays];
        Time time = new Time();
        time.setToNow();

        // Populate datePicker values based on maxDays setting
        displayedDates[0] = "Today";

        for(int i=1;i<maxDays;++i)
        {
            time.monthDay += 1;
            time.normalize(false);
            displayedDates[i] = time.format(DAY_OF_MONTH_FORMAT);
        }

        Log.d(TAG, "displayedDates: " + Arrays.toString(displayedDates));
        datePicker.setDisplayedValues(displayedDates);
    }

    private int getDayDifference(Time time1, Time time2)
    {
        // Assume time1 and time2 are same timezone
        if (time1.year == time2.year)
        {
            if (time1.month == time2.month)
            {
                return time2.monthDay - time1.monthDay;
            }
            else
            {
                return time2.yearDay - time1.yearDay;
            }
        }
        else
        {
            int daysDiff = time1.getActualMaximum(Time.YEAR_DAY) - time1.yearDay;
            time1.year += 1;
            while(time1.year < time2.year)
            {
                daysDiff += time1.getActualMaximum(Time.YEAR_DAY);
            }
            daysDiff += time2.yearDay;
            return daysDiff;
        }
    }

    private void readPickers()
    {
        int day = datePicker.getValue(); // Days from Today
        int hour = hourPicker.getValue();
        int minute = minutePicker.getValue();
        int period = periodPicker.getValue();
        if(period==1) // PM
        {
            hour += 12;
            hour %= 24;
        }

        Time today = new Time();
        today.setToNow();
        today.normalize(false);

        Time time = new Time();
        time.setToNow();
        time.monthDay += day;
        time.hour = hour;
        time.minute = minute;
        time.second = 0;
        time.normalize(false);

        // If time before now is not allowed
        if (time.before(today))
        {
            time = today;
        }

        time.switchTimezone(Time.TIMEZONE_UTC);
        currentDT = time.format(ISO8601_FORMAT);
    }

    private void setPickers()
    {
        // Set to currentDT
        Time time = new Time();
        try
        {
            time.parse3339(currentDT);
            time.normalize(false);
            time.switchTimezone(Time.getCurrentTimezone());
        }
        catch(NullPointerException ex)
        {
            Log.w(TAG, "currentDT is null");
            time.setToNow();
        }
        catch(TimeFormatException ex)
        {
            Log.w(TAG, "currentDT is invalid: " + ex.getMessage());
            time.setToNow();
        }

        Time today = new Time();
        today.setToNow();
        today.normalize(false);

        int daysDiff = 0;

        Log.d(TAG, "currentDT: " + time.format3339(false) + " today: " + today.format3339(false));
        // Only if time before now is not allowed
        if(time.before(today))
        {
            time = today;
        }
        else
        {
            daysDiff = getDayDifference(today, time);
            Log.d(TAG, "Day difference between restored and today: " + daysDiff);
            // Previously selected time exceeds allowed range
            if(daysDiff>(maxDays - 1))
            {
                time = today;
                daysDiff = 0;
            }
        }

        datePicker.setValue(daysDiff);
        if(time.hour >= 12){
            hourPicker.setValue((time.hour-12)==0 ? 12 : (time.hour-12));
            periodPicker.setValue(1);
        }
        else
        {
            hourPicker.setValue((time.hour==0) ? 12 : time.hour);
            periodPicker.setValue(0);
        }
        minutePicker.setValue(time.minute);
        //currentDT = time.format(ISO8601_FORMAT);
    }

    private String formatDate()
    {
        return formatDate(null);
    }

    private String formatDate(Time time)
    {
        String result;
        if(time==null)
        {
            time = new Time(Time.TIMEZONE_UTC);
            time.setToNow();
            time.normalize(false);
        }
        else
        {
            time.switchTimezone(Time.TIMEZONE_UTC);
        }
        result = time.format(ISO8601_FORMAT);
        Log.d(TAG, "formatDate: " + result);
        return result;
    }

    @Override
    protected void onDialogClosed(boolean positiveResult)
    {
        super.onDialogClosed(positiveResult);
        if(positiveResult)
        {
            readPickers();
            if(callChangeListener(currentDT))
            {
                persistString(currentDT);
                
                setSummary(toFriendlyString(currentDT));
            }
        }
    }

    @Override
    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue)
    {
        Log.d(TAG, "onSetInitialValue()" + (String)defaultValue);
        if(restorePersistedValue)
        {
            currentDT = this.getPersistedString(DEFAULT_VALUE);
            //setSummary(currentDT);
        }
        else
        {
            if (defaultValue==null)
            {
                currentDT = formatDate();
            }
            else
            {
                currentDT = (String) defaultValue;
            }
            persistString(currentDT);
        }
    }

    @Override
    protected Object onGetDefaultValue(TypedArray a, int index)
    {
        return a.getString(index);
    }
    
    public static String toFriendlyString(String ISO8601_datestring)
    {
    	Time time = new Time();
        try
        {
            time.parse3339(ISO8601_datestring);
            time.normalize(false);
            time.switchTimezone(Time.getCurrentTimezone());
        }
        catch(NullPointerException ex)
        {
            Log.w(TAG, "ISO8601_datestring is null");
            return null;
        }
        catch(TimeFormatException ex)
        {
            Log.w(TAG, "ISO8601_datestring is invalid: " + ex.getMessage());
            return null;
        }

        return time.format(FRIENDLY_FORMAT);
    }
}
