#ifndef UI_CORE_BOX_H_
#define UI_CORE_BOX_H_

#pragma once

namespace ui 
{

class Box;
class UILIB_API Layout
{
public:
	Layout();
	Layout& operator=(const Layout& r) = delete;
	virtual ~Layout() {}

	/**
	 * @brief 设置所有者
	 * @return 无
	 */
	void SetOwner(Box* pOwner);

	/**
	 * @brief 设置浮动状态下的坐标信息
	 * @param[in] pControl 控件句柄
	 * @param[in] rcContainer 要设置的位置信息
	 * @return 返回控件最终的位置信息
	 */
	static CSize SetFloatPos(Control* pControl, UiRect rcContainer);

	/**
	 * @brief 设置布局属性
	 * @param[in] strName 要设置的属性名
	 * @param[in] strValue 要设置的属性值
	 * @return true 设置成功，false 属性不存在
	 */
	virtual bool SetAttribute(const std::wstring& strName, const std::wstring& strValue);

	/**
	 * @brief 调整内部所有控件的位置信息
	 * @param[in] items 控件列表
	 * @param[in] rc 当前容器位置信息
	 * @return 返回排列后最终盒子的宽度和高度信息
	 */
	virtual CSize ArrangeChild(const std::vector<Control*>& items, UiRect rc);

	/**
	 * @brief 根据内部子控件大小调整容器自身大小
	 * @param[in] items 控件列表
	 * @param[in] szAvailable 子控件允许的最大宽度
	 * @return 返回排列后最终盒子的宽度和高度信息
	 */
	virtual CSize AjustSizeByChild(const std::vector<Control*>& items, CSize szAvailable);

	/**
	 * @brief 获取内边距
	 * @return 返回内边距四边的大小
	 */
	virtual UiRect GetPadding() const;

	/**
	 * @brief 设置内边距，相当于设置客户区
	 * @param[in] rcPadding 内边距数据
	 * @param[in] bNeedDpiScale 是否根据 DPI 自适应，默认为 true
	 * @return 无
	 */
	virtual void SetPadding(UiRect rcPadding, bool bNeedDpiScale = true);

	/**
	 * @brief 获取子控件之间的额外边距
	 * @return 返回额外间距的数值
	 */
	virtual int GetChildMargin() const;

	/**
	 * @brief 设置子控件之间的额外边距
	 * @param[in] iMargin 要设置的边距数值
	 * @return 无
	 */
	virtual void SetChildMargin(int iMargin);

	/**
	 * @brief 获取除了内边距外的可用范围
	 * @return 返回可用范围位置信息
	 */
	UiRect GetInternalPos() const;

protected:
	UiRect m_rcPadding;
	int m_iChildMargin;
	Box *m_pOwner;
};


/////////////////////////////////////////////////////////////////////////////////////
//
class ScrollBar;

class UILIB_API Box : public Control
{
public:
	Box(Layout* pLayout = new Layout());
	Box(const Box& r);
	Box& operator=(const Box& r) = delete;
    virtual ~Box();

public:
	/// 重写父类接口，提供个性化功能。方法具体说明请查看 Control 控件             */
	virtual void SetWindow(Window* pManager, Box* pParent, bool bInit = true) override;
	virtual void SetAttribute(const std::wstring& strName, const std::wstring& strValue) override;
	virtual void SetPos(UiRect rc) override;
	virtual void HandleMessageTemplate(EventArgs& msg) override;
	virtual void PaintChild(IRenderContext* pRender, const UiRect& rcPaint) override;
	virtual void SetVisible_(bool bVisible) override;
	virtual void SetInternVisible(bool bVisible = true) override;
	virtual void SetEnabled(bool bEnabled) override;
	virtual CSize EstimateSize(CSize szAvailable) override;
	virtual Control* FindControl(FINDCONTROLPROC Proc, LPVOID pData, UINT uFlags, CPoint scrollPos = CPoint()) override;
	virtual void InvokeLoadImageCache() override;
	virtual void UnLoadImageCache() override;
	virtual void ClearImageCache() override;
	virtual UINT GetControlFlags() const override;


	/// 容器自有方法
	/**
	 * @brief 查找指定子控件
	 * @param[in] pstrSubControlName 子控件名称
	 * @return 返回子控件指针
	 */
	Control* FindSubControl(const std::wstring& pstrSubControlName);

	/**
	 * @brief 查找下一个可选控件的索引（面向 list、combo）
	 * @param[in] iIndex 指定要起始查找的索引
	 * @param[in] bForward true 为递增查找， false 为递减查找
	 * @return 下一个可选控件的索引，返回 -1 为没有可选控件
	 */
	virtual int FindSelectable(int iIndex, bool bForward = true) const;

	/**
	 * @brief 根据索引查找指定控件
	 * @param[in] iIndex 控件索引
	 * @return 返回控件指针
	 */
	virtual Control* GetItemAt(std::size_t iIndex) const;

	/**
	 * @brief 根据控件指针获取索引
	 * @param[in] pControl 控件指针
	 * @return 返回 pControl 所指向的控件索引
	 */
	virtual int GetItemIndex(Control* pControl) const;

	/**
	 * @brief 设置控件索引（内部会重新排列控件位置）
	 * @param[in] pControl 控件指针
	 * @param[in] iIndex 要设置的索引值
	 * @return 返回 true 设置成功，false 设置失败
	 */
	virtual bool SetItemIndex(Control* pControl, std::size_t iIndex);

	/**
	 * @brief 获取子控件数量
	 * @return 返回子控件数量
	 */
	virtual int GetCount() const;

	/**
	 * @brief 添加一个控件到容器中
	 * @param[in] pControl 控件指针
	 * @return 返回 true 添加成功，false 为添加失败
	 */
	virtual bool Add(Control* pControl);

	/**
	 * @brief 向指定位置添加一个控件
	 * @param[in] pControl 控件指针
	 * @param[in] iIndex 在该索引之后插入控件
	 * @return 返回 true 为添加成功，false 为添加失败
	 */
	virtual bool AddAt(Control* pControl, std::size_t iIndex);

	/**
	 * @brief 根据控件指针从容器中移除一个控件
	 * @param[in] pControl 控件的指针
	 * @return 返回 true 为移除成功，false 为移除失败（控件可能不存在）
	 */
	virtual bool Remove(Control* pControl);

	/**
	 * @brief 根据控件索引从容器中移除一个控件
	 * @param[in] iIndex 要移除的控件索引
	 * @return 返回 true 为移除成功，false 为移除失败（索引值太小或超出了子控件总数）
	 */
	virtual bool RemoveAt(std::size_t iIndex);

	/**
	 * @brief 移除所有子控件
	 * @return 无
	 */
	virtual void RemoveAll();

	/**
	 * @brief 交换子控件位置
	 * @param[in] pChild1 子控件1 指针
	 * @param[in] pChild2 子控件2 指针
	 * @return 无
	 */
	void SwapChild(Control* pChild1, Control* pChild2);

	/**
	 * @brief 重置一个子控件位置
	 * @param[in] pChild 控件指针
	 * @param[in] iIndex 要重置的位置
	 * @return 无
	 */
	void ResetChildIndex(Control* pChild, std::size_t iIndex);

	/**
	 * @brief 判断是否自动销毁
	 * @return true 为自动销毁，false 为不自动销毁
	 */
    virtual bool IsAutoDestroy() const;

	/**
	 * @brief 设置控件是否自动销毁
	 * @param[in] bAuto true 为自动销毁，false 为不自动销毁
	 * @return 无
	 */
    virtual void SetAutoDestroy(bool bAuto);

	/**
	 * @brief 判断窗口关闭后是否自动销毁
	 * @return true 为自动销毁，false 为不自动销毁
	 */
    virtual bool IsDelayedDestroy() const;

	/**
	 * @brief 设置窗口关闭后是否自动销毁
	 * @param[in] bDelayed true 为自动销毁，false 为不自动销毁
	 * @return 无
	 */
    virtual void SetDelayedDestroy(bool bDelayed);

	/**
	 * @brief 获取容器是否响应鼠标操作
	 * @return true 为响应，false 为不响应
	 */
    virtual bool IsMouseChildEnabled() const;

	/**
	 * @brief 设置容器响应鼠标操作
	 * @param[in] bEnable 设置为 true 为响应鼠标操作，设置为 false 为不响应，默认为 true
	 * @return 无
	 */
    virtual void SetMouseChildEnabled(bool bEnable = true);

	/**
	 * @brief 获取容器布局对象指针
	 * @return 返回容器关联的布局对象指针
	 */
	virtual Layout* GetLayout() const;

	/**
	 * @brief 重新关联布局对象
	 * @param[in] pLayout 布局对象指针
	 * @return 无
	 */
	virtual void RetSetLayout(Layout* pLayout);

	/**
	 * @brief 获取内边距的位置信息
	 * @return 返回内边距的位置信息
	 */
	virtual	UiRect GetPaddingPos() const;

	/**
	 * @brief 绑定事件处理函数
	 * @param[in] eventType 事件类型
	 * @return callback 指定回调函数
	 */
	void AttachBubbledEvent(EventType eventType, const EventCallback& callback)	{ OnBubbledEvent[eventType] += callback; }

private:
	friend WindowBuilder;

	/**
	 * @brief 绑定 XML 中编写的 Event 和 BubbleEvent 事件的处理函数
	 * @param[in] eventType 事件类型
	 * @return callback 指定回调函数
	 */
	void AttachXmlBubbledEvent(EventType eventType, const EventCallback& callback) { OnXmlBubbledEvent[eventType] += callback; }

	EventMap OnXmlBubbledEvent;

protected:
	std::unique_ptr<Layout> m_pLayout;
	bool m_bAutoDestroy;
	bool m_bDelayedDestroy;
	bool m_bMouseChildEnabled;
	std::vector<Control*> m_items;
	EventMap OnBubbledEvent;
};

 /// 带有垂直或水平滚动条的容器，使容器可以容纳更多内容
class UILIB_API ScrollableBox : public Box
{
public:
	ScrollableBox(Layout* pLayout);
	ScrollableBox(const ScrollableBox& r);
	ScrollableBox& operator=(const ScrollableBox& r) = delete;

	virtual void SetAttribute(const std::wstring& pstrName, const std::wstring& pstrValue) override;
	virtual void SetPos(UiRect rc) override;
	virtual void HandleMessage(EventArgs& event) override;
	virtual bool MouseEnter(EventArgs& msg) override;
	virtual bool MouseLeave(EventArgs& msg) override;
	virtual void PaintChild(IRenderContext* pRender, const UiRect& rcPaint) override;
	virtual void SetMouseEnabled(bool bEnable = true) override;
	virtual void SetWindow(Window* pManager, Box* pParent, bool bInit) override;
	virtual Control* FindControl(FINDCONTROLPROC Proc, LPVOID pData, UINT uFlags, CPoint scrollPos = CPoint()) override;
	virtual void ClearImageCache() override;

	/**
	 * @brief 获取滚动条位置
	 * @return 返回滚动条的位置信息
	 */
	virtual CSize GetScrollPos() const;

	/**
	 * @brief 获取滚动条的范围
	 * @return 返回滚动条的范围信息
	 */
	virtual CSize GetScrollRange() const;

	/**
	 * @brief 设置滚动条位置
	 * @param[in] szPos 要设置的位置数据
	 * @return 无
	 */
	virtual void SetScrollPos(CSize szPos);

	/**
	 * @brief 设置滚动条 Y 轴坐标
	 * @param[in] y 要设置的 Y 轴坐标数值
	 * @return 无
	 */
	virtual void SetScrollPosY(int y);
	
	/**
	 * @brief 设置滚动条 X 轴坐标
	 * @param[in] x 要设置的 X 轴坐标数值
	 * @return 无
	 */
    virtual void SetScrollPosX(int x);
	
	/**
	 * @brief 向上滚动滚动条
	 * @param[in] deltaValue 滚动距离，默认为 DUI_NOSET_VALUE
 	 * @param[in] withAnimation 是否附带动画效果，默认为 true
	 * @return 无
	 */
	virtual void LineUp(int detaValue = DUI_NOSET_VALUE, bool withAnimation = true);
	
	/**
	 * @brief 向下滚动滚动条
	 * @param[in] deltaValue 滚动距离，默认为 DUI_NOSET_VALUE
 	 * @param[in] withAnimation 是否附带动画效果，默认为 true
	 * @return 无
	 */
	virtual void LineDown(int detaValue = DUI_NOSET_VALUE, bool withAnimation = true);
	
	/**
	 * @brief 向左滚动滚动条
	 * @param[in] deltaValue 滚动距离，默认为 DUI_NOSET_VALUE
	 * @return 无
	 */
    virtual void LineLeft(int detaValue = DUI_NOSET_VALUE);
	
	/**
	 * @brief 向右滚动滚动条
	 * @param[in] deltaValue 滚动距离，默认为 DUI_NOSET_VALUE
	 * @return 无
	 */
    virtual void LineRight(int detaValue = DUI_NOSET_VALUE);
	/**
	 * @brief 向上滚动一个页面大小的距离
	 * @return 无
	 */
	virtual void PageUp();

	/**
	 * @brief 向下滚动一个页面大小的距离
	 * @return 无
	 */
	virtual void PageDown();

	/**
	 * @brief 回到滚动条最上方
	 * @return 无
	 */
	virtual void HomeUp();

	/**
	 * @brief 滚动到最下方位置
	 * @param[in] arrange 是否重置滚动条位置，默认为 true
	 * @param[in] withAnimation 是否包含动画特效，默认为 true
	 * @return 无
	 */
	virtual void EndDown(bool arrange = true, bool withAnimation = true);
	/**
	 * @brief 向左滚动一个页面大小的距离
	 * @return 无
	 */
	virtual void PageLeft();

	/**
	 * @brief 向右滚动一个页面大小的距离
	 * @return 无
	 */
	virtual void PageRight();

	/**
	 * @brief 滚动到最左侧
	 * @return 无
	 */
	virtual void HomeLeft();

	/**
	 * @brief 滚动到最右侧
	 * @return 无
	 */
	virtual void EndRight();

	/**
	 * @brief 触摸向上滚动（响应 WM_TOUCH 消息）
	 * @param[in] deltaValue 滚动距离
	 * @return 无
	 */
	virtual void TouchUp(int deltaValue);

	/**
	 * @brief 触摸向下滚动（响应 WM_TOUCH 消息）
	 * @param[in] deltaValue 滚动距离
	 * @return 无
	 */
	virtual void TouchDown(int deltaValue);

	/**
	 * @brief 启用滚动条
	 * @param[in] bEnableVertical 是否启用垂直滚动条，默认为 true
	 * @param[in] bEnableHorizontal 是否启用水平滚动条，默认为 true
	 * @return 无
	 */
	virtual void EnableScrollBar(bool bEnableVertical = true, bool bEnableHorizontal = false);

	/**
	 * @brief 获取垂直滚动条对象指针
	 * @return 返回垂直滚动条对象指针
	 */
	virtual ScrollBar* GetVerticalScrollBar() const;

	/**
	 * @brief 获取水平滚动条对象指针
	 * @return 返回水平滚动条对象指针
	 */
	virtual ScrollBar* GetHorizontalScrollBar() const;

	/**
	 * @brief 待补充
	 * @param[in] 待补充
	 * @return 待补充
	 */
	virtual void ProcessVScrollBar(UiRect rc, int cyRequired);

	/**
	 * @brief 待补充
	 * @param[in] 待补充
	 * @return 待补充
	 */
	virtual void ProcessHScrollBar(UiRect rc, int cxRequired);

	/**
	 * @brief 判断垂直滚动条是否有效
	 * @return 返回 true 表示有效，否则 false 为无效
	 */
	bool IsVScrollBarValid() const;

	/**
	 * @brief 判断水平滚动条是否有效
	 * @return 返回 true 表示有效，否则 false 为无效
	 */
	bool IsHScrollBarValid() const;

	/**
	 * @brief 待补充
	 * @param[in] 待补充
	 * @return 待补充
	 */
	void ReomveLastItemAnimation();

	/**
	 * @brief 待补充
	 * @param[in] 待补充
	 * @return 待补充
	 */
	void PlayRenderOffsetYAnimation(int nRenderY);

	/**
	 * @brief 是否已经在底部
	 * @return 返回 true 表示已经在底部，否则为 false
	 */
	bool IsAtEnd() const;

	/**
	 * @brief 是否锁定到底部
	 * @return 回 true 表示锁定在底部，否则为 false
	 */
	bool IsHoldEnd() const;

	/**
	 * @brief 设置滚动条是否始终锁定到底部位置
	 * @param[in] bHoldEnd 设置 true 表示锁定，false 为不锁定
	 * @return 无
	 */
	void SetHoldEnd(bool bHoldEnd);
	
	/**
	 * @brief 获取垂直滚动条滚动步长
	 * @return 返回滚动步长
	 */
	int GetVerScrollUnitPixels() const;

	/**
	 * @brief 设置垂直滚动条滚动步长
	 * @param[in] nUnitPixels 要设置的步长
	 * @return 无
	 */
	void SetVerScrollUnitPixels(int nUnitPixels);

	/**
	* @brief 获取横向滚动条滚动步长
	* @return 返回滚动步长
	*/
	int GetHorScrollUnitPixels() const;

	/**
	* @brief 设置横向滚动条滚动步长
	* @param[in] nUnitPixels 要设置的步长
	* @return 无
	*/
	void SetHorScrollUnitPixels(int nUnitPixels);

	/**
	 * @brief 获取容器的滚动条是否悬浮在子控件上面
	 * @return 返回 true 表示悬浮在滚动条上，否则为 false
	 */
	bool GetScrollBarFloat() const;

	/**
	 * @brief 设置容器的滚动条是否悬浮在子控件上面
	 * @param[in] bScrollBarFloat true 表示悬浮在滚动条上，false 表示不悬浮在控件上
	 * @return 无
	 */
	void SetScrollBarFloat(bool bScrollBarFloat);

	/**
	 * @brief 获取滚动条的外边距
	 * @return 返回边距信息 
	 */
	UiRect GetScrollBarPadding() const;

	/**
	 * @brief 设置滚动条的外边距，可以让滚动条不占满容器
	 * @param[in] rcScrollBarPadding 要设置的边距
	 * @return 无
	 */
	void SetScrollBarPadding(UiRect rcScrollBarPadding);

	/**
	 * @brief 待补充
	 * @param[in] 待补充
	 * @return 待补充
	 */
	bool GetDefaultDisplayScrollbar() const;

	/**
	 * @brief 待补充
	 * @param[in] 待补充
	 * @return 待补充
	 */
	void SetDefaultDisplayScrollbar(bool bDefaultDisplay);

	/**
	 * @brief 监听滚动条位置变化事件
	 * @param[in] callback 有变化后通知的回调函数
	 * @return 无
	 */
	void AttachScrollChange(const EventCallback& callback) { OnEvent[kEventScrollChange] += callback; }
protected:

	/**
	 * @brief 计算所需的尺寸
	 * @param[in] rc 当前位置信息
	 * @return 返回所需尺寸大小
	 */
	virtual CSize CalcRequiredSize(const UiRect& rc);

	/**
	 * @brief 加载图片缓存，仅供 ScrollableBox 内部使用
	 * @param[in] bFromTopLeft 暂无意义
	 * @return 无
	 */
	void LoadImageCache(bool bFromTopLeft);
private:
	/**
	 * @brief 待补充
	 * @param[in] 待补充
	 * @return 待补充
	 */
	void SetPosInternally(UiRect rc);

protected:
	std::unique_ptr<ScrollBar> m_pVerticalScrollBar;
	std::unique_ptr<ScrollBar> m_pHorizontalScrollBar;

	int m_nVerScrollUnitPixels;
    int m_nHerScrollUnitPixels;
	bool m_bScrollProcess; // 防止SetPos循环调用
	bool m_bHoldEnd;
	bool m_bScrollBarFloat;
	bool m_bDefaultDisplayScrollbar;
	UiRect m_rcScrollBarPadding;

	CPoint m_ptLastTouchPos;
	AnimationPlayer m_scrollAnimation;
	AnimationPlayer m_renderOffsetYAnimation;
};

} // namespace ui

#endif // UI_CORE_BOX_H_
