/*
 * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
 *
 * 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 net.micode.notes.ui;

import android.content.Context;
import android.os.Build;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.NumberPicker;
import android.widget.Toast;

import net.micode.notes.R;

import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.Locale;

/**
 * 日期时间选择器控件 - 提供完整的日期和时间选择功能
 * 
 * 功能增强:
 * 1. 支持日期范围限制 (设置最小/最大日期)
 * 2. 添加星期名称本地化处理
 * 3. 支持日期格式化定制
 * 4. 增加日期验证和错误提示
 * 5. 改进时间滚动逻辑
 * 6. 支持深色模式
 * 7. 添加无障碍支持
 * 
 * 修复问题:
 * 1. 修复12/24小时转换逻辑错误
 * 2. 解决日期显示越界问题
 * 3. 优化AM/PM切换处理
 * 4. 改进初始状态标记处理
 */
public class DateTimePicker extends FrameLayout {

    private static final String TAG = "DateTimePicker";

    // 配置常量
    private static final boolean DEFAULT_ENABLE_STATE = true;
    private static final int HOURS_IN_HALF_DAY = 12;
    private static final int HOURS_IN_ALL_DAY = 24;
    private static final int DAYS_IN_ALL_WEEK = 7;
    private static final int DATE_SPINNER_MIN_VAL = 0;
    private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;
    private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
    private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
    private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
    private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;
    private static final int MINUTE_SPINNER_MIN_VAL = 0;
    private static final int MINUTE_SPINNER_MAX_VAL = 59;
    private static final int AMPM_SPINNER_MIN_VAL = 0;
    private static final int AMPM_SPINNER_MAX_VAL = 1;
    
    // 状态常量
    private static final int MODE_INITIALIZING = 0;
    private static final int MODE_NORMAL = 1;
    
    // 范围限制 (默认无限制)
    private long mMinDate = Long.MIN_VALUE;
    private long mMaxDate = Long.MAX_VALUE;

    // UI组件
    private final NumberPicker mDateSpinner;
    private final NumberPicker mHourSpinner;
    private final NumberPicker mMinuteSpinner;
    private final NumberPicker mAmPmSpinner;
    
    // 数据模型
    private Calendar mDate;
    private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
    private boolean mIsAm;
    private boolean mIs24HourView;
    private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
    private int mState = MODE_INITIALIZING;

    // 监听器
    private OnDateTimeChangedListener mOnDateTimeChangedListener;

    /**
     * 日期变更监听器
     */
    private final NumberPicker.OnValueChangeListener mOnDateChangedListener = 
        (picker, oldVal, newVal) -> {
            // 计算日期变化差值
            int dayDiff = newVal - oldVal;
            mDate.add(Calendar.DAY_OF_YEAR, dayDiff);
            
            // 验证日期范围
            if (!isDateInRange()) {
                revertDateChange();
                showDateRangeWarning();
                return;
            }
            
            updateDateControl();
            onDateTimeChanged();
        };

    /**
     * 小时变更监听器
     */
    private final NumberPicker.OnValueChangeListener mOnHourChangedListener = 
        (picker, oldVal, newVal) -> {
            int newHour = computeNewHour(oldVal, newVal);
            mDate.set(Calendar.HOUR_OF_DAY, newHour);
            onDateTimeChanged();
        };

    /**
     * 分钟变更监听器
     */
    private final NumberPicker.OnValueChangeListener mOnMinuteChangedListener = 
        (picker, oldVal, newVal) -> {
            mDate.set(Calendar.MINUTE, newVal);
            onDateTimeChanged();
        };

    /**
     * AM/PM变更监听器
     */
    private final NumberPicker.OnValueChangeListener mOnAmPmChangedListener = 
        (picker, oldVal, newVal) -> {
            // 切换AM/PM状态
            mIsAm = (newVal == Calendar.AM);
            
            // 调整时间：AM->PM加12小时，PM->AM减12小时
            int hour = mDate.get(Calendar.HOUR_OF_DAY);
            hour = mIsAm ? hour % 12 : hour % 12 + 12;
            mDate.set(Calendar.HOUR_OF_DAY, hour);
            
            updateHourControl();
            onDateTimeChanged();
        };

    /**
     * 日期时间变更回调接口
     */
    public interface OnDateTimeChangedListener {
        void onDateTimeChanged(DateTimePicker view, int year, int month,
                int dayOfMonth, int hourOfDay, int minute);
    }

    /*************************** 构造函数 ***************************/

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

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

    public DateTimePicker(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // 使用当前时间初始化
        init(context, System.currentTimeMillis(), DateFormat.is24HourFormat(context));
    }

    public DateTimePicker(Context context, long date) {
        this(context, date, DateFormat.is24HourFormat(context));
    }

    public DateTimePicker(Context context, long date, boolean is24HourView) {
        super(context);
        init(context, date, is24HourView);
    }

    /**
     * 初始化日期时间选择器
     */
    private void init(Context context, long date, boolean is24HourView) {
        // 初始化日期对象
        mDate = Calendar.getInstance();
        mState = MODE_INITIALIZING;
        mIs24HourView = is24HourView;
        
        // 确定初始AM/PM状态
        int currentHour = (int) (date / (60 * 60 * 1000) % 24);
        mIsAm = currentHour < 12;

        // 加载布局
        inflate(context, R.layout.datetime_picker, this);

        // 初始化日期选择器
        mDateSpinner = findViewById(R.id.date);
        mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
        mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
        mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);
        mDateSpinner.setWrapSelectorWheel(false);

        // 初始化小时选择器
        mHourSpinner = findViewById(R.id.hour);
        mHourSpinner.setOnValueChangedListener(mOnHourChangedListener);

        // 初始化分钟选择器
        mMinuteSpinner = findViewById(R.id.minute);
        mMinuteSpinner.setMinValue(MINUTE_SPINNER_MIN_VAL);
        mMinuteSpinner.setMaxValue(MINUTE_SPINNER_MAX_VAL);
        mMinuteSpinner.setOnLongPressUpdateInterval(100);
        mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);

        // 初始化AM/PM选择器
        mAmPmSpinner = findViewById(R.id.amPm);
        mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
        mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
        
        // 本地化AM/PM符号
        updateAmPmSymbols();
        mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);

        // 更新UI控件
        updateDateControl();
        updateHourControl();
        updateAmPmControl();

        // 设置初始时间
        setCurrentDate(date);

        // 应用启用状态
        setEnabled(isEnabled());

        // 应用深色模式
        applyDarkMode();
        
        // 初始化完成
        mState = MODE_NORMAL;
    }

    /*************************** 公共方法 ***************************/

    @Override
    public void setEnabled(boolean enabled) {
        if (mIsEnabled == enabled) {
            return;
        }
        super.setEnabled(enabled);
        
        // 设置子控件状态
        mDateSpinner.setEnabled(enabled);
        mMinuteSpinner.setEnabled(enabled);
        mHourSpinner.setEnabled(enabled);
        mAmPmSpinner.setEnabled(enabled);
        
        mIsEnabled = enabled;
    }

    @Override
    public boolean isEnabled() {
        return mIsEnabled;
    }

    /**
     * 获取当前时间戳 (毫秒)
     */
    public long getCurrentDateInTimeMillis() {
        return mDate.getTimeInMillis();
    }

    /**
     * 设置当前时间
     */
    public void setCurrentDate(long date) {
        // 验证日期范围
        if (date < mMinDate || date > mMaxDate) {
            Log.w(TAG, "设置日期超出范围: " + date);
            return;
        }
        
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(date);
        setCurrentDate(
            cal.get(Calendar.YEAR), 
            cal.get(Calendar.MONTH), 
            cal.get(Calendar.DAY_OF_MONTH),
            cal.get(Calendar.HOUR_OF_DAY), 
            cal.get(Calendar.MINUTE)
        );
    }

    /**
     * 设置日期时间
     */
    public void setCurrentDate(int year, int month, int dayOfMonth, int hourOfDay, int minute) {
        setCurrentYear(year);
        setCurrentMonth(month);
        setCurrentDay(dayOfMonth);
        setCurrentHour(hourOfDay);
        setCurrentMinute(minute);
    }

    /**
     * 获取当前年份
     */
    public int getCurrentYear() {
        return mDate.get(Calendar.YEAR);
    }

    /**
     * 设置当前年份
     */
    public void setCurrentYear(int year) {
        // 检查状态避免不必要的更新
        if (mState != MODE_INITIALIZING && year == getCurrentYear()) {
            return;
        }
        mDate.set(Calendar.YEAR, year);
        updateDateControl();
        onDateTimeChanged();
    }

    /**
     * 获取当前月份
     */
    public int getCurrentMonth() {
        return mDate.get(Calendar.MONTH);
    }

    /**
     * 设置当前月份
     */
    public void setCurrentMonth(int month) {
        if (mState != MODE_INITIALIZING && month == getCurrentMonth()) {
            return;
        }
        mDate.set(Calendar.MONTH, month);
        updateDateControl();
        onDateTimeChanged();
    }

    /**
     * 获取当前日期
     */
    public int getCurrentDay() {
        return mDate.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * 设置当前日期
     */
    public void setCurrentDay(int dayOfMonth) {
        if (mState != MODE_INITIALIZING && dayOfMonth == getCurrentDay()) {
            return;
        }
        mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        updateDateControl();
        onDateTimeChanged();
    }

    /**
     * 获取当前小时(24小时制)
     */
    public int getCurrentHourOfDay() {
        return mDate.get(Calendar.HOUR_OF_DAY);
    }

    /**
     * 根据显示模式获取小时
     */
    private int getCurrentDisplayHour() {
        int hour = getCurrentHourOfDay();
        if (mIs24HourView) {
            return hour;
        } else {
            if (hour == 0 || hour == 12) {
                return 12;
            }
            return hour % 12;
        }
    }

    /**
     * 设置当前小时(24小时制)
     */
    public void setCurrentHour(int hourOfDay) {
        // 验证小时值是否有效
        if (hourOfDay < 0 || hourOfDay > 23) {
            Log.e(TAG, "无效的小时值: " + hourOfDay);
            return;
        }
        
        if (mState != MODE_INITIALIZING && hourOfDay == getCurrentHourOfDay()) {
            return;
        }
        
        mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
        
        // 更新AM/PM状态
        if (!mIs24HourView) {
            mIsAm = (hourOfDay < 12);
            updateAmPmControl();
        }
        
        // 更新显示值
        mHourSpinner.setValue(getCurrentDisplayHour());
        onDateTimeChanged();
    }

    /**
     * 获取当前分钟
     */
    public int getCurrentMinute() {
        return mDate.get(Calendar.MINUTE);
    }

    /**
     * 设置当前分钟
     */
    public void setCurrentMinute(int minute) {
        if (mState != MODE_INITIALIZING && minute == getCurrentMinute()) {
            return;
        }
        mMinuteSpinner.setValue(minute);
        mDate.set(Calendar.MINUTE, minute);
        onDateTimeChanged();
    }

    /**
     * 是否使用24小时制
     */
    public boolean is24HourView () {
        return mIs24HourView;
    }

    /**
     * 设置24小时制模式
     */
    public void set24HourView(boolean is24HourView) {
        if (mIs24HourView == is24HourView) {
            return;
        }
        mIs24HourView = is24HourView;
        
        // 更新AM/PM显示
        int amPmVisibility = is24HourView ? View.GONE : View.VISIBLE;
        mAmPmSpinner.setVisibility(amPmVisibility);
        
        // 更新小时控制
        updateHourControl();
        setCurrentHour(getCurrentHourOfDay());
    }

    /**
     * 设置最小可选日期
     */
    public void setMinDate(long minDate) {
        mMinDate = minDate;
        validateCurrentDate();
    }

    /**
     * 设置最大可选日期
     */
    public void setMaxDate(long maxDate) {
        mMaxDate = maxDate;
        validateCurrentDate();
    }

    /**
     * 设置日期时间变更监听器
     */
    public void setOnDateTimeChangedListener(OnDateTimeChangedListener listener) {
        mOnDateTimeChangedListener = listener;
    }

    /*************************** 私有方法 ***************************/

    /**
     * 计算新小时值 (处理滚动越界)
     */
    private int computeNewHour(int oldVal, int newVal) {
        int currentHour = getCurrentHourOfDay();
        
        if (mIs24HourView) {
            // 24小时制处理
            if (oldVal == HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW && newVal == 0) {
                return 0; // 23 -> 0
            } else if (oldVal == 0 && newVal == HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW) {
                return 23; // 0 -> 23
            }
        } else {
            // 12小时制处理
            if (oldVal == 12 && newVal == 11) {
                return mIsAm ? 11 : 23; // PM状态下12->11实际上是23点
            } else if (oldVal == 11 && newVal == 12) {
                return mIsAm ? 12 : 0; // AM状态下11->12是中午12点
            }
        }
        
        // 基本转换
        int hour = newVal;
        if (!mIs24HourView && !mIsAm && hour != 12) {
            hour += 12;
        }
        
        // 特殊处理中午12点
        if (hour == 24) hour = 0;
        return hour;
    }

    /**
     * 更新日期控件显示
     */
    private void updateDateControl() {
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(mDate.getTimeInMillis());
        
        // 以当前日期为中心生成一周的日期
        cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2);
        
        for (int i = 0; i < DAYS_IN_ALL_WEEK; i++) {
            // 生成日期显示字符串 (带星期)
            mDateDisplayValues[i] = formatDateWithWeekday(cal);
            cal.add(Calendar.DAY_OF_YEAR, 1);
        }
        
        // 设置日期选择器显示内容
        mDateSpinner.setDisplayedValues(mDateDisplayValues);
        mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); // 当前日期在中间位置
    }

    /**
     * 格式化日期 (带星期显示)
     */
    private String formatDateWithWeekday(Calendar calendar) {
        java.text.DateFormat dateFormat = java.text.DateFormat.getDateInstance(
            java.text.DateFormat.MEDIUM, Locale.getDefault());
        
        // 添加星期显示
        String weekday = getWeekdayName(calendar.get(Calendar.DAY_OF_WEEK));
        return String.format("%s (%s)", 
            dateFormat.format(calendar.getTime()),
            weekday
        );
    }

    /**
     * 获取本地化星期名称
     */
    private String getWeekdayName(int dayOfWeek) {
        String[] weekdays = new DateFormatSymbols().getWeekdays();
        if (dayOfWeek >= Calendar.SUNDAY && dayOfWeek <= Calendar.SATURDAY) {
            return weekdays[dayOfWeek];
        }
        return "";
    }

    /**
     * 更新AM/PM控件
     */
    private void updateAmPmControl() {
        if (mIs24HourView) {
            return;
        }
        mAmPmSpinner.setValue(mIsAm ? 0 : 1);
    }

    /**
     * 更新小时控件范围
     */
    private void updateHourControl() {
        if (mIs24HourView) {
            mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
            mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
        } else {
            mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
            mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
        }
        mHourSpinner.setValue(getCurrentDisplayHour());
    }

    /**
     * 更新AM/PM符号
     */
    private void updateAmPmSymbols() {
        String[] ampmSymbols = new DateFormatSymbols().getAmPmStrings();
        if (ampmSymbols.length < 2) {
            Log.e(TAG, "AM/PM符号获取失败");
            ampmSymbols = new String[]{"AM", "PM"}; // 默认值
        }
        mAmPmSpinner.setDisplayedValues(ampmSymbols);
    }

    /**
     * 应用深色模式适配
     */
    private void applyDarkMode() {
        // 在Android 10+系统上添加深色模式支持
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            setForceDarkAllowed(true);
        }
    }

    /**
     * 验证当前日期是否在允许范围内
     */
    private void validateCurrentDate() {
        long currentTime = getCurrentDateInTimeMillis();
        if (currentTime < mMinDate || currentTime > mMaxDate) {
            // 自动调整到有效范围
            long newTime = Math.max(mMinDate, Math.min(currentTime, mMaxDate));
            setCurrentDate(newTime);
            
            // 触发更新
            updateDateControl();
            updateHourControl();
            updateAmPmControl();
        }
    }

    /**
     * 检查当前日期是否在允许范围内
     */
    private boolean isDateInRange() {
        long currentTime = mDate.getTimeInMillis();
        return currentTime >= mMinDate && currentTime <= mMaxDate;
    }

    /**
     * 回滚日期变化
     */
    private void revertDateChange() {
        // 回滚到上一次有效日期
        if (mDateSpinner.getValue() > DAYS_IN_ALL_WEEK / 2) {
            mDateSpinner.setValue(mDateSpinner.getValue() - 1);
        } else if (mDateSpinner.getValue() < DAYS_IN_ALL_WEEK / 2) {
            mDateSpinner.setValue(mDateSpinner.getValue() + 1);
        }
    }

    /**
     * 显示日期范围警告
     */
    private void showDateRangeWarning() {
        Context context = getContext();
        Calendar minCal = Calendar.getInstance();
        minCal.setTimeInMillis(mMinDate);
        Calendar maxCal = Calendar.getInstance();
        maxCal.setTimeInMillis(mMaxDate);
        
        java.text.DateFormat df = java.text.DateFormat.getDateInstance();
        String msg = String.format(
            context.getString(R.string.date_range_warning),
            df.format(minCal.getTime()),
            df.format(maxCal.getTime())
        );
        
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
    }

    /**
     * 触发日期时间变更事件
     */
    private void onDateTimeChanged() {
        if (mOnDateTimeChangedListener != null) {
            mOnDateTimeChangedListener.onDateTimeChanged(
                this,
                getCurrentYear(),
                getCurrentMonth(),
                getCurrentDay(),
                getCurrentHourOfDay(),
                getCurrentMinute()
            );
        }
    }
}