package cc.winboll.studio.libappbase;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ListView;
import android.widget.Scroller;

/**
 * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
 * @Date 2025/11/11 20:26
 * @Describe 水平滚动 ListView 控件
 * 继承自 ListView，重写布局和测量逻辑，实现子项水平排列和滚动，替代默认垂直布局
 */
public class HorizontalListView extends ListView {
	/** 日志标签，用于当前控件的日志输出标识 */
	public static final String TAG = "HorizontalListView";

	/** 子项垂直偏移量（用于调整子项在垂直方向的位置，默认 0） */
	private int mVerticalOffset = 0;
	/** 平滑滚动控制器（用于实现水平方向的平滑滚动动画） */
	private Scroller mScroller;
	/** 所有子项总宽度（包含内边距），用于计算滚动范围 */
	private int mTotalWidth;

	/**
	 * 构造方法：仅上下文
	 * @param context 上下文（Activity/Fragment）
	 */
	public HorizontalListView(Context context) {
		super(context);
		init();
	}

	/**
	 * 构造方法：上下文 + 自定义属性
	 * @param context 上下文
	 * @param attrs 自定义属性集合（如布局文件中设置的属性）
	 */
	public HorizontalListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	/**
	 * 构造方法：上下文 + 自定义属性 + 样式属性
	 * @param context 上下文
	 * @param attrs 自定义属性集合
	 * @param defStyle 样式属性（如系统默认样式）
	 */
	public HorizontalListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init();
	}

	/**
	 * 初始化控件配置
	 * 初始化滚动控制器，设置滚动条显示状态
	 */
	private void init() {
		// 初始化平滑滚动器（上下文为当前控件所在上下文）
		mScroller = new Scroller(getContext());
		// 启用水平滚动条（默认显示）
		setHorizontalScrollBarEnabled(true);
		// 禁用垂直滚动条（水平列表无需垂直滚动）
		setVerticalScrollBarEnabled(false);
	}

	/**
	 * 设置子项垂直偏移量
	 * 用于整体调整所有子项在垂直方向的位置（如居中、偏移）
	 * @param verticalOffset 垂直偏移像素值（正数向下偏移，负数向上偏移）
	 */
	public void setVerticalOffset(int verticalOffset) {
		this.mVerticalOffset = verticalOffset;
	}

	/**
	 * 重写布局方法：实现子项水平排列
	 * 遍历所有子项，按水平方向依次布局（左对齐，叠加排列）
	 * @param changed 布局是否发生变化（true：首次布局或尺寸变化；false：重绘）
	 * @param l 控件左边界坐标
	 * @param t 控件上边界坐标
	 * @param r 控件右边界坐标
	 * @param b 控件下边界坐标
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b); // 执行父类布局逻辑（确保基础配置生效）

		int childCount = getChildCount(); // 获取当前可见子项数量
		int left = getPaddingLeft(); // 子项起始左坐标（包含控件左内边距）
		// 控件可用高度（总高度 - 上下内边距）
		int viewHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
		mTotalWidth = left; // 初始化总宽度为左内边距

		// 遍历子项，水平排列
		for (int i = 0; i < childCount; i++) {
			View child = getChildAt(i); // 获取当前子项
			int childWidth = child.getMeasuredWidth(); // 子项测量宽度
			int childHeight = child.getMeasuredHeight(); // 子项测量高度

			// 布局子项：水平方向从 left 开始，垂直方向偏移 mVerticalOffset
			child.layout(
				left,                      // 子项左边界
				mVerticalOffset,           // 子项上边界（带垂直偏移）
				left + childWidth,         // 子项右边界（左 + 宽度）
				mVerticalOffset + childHeight // 子项下边界（偏移 + 高度）
			);

			left += childWidth; // 更新下一个子项的起始左坐标
		}

		// 计算总宽度（所有子项宽度 + 左右内边距）
		mTotalWidth = left + getPaddingRight();
	}

	/**
	 * 重写测量方法：设置控件测量规则
	 * 水平方向：允许无限宽度（适应所有子项总宽度）；垂直方向：自适应内容高度
	 * @param widthMeasureSpec 父控件传递的宽度测量规格
	 * @param heightMeasureSpec 父控件传递的高度测量规格
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// 重写宽度测量规则：最大值（Integer.MAX_VALUE >> 2 避免溢出），自适应内容
		int newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
		// 重写高度测量规则：同上，自适应子项高度
		int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);

		// 执行父类测量逻辑（使用重写后的测量规格）
		super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
	}

	/**
	 * 重写滚动计算方法：实现平滑滚动
	 * 配合 Scroller 实现水平方向的平滑滚动动画（需在滚动时调用 invalidate() 触发）
	 */
	@Override
	public void computeScroll() {
		// 判断滚动是否正在进行（Scroller 计算当前滚动位置）
		if (mScroller.computeScrollOffset()) {
			// 滚动到当前计算的位置（x 轴水平滚动，y 轴固定 0）
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
			// 触发重绘，持续更新滚动状态
			postInvalidate();
		}
	}

	/**
	 * 平滑滚动到指定坐标
	 * 基于 Scroller 实现水平方向的平滑滚动（300ms 动画时长）
	 * @param x 目标 x 轴坐标（水平滚动位置）
	 * @param y 目标 y 轴坐标（固定 0，无需垂直滚动）
	 */
	public void smoothScrollTo(int x, int y) {
		// 计算滚动距离（目标坐标 - 当前滚动坐标）
		int dx = x - getScrollX();
		int dy = y - getScrollY();

		// 启动平滑滚动：起始坐标（当前滚动位置）、滚动距离、动画时长（300ms）
		mScroller.startScroll(getScrollX(), getScrollY(), dx, dy, 300);
		// 触发重绘，启动滚动动画
		invalidate();
	}

	/**
	 * 计算水平滚动总范围（用于滚动条显示比例）
	 * @return 滚动总宽度（所有子项总宽度 + 内边距）
	 */
	@Override
	public int computeHorizontalScrollRange() {
		return mTotalWidth;
	}

	/**
	 * 计算当前水平滚动偏移量（用于滚动条位置）
	 * @return 当前 x 轴滚动坐标
	 */
	@Override
	public int computeHorizontalScrollOffset() {
		return getScrollX();
	}

	/**
	 * 计算水平滚动可视范围（用于滚动条大小）
	 * @return 控件可见宽度（当前显示区域宽度）
	 */
	@Override
	public int computeHorizontalScrollExtent() {
		return getWidth();
	}

	/**
	 * 滚动到指定位置的子项（水平方向）
	 * 定位目标子项，计算滚动坐标，执行平滑滚动
	 * @param position 子项索引（从 0 开始，仅当前可见子项有效）
	 */
	public void scrollToItem(int position) {
		// 校验索引有效性（避免数组越界）
		if (position < 0 || position >= getChildCount()) {
			LogUtils.d(TAG, "无效的子项索引: " + position);
			return;
		}

		View targetView = getChildAt(position); // 获取目标子项
		int targetLeft = targetView.getLeft(); // 目标子项左边界坐标
		// 计算目标滚动坐标（子项左边界 - 控件左内边距，确保子项左对齐显示）
		int scrollX = targetLeft - getPaddingLeft();

		// 修正滚动范围（避免超出总宽度或小于 0）
		int maxScrollX = mTotalWidth - getWidth(); // 最大滚动坐标（总宽度 - 控件宽度）
		scrollX = Math.max(0, Math.min(scrollX, maxScrollX));

		// 强制重新布局和绘制（确保子项位置正确）
		requestLayout();
		invalidateViews();
		// 平滑滚动到目标坐标
		smoothScrollTo(scrollX, 0);

		// 打印滚动日志（调试用）
		LogUtils.d(TAG, String.format(
					   "滚动到子项索引: %d, 目标滚动X: %d, 总滚动范围: %d",
					   position, scrollX, computeHorizontalScrollRange()
				   ));
	}

	/**
	 * 重置滚动到起始位置（最左侧）
	 * 强制重新布局后，平滑滚动到 x=0 坐标
	 */
	public void resetScrollToStart() {
		// 强制重新布局和绘制（确保滚动位置准确）
		requestLayout();
		invalidateViews();
		// 平滑滚动到最左侧（x=0，y=0）
		smoothScrollTo(0, 0);
	}
}

