--## Notes: 自动记录所有68+级角色的商业技能专注、地下堡、装等及赛季货币等情况  ## Author: DFCN  ## Version: 1.60
local DFCNSL = {}
if GetLocale() == "zhCN" then
	DFCNSL["SECONDS"] = "秒"
	DFCNSL["MINUTES"] = "分钟"
	DFCNSL["HOURS"] = "小时"
	DFCNSL["DAYS"] = "天"
	DFCNSL["IN_BAG"] = "背包中"
	DFCNSL["USED"] = "已使用"
	DFCNSL["NOT_OBTAINED"] = "未获得"
	DFCNSL["NO_PROFESSION"] = "无专业"
	DFCNSL["PROFESSION_SLOT"] = "专业%d"
	DFCNSL["NOT_LEARNED"] = "未学习当前版本技能"
	DFCNSL["CHARACTER_NAME"] = "角色名称"
	DFCNSL["PROF_CONCENTRATION"] = "技能专注"
	DFCNSL["KEY_ACQUISITION"] = "钥匙"
	DFCNSL["SHARD_ACQUISITION"] = "碎片获取"
	DFCNSL["DELVE_COUNT"] = "宝库"
	DFCNSL["TREASURE_MAP"] = "藏宝图"
	DFCNSL["GOLDEN_CACHE"] = "藏匿物"
	DFCNSL["ITEM_LEVEL"] = "装等"
	DFCNSL["OVERALL_ITEM_LEVEL"] = "平均装等"
	DFCNSL["MANUAL_UPDATE"] = "手动更新"
	DFCNSL["SWITCH_TO_CURRENCY"] = "切换到货币显示"
	DFCNSL["SWITCH_TO_DELVES"] = "切换到地堡信息"
    DFCNSL["MULTI_CHAR_MANAGER"] = "|cff00ff00[角色]|r数据管理"
	DFCNSL["MULTI_CHAR_MANAGER_SHORT"] = "多角色管理器"
	DFCNSL["DELVES_STATS"] = "地下堡信息统计"
	DFCNSL["CURRENCY_STATS"] = "赛季货币统计"
	DFCNSL["FONT_SIZE"] = "字体"
	DFCNSL["CLICK_OR_SPACE"] = "点击或按空格键切换视图模式"
	DFCNSL["CHARACTER_RESET"] = "多角色管理器: 已为%d个角色重置本周数据！"
	DFCNSL["ADDON_LOADED"] = "多角色管理器已加载"
	DFCNSL["DATABASE_CREATED"] = "已创建新的插件角色数据库 (v%s)"
	DFCNSL["DATA_UPDATED"] = "当前角色数据已手动强制更新"
	DFCNSL["MINIMAP_SHOWN"] = "小地图图标已显示"
	DFCNSL["MINIMAP_HIDDEN"] = "小地图图标已隐藏"
	DFCNSL["CHARACTER_DELETED"] = "已删除角色: %s"
	DFCNSL["SHOW_MINIMAP_ICON"] = "显示小地图图标"
	DFCNSL["OPEN_PANEL"] = "打开角色管理面板"
	DFCNSL["CLOSE_PANEL"] = "关闭角色管理面板"
	DFCNSL["MACRO_COMMAND"] = "宏调用指令: /dfcnchar"
	DFCNSL["ORIGINAL_DOWNLOAD"] = "原始发布地址："
	DFCNSL["CLICK_TO_COPY"] = "点击选中网址，按Ctrl+C复制"
	DFCNSL["CURRENCY"] = "货币"
	DFCNSL["CURRENCY_LV1"] = "冒险纹章"
	DFCNSL["CURRENCY_CATACLYSM"] = "晦幽铸币"
	DFCNSL["CURRENCY_LV2"] = "老兵纹章"
	DFCNSL["CURRENCY_LV3"] = "勇士纹章"
	DFCNSL["CURRENCY_LV4"] = "英雄纹章"
	DFCNSL["CURRENCY_LV5"] = "神话纹章"
	DFCNSL["CURRENCY_STARLIGHT"] = "光耀火花"
	DFCNSL["CURRENCY_FRAGMENT"] = "法力熔剂"
	DFCNSL["TOOLTIP_CHARACTER_NAME"] = "角色名称 - 服务器"
	DFCNSL["TOOLTIP_PROF_CONCENTRATION"] = "至暗之夜商业技能(专注点)"
	DFCNSL["TOOLTIP_KEY_ACQUISITION"] = "钥匙总数量 | 本周获取数量/4"
	DFCNSL["TOOLTIP_SHARD_ACQUISITION"] = "已获取钥匙碎片数量 / 赛季上限"
	DFCNSL["TOOLTIP_DELVE_COUNT"] = "8层以上合计 | 宝库激活格数"
	DFCNSL["TOOLTIP_TREASURE_MAP"] = "地下堡藏宝图获取状态"
	DFCNSL["TOOLTIP_GOLDEN_CACHE"] = "地下堡鎏金藏匿物获取状态"
	DFCNSL["TOOLTIP_ITEM_LEVEL"] = "角色披风装等"
	DFCNSL["CURRENCY_MANA_CRYSTAL"] = "法力水晶"
	DFCNSL["TOOLTIP_MANA_CRYSTAL"] = "未被污染的法力水晶货币数量"
	DFCNSL["TOOLTIP_OVERALL_ITEM_LEVEL"] = "角色全身平均装等"
	DFCNSL["LEFT_CLICK"] = "左键: 打开/关闭面板"
	DFCNSL["COMMAND_OPEN_CLOSE"] = "|cFFFFFF00/dfcnchar|r - 打开/关闭面板"
	DFCNSL["COMMAND_MINIMAP_ON"] = "|cFFFFFF00/dfcnchar minimap on|r - 显示小地图图标"
	DFCNSL["COMMAND_MINIMAP_OFF"] = "|cFFFFFF00/dfcnchar minimap off|r - 隐藏小地图图标"
	DFCNSL["MACRO_COMMAND"] = "宏命令: |cFFFFFF00/dfcnchar|r"
	DFCNSL["MINIMAP_ICON"] = "小地图图标: |cFFFFFF00/dfcnchar minimap on/off|r"
	DFCNSL["ESC_TO_CLOSE"] = "面板打开时按ESC可关闭"
	DFCNSL["CONCENTRATION_VALUE"] = "专注值"
	DFCNSL["NO_CONCENTRATION_TIP"] = "采集专业无专注值"
	DFCNSL["CONCENTRATION_FULL"] = "专注已满"
	DFCNSL["CURRENT_VALUE"] = "当前值"
	DFCNSL["TIME_TO_FULL"] = "专注回满还需"
	DFCNSL["ESTIMATED_FULL_TIME"] = "预计回满时间"
	DFCNSL["LAST_LOGIN_TIME"] = "最后上线时间"
	DFCNSL["CURRENCY_TOOLTIP_UNKNOWN"] = "尚未登录该角色，数据未知"
	DFCNSL["CURRENCY_TOOLTIP_LOGIN"] = "请登录该角色以自动获取最新数据"
	DFCNSL["UNKNOWN"] = "未知"
	DFCNSL["FONT_SIZE_SLIDER"] = "字体大小: %s"
	DFCNSL["FONT_SIZE_POSITIVE"] = "字体大小: +%d"
	DFCNSL["FONT_SIZE_NEGATIVE"] = "字体大小: %d"
	DFCNSL["FONT_SIZE_ZERO"] = "字体大小: 0"
	DFCNSL["DELETE_CHAR_CONFIRM"] = "确定要删除 |cFFFFD100%s|r 的数据吗？"
	DFCNSL["DELETE"] = "删除"
	DFCNSL["CANCEL"] = "取消"
	DFCNSL["DELETE_CHAR_TOOLTIP"] = "删除此角色数据"
	DFCNSL["GREAT_VAULT_NOT_COLLECTED"] = "宏伟宝库未领取"
	DFCNSL["QUANTITY_HELD"] = "持有数量"
	DFCNSL["SEASON_CAP"] = "赛季上限"
	DFCNSL["HOLDING_CAP"] = "持有上限"
	DFCNSL["SEASON_EARNED"] = "赛季获取"
	DFCNSL["WEEKLY_EARNED"] = "每周获取"
	DFCNSL["REACHED_CAP"] = "已达上限"
	DFCNSL["NEED_MORE"] = "再获取 %d 达到赛季上限"
	DFCNSL["HOLD_MORE"] = "再获取 %d 达到持有上限"
	DFCNSL["WEEKLY_CAP"] = "每周上限"
	DFCNSL["REACHED_WEEKLY_CAP"] = "已达到每周上限"
	DFCNSL["NEED_MORE_WEEKLY"] = "再获取 %d 达到每周上限"
	DFCNSL["INFO"] = "信息"
	DFCNSL["UPGRADE"] = "数据库重置"
	DFCNSL["RESET"] = "重置"
	DFCNSL["DATA_AUTO_UPDATED"] = "(数据已自动更新)"
	DFCNSL["DATA_MAY_BE_OUTDATED"] = "(数据可能已过期)"
	DFCNSL["VIEW_ONLY_SUPPORT"] = "(仅支持查看)"
	DFCNSL["CURRENT_REGION_FORMAT"] = "当前为%s，每%s7点自动重置数据"
	DFCNSL["CHARACTER_LEVEL_68_PLUS"] = "当前角色78级以上"
	DFCNSL["CHARACTER_LEVEL_BELOW_68"] = "当前角色不足78级"
	DFCNSL["REGION_US"] = "[美服]"
	DFCNSL["REGION_KR"] = "[韩服]"
	DFCNSL["REGION_EU"] = "[欧服]"
	DFCNSL["REGION_TW"] = "[台服]"
	DFCNSL["REGION_CN"] = "[国服]"
	DFCNSL["REGION_PTR"] = "[PTR]"
	DFCNSL["SUNDAY"] = "周日"
	DFCNSL["MONDAY"] = "周一"
	DFCNSL["TUESDAY"] = "周二"
	DFCNSL["WEDNESDAY"] = "周三"
	DFCNSL["THURSDAY"] = "周四"
	DFCNSL["FRIDAY"] = "周五"
	DFCNSL["SATURDAY"] = "周六"
	DFCNSL["DATABASE_CLEARED"] = "检测到游戏大版本更新，所有角色数据已重置。"
	DFCNSL["REST_XP"] = "双倍经验"
	DFCNSL["TIME_TO_FULL_REST"] = "满双倍时间："
	DFCNSL["REST_XP_FULL"] = "双倍经验已满！"
	DFCNSL["MidnightKeyword"] = "至暗之夜"
	DFCNSL["HOTKEY_SETTING"] = "切换视图热键：穿透生效，不影响原键功能"
	DFCNSL["PRESS_NEW_HOTKEY"] = "按下新热键..."
	DFCNSL["CLICK_BUTTON_AGAIN_TO_CANCEL"] = "再次点击按钮取消"
	DFCNSL["MODIFIER_NOT_ALLOWED"] = "不能设置为修饰键（Ctrl/Alt/Shift）"
	DFCNSL["HOTKEY_SET_SUCCESS"] = "热键设置成功"
	DFCNSL["CURRENCY_VOID_CORE"] = "虚空核心"
	DFCNSL["CURRENCY_DUDU"] = "钝顿碎片"
	DFCNSL["LEFT_CLICK_DRAG"] = "左键拖拽可移动边条位置"
	DFCNSL["DOCK_MODE"] = "启用边条模式"

elseif GetLocale() == "zhTW" then
	--This text has been translated by AI:DeepSeek
	DFCNSL["SECONDS"] = "秒"
	DFCNSL["MINUTES"] = "分鐘"
	DFCNSL["HOURS"] = "小時"
	DFCNSL["DAYS"] = "天"
	DFCNSL["IN_BAG"] = "背包中"
	DFCNSL["USED"] = "已使用"
	DFCNSL["NOT_OBTAINED"] = "未獲得"
	DFCNSL["NO_PROFESSION"] = "無專業"
	DFCNSL["PROFESSION_SLOT"] = "專業%d"
	DFCNSL["NOT_LEARNED"] = "未學習目前版本技能"
	DFCNSL["CHARACTER_NAME"] = "角色名稱"
	DFCNSL["PROF_CONCENTRATION"] = "技能專注"
	DFCNSL["KEY_ACQUISITION"] = "鑰匙"
	DFCNSL["SHARD_ACQUISITION"] = "裂片獲取"
	DFCNSL["DELVE_COUNT"] = "寶庫"
	DFCNSL["TREASURE_MAP"] = "藏寶圖"
	DFCNSL["GOLDEN_CACHE"] = "儲物箱"
	DFCNSL["ITEM_LEVEL"] = "物等"
	DFCNSL["OVERALL_ITEM_LEVEL"] = "平均物等"
	DFCNSL["MANUAL_UPDATE"] = "手動更新"
	DFCNSL["SWITCH_TO_CURRENCY"] = "切換到通貨資訊"
	DFCNSL["SWITCH_TO_DELVES"] = "切換到探究資訊"
	DFCNSL["MULTI_CHAR_MANAGER"] = "多角色管理器"
	DFCNSL["MULTI_CHAR_MANAGER_SHORT"] = "多角色管理器"
	DFCNSL["DELVES_STATS"] = "探究資訊統計"
	DFCNSL["CURRENCY_STATS"] = "賽季通貨統計"
	DFCNSL["FONT_SIZE"] = "字體"
	DFCNSL["CLICK_OR_SPACE"] = "點擊或按空格鍵切換視圖模式"
	DFCNSL["CHARACTER_RESET"] = "多角色管理器: 已為%d個角色重置本週資料！"
	DFCNSL["ADDON_LOADED"] = "多角色管理器已加載"
	DFCNSL["DATABASE_CREATED"] = "已建立新的插件角色資料庫 (v%s)"
	DFCNSL["DATA_UPDATED"] = "當前角色資料已手動強制更新"
	DFCNSL["MINIMAP_SHOWN"] = "小地圖圖示已顯示"
	DFCNSL["MINIMAP_HIDDEN"] = "小地圖圖示已隱藏"
	DFCNSL["CHARACTER_DELETED"] = "已刪除角色: %s"
	DFCNSL["SHOW_MINIMAP_ICON"] = "顯示小地圖圖示"
	DFCNSL["OPEN_PANEL"] = "打開角色管理面板"
	DFCNSL["CLOSE_PANEL"] = "關閉角色管理面板"
	DFCNSL["MACRO_COMMAND"] = "巨集調用指令: /dfcnchar"
	DFCNSL["ORIGINAL_DOWNLOAD"] = "原始發布網址："
	DFCNSL["CLICK_TO_COPY"] = "點擊選中網址，按Ctrl+C複製"
	DFCNSL["CURRENCY"] = "通貨"
	DFCNSL["CURRENCY_LV1"] = "冒險紋章"
	DFCNSL["CURRENCY_CATACLYSM"] = "地底幣"
	DFCNSL["CURRENCY_LV2"] = "精兵紋章"
	DFCNSL["CURRENCY_LV3"] = "勇士紋章"
	DFCNSL["CURRENCY_LV4"] = "英雄紋章"
	DFCNSL["CURRENCY_LV5"] = "傳奇紋章"
	DFCNSL["CURRENCY_STARLIGHT"] = "聖輝火花"
	DFCNSL["CURRENCY_FRAGMENT"] = "法力通量"
	DFCNSL["TOOLTIP_CHARACTER_NAME"] = "角色名稱 - 伺服器"
	DFCNSL["TOOLTIP_PROF_CONCENTRATION"] = "至暗之夜商業技能(專注點)"
	DFCNSL["TOOLTIP_KEY_ACQUISITION"] = "鑰匙總數量 | 本週獲取數量/4"
	DFCNSL["TOOLTIP_SHARD_ACQUISITION"] = "已獲取鑰匙裂片數量 / 賽季獲取上限"
	DFCNSL["TOOLTIP_DELVE_COUNT"] = "8級以上次數 | 寶庫解鎖欄位"
	DFCNSL["TOOLTIP_TREASURE_MAP"] = "探究藏寶圖獲取狀態"
	DFCNSL["TOOLTIP_GOLDEN_CACHE"] = "探究鍍金儲物箱獲取狀態"
	DFCNSL["TOOLTIP_ITEM_LEVEL"] = "角色披風裝等"
	DFCNSL["CURRENCY_MANA_CRYSTAL"] = "法力水晶"
	DFCNSL["TOOLTIP_MANA_CRYSTAL"] = "未受污染的法力水晶貨幣數量"
	DFCNSL["TOOLTIP_OVERALL_ITEM_LEVEL"] = "角色全身裝等"
	DFCNSL["LEFT_CLICK"] = "左鍵: 打開/關閉面板"
	DFCNSL["COMMAND_OPEN_CLOSE"] = "|cFFFFFF00/dfcnchar|r - 開啟/關閉面板"
	DFCNSL["COMMAND_MINIMAP_ON"] = "|cFFFFFF00/dfcnchar minimap on|r - 顯示小地圖圖示"
	DFCNSL["COMMAND_MINIMAP_OFF"] = "|cFFFFFF00/dfcnchar minimap off|r - 隱藏小地圖圖示"
	DFCNSL["MACRO_COMMAND"] = "巨集命令: |cFFFFFF00/dfcnchar|r"
	DFCNSL["MINIMAP_ICON"] = "小地圖圖示: |cFFFFFF00/dfcnchar minimap on/off|r"
	DFCNSL["ESC_TO_CLOSE"] = "面板打開時按ESC可關閉"
	DFCNSL["CONCENTRATION_VALUE"] = "專注值"
	DFCNSL["NO_CONCENTRATION_TIP"] = "採集專業無專注值"
	DFCNSL["CONCENTRATION_FULL"] = "專注已滿"
	DFCNSL["CURRENT_VALUE"] = "當前值"
	DFCNSL["TIME_TO_FULL"] = "專注回滿還需"
	DFCNSL["ESTIMATED_FULL_TIME"] = "預計回滿時間"
	DFCNSL["LAST_LOGIN_TIME"] = "最後上線時間"
	DFCNSL["CURRENCY_TOOLTIP_UNKNOWN"] = "尚未登入該角色，資料未知"
	DFCNSL["CURRENCY_TOOLTIP_LOGIN"] = "請登錄該角色以自動獲取最新資料"
	DFCNSL["UNKNOWN"] = "未知"
	DFCNSL["FONT_SIZE_SLIDER"] = "字體大小: %s"
	DFCNSL["FONT_SIZE_POSITIVE"] = "字體大小: +%d"
	DFCNSL["FONT_SIZE_NEGATIVE"] = "字體大小: %d"
	DFCNSL["FONT_SIZE_ZERO"] = "字體大小: 0"
	DFCNSL["DELETE_CHAR_CONFIRM"] = "確定要刪除 |cFFFFD100%s|r 的資料嗎？"
	DFCNSL["DELETE"] = "刪除"
	DFCNSL["CANCEL"] = "取消"
	DFCNSL["DELETE_CHAR_TOOLTIP"] = "刪除此角色資料"
	DFCNSL["GREAT_VAULT_NOT_COLLECTED"] = "偉業寶庫未領取"
	DFCNSL["QUANTITY_HELD"] = "持有數量"
	DFCNSL["SEASON_CAP"] = "賽季上限"
	DFCNSL["HOLDING_CAP"] = "持有上限"
	DFCNSL["SEASON_EARNED"] = "賽季獲取"
	DFCNSL["WEEKLY_EARNED"] = "每周獲取"
	DFCNSL["REACHED_CAP"] = "已達上限"
	DFCNSL["NEED_MORE"] = "再獲取 %d 達到賽季上限"
	DFCNSL["HOLD_MORE"] = "再獲取 %d 達到持有上限"
	DFCNSL["WEEKLY_CAP"] = "每周上限"
	DFCNSL["REACHED_WEEKLY_CAP"] = "已達到每周上限"
	DFCNSL["NEED_MORE_WEEKLY"] = "再獲取 %d 達到每周上限"
	DFCNSL["INFO"] = "資訊"
	DFCNSL["UPGRADE"] = "數據庫重置"
	DFCNSL["RESET"] = "重置"
	DFCNSL["DATA_AUTO_UPDATED"] = "(資料已自動更新)"
	DFCNSL["DATA_MAY_BE_OUTDATED"] = "(資料可能已過期)"
	DFCNSL["VIEW_ONLY_SUPPORT"] = "(僅支援檢視)"
	DFCNSL["CURRENT_REGION_FORMAT"] = "目前為%s，每%s7點自動重置資料"
	DFCNSL["CHARACTER_LEVEL_68_PLUS"] = "目前角色78級以上"
	DFCNSL["CHARACTER_LEVEL_BELOW_68"] = "目前角色不足78級"
	DFCNSL["REGION_US"] = "[美服]"
	DFCNSL["REGION_KR"] = "[韓服]"
	DFCNSL["REGION_EU"] = "[歐服]"
	DFCNSL["REGION_TW"] = "[台服]"
	DFCNSL["REGION_CN"] = "[陸服]"
	DFCNSL["REGION_PTR"] = "[PTR]"
	DFCNSL["SUNDAY"] = "週日"
	DFCNSL["MONDAY"] = "週一"
	DFCNSL["TUESDAY"] = "週二"
	DFCNSL["WEDNESDAY"] = "週三"
	DFCNSL["THURSDAY"] = "週四"
	DFCNSL["FRIDAY"] = "週五"
	DFCNSL["SATURDAY"] = "週六"
	DFCNSL["DATABASE_CLEARED"] = "偵測到遊戲大版本更新，所有角色資料已重置。"
	DFCNSL["REST_XP"] = "雙倍經驗"
	DFCNSL["TIME_TO_FULL_REST"] = "滿雙倍時間："
	DFCNSL["REST_XP_FULL"] = "雙倍經驗已滿！"
	DFCNSL["MidnightKeyword"] = "至暗之夜"
	DFCNSL["HOTKEY_SETTING"] = "切換視圖熱鍵：穿透生效，不影響原鍵功能"
	DFCNSL["PRESS_NEW_HOTKEY"] = "按下新熱鍵..."
	DFCNSL["CLICK_BUTTON_AGAIN_TO_CANCEL"] = "再次點擊按鈕取消"
	DFCNSL["MODIFIER_NOT_ALLOWED"] = "不能設定為修飾鍵（Ctrl/Alt/Shift）"
	DFCNSL["HOTKEY_SET_SUCCESS"] = "熱鍵設定成功"
	DFCNSL["CURRENCY_VOID_CORE"] = "虛無之核"
	DFCNSL["CURRENCY_DUDU"] = "敦敦裂片"
	DFCNSL["LEFT_CLICK_DRAG"] = "左鍵拖曳以移動邊條"
	DFCNSL["DOCK_MODE"] = "啟用邊條模式"
else
	--This text has been translated by AI:DeepSeek
	DFCNSL["SECONDS"] = "s"
	DFCNSL["MINUTES"] = "min"
	DFCNSL["HOURS"] = "h"
	DFCNSL["DAYS"] = "d"
	DFCNSL["IN_BAG"] = "In Bag"
	DFCNSL["USED"] = "Used"
	DFCNSL["NOT_OBTAINED"] = "Not Obtained"
	DFCNSL["NO_PROFESSION"] = "No Profession"
	DFCNSL["PROFESSION_SLOT"] = "Profession %d"
	DFCNSL["NOT_LEARNED"] = "Not learned (current expansion)"
	DFCNSL["CHARACTER_NAME"] = "Character"
	DFCNSL["PROF_CONCENTRATION"] = "Conc"
	DFCNSL["KEY_ACQUISITION"] = "Key"
	DFCNSL["SHARD_ACQUISITION"] = "Key Shard"
	DFCNSL["DELVE_COUNT"] = "Vault"
	DFCNSL["TREASURE_MAP"] = "Bounty"
	DFCNSL["GOLDEN_CACHE"] = "Stash"
	DFCNSL["ITEM_LEVEL"] = "ilvl"
	DFCNSL["OVERALL_ITEM_LEVEL"] = "avg ilvl"
	DFCNSL["MANUAL_UPDATE"] = "Manual Update"
	DFCNSL["SWITCH_TO_CURRENCY"] = "to Currency Statistics"
	DFCNSL["SWITCH_TO_DELVES"] = "to Delves Statistics"
	DFCNSL["MULTI_CHAR_MANAGER"] = "Multi-Character Dashboard"
	DFCNSL["MULTI_CHAR_MANAGER_SHORT"] = "MCD Manager"
	DFCNSL["DELVES_STATS"] = "Delves Statistics"
	DFCNSL["CURRENCY_STATS"] = "Season Currency Statistics"
	DFCNSL["FONT_SIZE"] = "Font Size"
	DFCNSL["CLICK_OR_SPACE"] = "Click or press SPACE to switch view mode"
	DFCNSL["CHARACTER_RESET"] = "Multi-Character Dashboard: Reset weekly data for %d characters!"
	DFCNSL["ADDON_LOADED"] = "Multi-Character Dashboard loaded"
	DFCNSL["DATABASE_CREATED"] = "Created new addon character database (v%s)"
	DFCNSL["DATA_UPDATED"] = "Current character data manually updated"
	DFCNSL["MINIMAP_SHOWN"] = "Minimap icon shown"
	DFCNSL["MINIMAP_HIDDEN"] = "Minimap icon hidden"
	DFCNSL["CHARACTER_DELETED"] = "Deleted character: %s"
	DFCNSL["SHOW_MINIMAP_ICON"] = "Show Minimap Icon"
	DFCNSL["OPEN_PANEL"] = "Open Panel"
	DFCNSL["CLOSE_PANEL"] = "Close Panel"
	DFCNSL["MACRO_COMMAND"] = "Macro: /dfcnchar"
	DFCNSL["ORIGINAL_DOWNLOAD"] = "Original Release:"
	DFCNSL["CLICK_TO_COPY"] = "Click to select, Ctrl+C to copy"
	DFCNSL["CURRENCY"] = "Currency"
	DFCNSL["CURRENCY_LV1"] = "Adventurer"
	DFCNSL["CURRENCY_CATACLYSM"] = "Undercoin"
	DFCNSL["CURRENCY_LV2"] = "Veteran"
	DFCNSL["CURRENCY_LV3"] = "Champion"
	DFCNSL["CURRENCY_LV4"] = "Hero"
	DFCNSL["CURRENCY_LV5"] = "Myth"
	DFCNSL["CURRENCY_STARLIGHT"] = "Spark Dust"
	DFCNSL["CURRENCY_FRAGMENT"] = "Manaflux"
	DFCNSL["TOOLTIP_CHARACTER_NAME"] = "Character Name - Server"
	DFCNSL["TOOLTIP_PROF_CONCENTRATION"] = "Midnight Profession (Concentration)"
	DFCNSL["TOOLTIP_KEY_ACQUISITION"] = "Total Keys | Weekly Keys/4"
	DFCNSL["TOOLTIP_SHARD_ACQUISITION"] = "Coffer Key Shards acquired / Season cap"
	DFCNSL["TOOLTIP_DELVE_COUNT"] = "Tier 8+ activities completed | Great Vault slots unlocked"
	DFCNSL["TOOLTIP_TREASURE_MAP"] = "Delver's Bounty status"
	DFCNSL["TOOLTIP_GOLDEN_CACHE"] = "Gilded Stash status"
	DFCNSL["TOOLTIP_ITEM_LEVEL"] = "Character belt item level"
	DFCNSL["CURRENCY_MANA_CRYSTAL"] = "Crystals"
	DFCNSL["TOOLTIP_MANA_CRYSTAL"] = "Untainted Mana-Crystals held"
	DFCNSL["TOOLTIP_OVERALL_ITEM_LEVEL"] = "Character overall item level"
	DFCNSL["LEFT_CLICK"] = "Left Click: Open/Close Panel"
	DFCNSL["COMMAND_OPEN_CLOSE"] = "|cFFFFFF00/dfcnchar|r - Open/Close Panel"
	DFCNSL["COMMAND_MINIMAP_ON"] = "|cFFFFFF00/dfcnchar minimap on|r - Show minimap icon"
	DFCNSL["COMMAND_MINIMAP_OFF"] = "|cFFFFFF00/dfcnchar minimap off|r - Hide minimap icon"
	DFCNSL["MACRO_COMMAND"] = "Macro: |cFFFFFF00/dfcnchar|r"
	DFCNSL["MINIMAP_ICON"] = "Minimap icon: |cFFFFFF00/dfcnchar minimap on/off|r"
	DFCNSL["ESC_TO_CLOSE"] = "ESC:Close Panel"
	DFCNSL["CONCENTRATION_VALUE"] = "Concentration"
	DFCNSL["NO_CONCENTRATION_TIP"] = "Gathering professions have no concentration"
	DFCNSL["CONCENTRATION_FULL"] = "Concentration Full"
	DFCNSL["CURRENT_VALUE"] = "Current Value"
	DFCNSL["TIME_TO_FULL"] = "Time to Full"
	DFCNSL["ESTIMATED_FULL_TIME"] = "Estimated Full Time"
	DFCNSL["LAST_LOGIN_TIME"] = "Last Login Time"
	DFCNSL["CURRENCY_TOOLTIP_UNKNOWN"] = "Haven't logged in this character, data unknown"
	DFCNSL["CURRENCY_TOOLTIP_LOGIN"] = "Please log in this character to get the latest data automatically"
	DFCNSL["UNKNOWN"] = "Unknown"
	DFCNSL["FONT_SIZE_SLIDER"] = "Font Size: %s"
	DFCNSL["FONT_SIZE_POSITIVE"] = "Font Size: +%d"
	DFCNSL["FONT_SIZE_NEGATIVE"] = "Font Size: %d"
	DFCNSL["FONT_SIZE_ZERO"] = "Font Size: 0"
	DFCNSL["DELETE_CHAR_CONFIRM"] = "Are you sure you want to delete data for |cFFFFD100%s|r?"
	DFCNSL["DELETE"] = "Delete"
	DFCNSL["CANCEL"] = "Cancel"
	DFCNSL["DELETE_CHAR_TOOLTIP"] = "Delete this character data"
	DFCNSL["GREAT_VAULT_NOT_COLLECTED"] = "Great Vault not collected"
	DFCNSL["QUANTITY_HELD"] = "Quantity Held"
	DFCNSL["SEASON_CAP"] = "Season Cap"
	DFCNSL["HOLDING_CAP"] = "Holding Cap"
	DFCNSL["WEEKLY_CAP"] = "Weekly Cap"
	DFCNSL["SEASON_EARNED"] = "Season Earned"
	DFCNSL["WEEKLY_EARNED"] = "Weekly Earned"
	DFCNSL["REACHED_CAP"] = "Reached Cap"
	DFCNSL["NEED_MORE"] = "Need %d more for season cap"
	DFCNSL["HOLD_MORE"] = "Need %d more for holding cap"
	DFCNSL["REACHED_WEEKLY_CAP"] = "Reached weekly cap"
	DFCNSL["NEED_MORE_WEEKLY"] = "Need %d more to reach weekly cap"
	DFCNSL["INFO"] = "Info"
	DFCNSL["UPGRADE"] = "Database reset"
	DFCNSL["RESET"] = "Reset"
	DFCNSL["DATA_AUTO_UPDATED"] = "(Data auto updated)"
	DFCNSL["DATA_MAY_BE_OUTDATED"] = "(Data may be outdated)"
	DFCNSL["VIEW_ONLY_SUPPORT"] = "(View only)"
	DFCNSL["CURRENT_REGION_FORMAT"] = "Currently in %s, data resets automatically at 7:00 every %s"
	DFCNSL["CHARACTER_LEVEL_68_PLUS"] = "Current character level 78 or above"
	DFCNSL["CHARACTER_LEVEL_BELOW_68"] = "Current character below level 78"
	DFCNSL["REGION_US"] = "[US]"
	DFCNSL["REGION_KR"] = "[KR]"
	DFCNSL["REGION_EU"] = "[EU]"
	DFCNSL["REGION_TW"] = "[TW]"
	DFCNSL["REGION_CN"] = "[CN]"
	DFCNSL["REGION_PTR"] = "[PTR]"
	DFCNSL["SUNDAY"] = "Sunday"
	DFCNSL["MONDAY"] = "Monday"
	DFCNSL["TUESDAY"] = "Tuesday"
	DFCNSL["WEDNESDAY"] = "Wednesday"
	DFCNSL["THURSDAY"] = "Thursday"
	DFCNSL["FRIDAY"] = "Friday"
	DFCNSL["SATURDAY"] = "Saturday"
	DFCNSL["DATABASE_CLEARED"] = "Major game version update detected; all character data has been reset."
	DFCNSL["REST_XP"] = "Rested XP"
	DFCNSL["TIME_TO_FULL_REST"] = "Time to full:"
	DFCNSL["REST_XP_FULL"] = "Rested XP is full!"
	DFCNSL["MidnightKeyword"] = "Midnight"
	DFCNSL["HOTKEY_SETTING"] = "Switch view hotkey works in pass-through mode, does not affect original key function"
	DFCNSL["PRESS_NEW_HOTKEY"] = "Press new hotkey..."
	DFCNSL["CLICK_BUTTON_AGAIN_TO_CANCEL"] = "Click button again to cancel"
	DFCNSL["MODIFIER_NOT_ALLOWED"] = "Modifier keys (Ctrl/Alt/Shift) not allowed"
	DFCNSL["HOTKEY_SET_SUCCESS"] = "Hotkey set successfully"
	DFCNSL["CURRENCY_VOID_CORE"] = "Voidcore"
	DFCNSL["CURRENCY_DUDU"] = "Dundun"
	DFCNSL["LEFT_CLICK_DRAG"] = "Left-click and drag to move the edge bar"
	DFCNSL["DOCK_MODE"] = "Enable edge bar mode"
end

local DFCN_Status = {}
DFCN_Status.db = {}
DFCN_Status.VERSION = "v1.60"
local dbVersion = 12

local function GetResetWeekday()
	local region = GetCurrentRegion()	
	if region == 1 then
		return 3
	elseif region == 3 then
		return 4
	else
		return 5
	end
end

local itemInfoRetryAttempts = 0
local MAX_RETRY_ATTEMPTS = 3
local craftUpdateThrottle = 0
local function ShortenProfName(name)
	if not name then return "" end
	local shortName = string.sub(name, 1, 6)
	return shortName
end

local function FormatTime(seconds)
	if not seconds then return "0" .. DFCNSL["SECONDS"] end
	if seconds < 60 then
		return string.format("%d" .. DFCNSL["SECONDS"], seconds)
	elseif seconds < 3600 then
		return string.format("%d" .. DFCNSL["MINUTES"], seconds / 60)
	elseif seconds < 86400 then
		return string.format("%d" .. DFCNSL["HOURS"] .. "%d" .. DFCNSL["MINUTES"], seconds / 3600, (seconds % 3600) / 60)
	else
		return string.format("%d" .. DFCNSL["DAYS"] .. "%d" .. DFCNSL["HOURS"], seconds / 86400, (seconds % 86400) / 3600)
	end
end

local function tContains(t, value)
	for _, v in ipairs(t) do
		if v == value then
			return true
		end
	end
	return false
end

local function GetPreciseTime()
	return time() + (DFCN_Status.serverTimeOffset or 0)
end

local function EstimateOfflineRestXP(savedXP, savedTime, wasResting, maxXP, isPandaren)
	if type(savedXP) ~= "number" or type(savedTime) ~= "number" or type(maxXP) ~= "number" or maxXP <= 0 then
		return 0
	end
	local now = GetPreciseTime()
	local dt = now - savedTime
	local rate = 0.05 / (8 * 3600)
	if not wasResting then
		rate = rate / 4
	end
	local gained = dt * maxXP * rate
	local maxRest = maxXP * (isPandaren and 3.0 or 1.5)
	return math.min(savedXP + gained, maxRest)
end

local function SecondsToFullRest(savedXP, wasResting, maxXP, isPandaren)
	if type(savedXP) ~= "number" or type(maxXP) ~= "number" or maxXP <= 0 then
		return 0
	end
	local maxRest = maxXP * (isPandaren and 3.0 or 1.5)
	local missing = maxRest - savedXP
	if missing <= 0 then return 0 end
	local rate = 0.05 / (8 * 3600)
	if not wasResting then
		rate = rate / 4
	end
	return missing / (maxXP * rate)
end

DFCN_Status.concentrationTracker = {
	values = {},
	lastUpdated = 0
}

local miniMapButton = nil
local miniMapIcon = "Interface\\Icons\\spell_magic_lesserinvisibilty"
local CURRENCY_UPDATE_DELAY = 0.5
local lastCurrencyUpdate = 0
local COLORS = {
	BACKGROUND = {r = 0.1, g = 0.1, b = 0.1, a = 0.9},
	TEXT_WHITE = {r = 1.0, g = 1.0, b = 1.0},
	TEXT_HIGHLIGHT = {r = 1.0, g = 0.82, b = 0},
	TEXT_PURPLE = {r = 0.63, g = 0.21, b = 0.93},
	TEXT_RED = {r = 1.0, g = 0.25, b = 0.25},
	TEXT_GREEN = {r = 0.25, g = 1.0, b = 0.25},
	TEXT_YELLOW = {r = 1.0, g = 1.0, b = 0},
	TEXT_GRAY = {r = 0.5, g = 0.5, b = 0.5},
	TEXT_HERO = {r = 0.3, g = 0.8, b = 1},
	TEXT_MYTH = {r = 1.0, g = 0.65, b = 0.0},
}

local CONCENTRATION_COLORS = {
	HIGH = {r = 1.0, g = 0.25, b = 0.25},
	LOW = {r = 0.25, g = 1.0, b = 0.25},
	BACKGROUND = {r = 0.1, g = 0.1, b = 0.1, a = 0.8}
}

local function GetConcentrationColor(value)
	if not value then return CONCENTRATION_COLORS.LOW end
	if value < 856 then
		return CONCENTRATION_COLORS.LOW
	else
		return CONCENTRATION_COLORS.HIGH
	end
end

local CLASS_COLORS = {
	WARRIOR = {r = 0.78, g = 0.61, b = 0.43},
	PALADIN = {r = 0.96, g = 0.55, b = 0.73},
	HUNTER = {r = 0.67, g = 0.83, b = 0.45},
	ROGUE = {r = 1.00, g = 0.96, b = 0.41},
	PRIEST = {r = 1.00, g = 1.00, b = 1.00},
	DEATHKNIGHT = {r = 0.77, g = 0.12, b = 0.23},
	SHAMAN = {r = 0.00, g = 0.44, b = 0.87},
	MAGE = {r = 0.25, g = 0.78, b = 0.92},
	WARLOCK = {r = 0.53, g = 0.53, b = 0.93},
	MONK = {r = 0.00, g = 1.00, b = 0.59},
	DRUID = {r = 1.00, g = 0.49, b = 0.04},
	DEMONHUNTER = {r = 0.64, g = 0.19, b = 0.79},
	EVOKER = {r = 0.20, g = 0.58, b = 0.50}
}

local ENHANCED_CLASS_COLORS = {}
for class, color in pairs(CLASS_COLORS) do
	ENHANCED_CLASS_COLORS[class] = {
		r = math.min(color.r * 1.0, 1.0),
		g = math.min(color.g * 1.0, 1.0),
		b = math.min(color.b * 1.0, 1.0)
	}
end

local CURRENCY_IDS = {2803, 3383, 3341, 3343, 3345, 3347, 3212, 3378, 3376}
local STARLIGHT_DUST_ITEM_ID = 232875
local CURRENCY_NAMES = {
	[2803] = DFCNSL["CURRENCY_CATACLYSM"],
	[3383] = DFCNSL["CURRENCY_LV1"],
	[3341] = DFCNSL["CURRENCY_LV2"],
	[3343] = DFCNSL["CURRENCY_LV3"],
	[3345] = DFCNSL["CURRENCY_LV4"],
	[3347] = DFCNSL["CURRENCY_LV5"],
	[3212] = DFCNSL["CURRENCY_STARLIGHT"],
	[3378] = DFCNSL["CURRENCY_FRAGMENT"],
	[3356] = DFCNSL["CURRENCY_MANA_CRYSTAL"],
	[3418] = DFCNSL["CURRENCY_VOID_CORE"],
	[3376] = DFCNSL["CURRENCY_DUDU"],
}

local DELVES_COLUMN_LAYOUT = {
	{name = "|T236544:14:14|t" .. DFCNSL["CHARACTER_NAME"], width = 230, align = "LEFT", color = true, sortable = true, sortKey = "name"},
	{name = "|T5747318:14:14|t" .. DFCNSL["PROF_CONCENTRATION"], width = 105, align = "LEFT", color = true, sortable = true, sortKey = "prof1Concentration"},
	{name = "|T5747318:14:14|t" .. DFCNSL["PROF_CONCENTRATION"], width = 105, align = "LEFT", color = true, sortable = true, sortKey = "prof2Concentration"},
	{name = "|T4622270:14:14|t" .. DFCNSL["KEY_ACQUISITION"], width = 80, align = "CENTER", color = true, sortable = true, sortKey = "keyStatus", currencyID = 3028},
	{name = "|T133016:14:14|t" .. DFCNSL["SHARD_ACQUISITION"], width = 105, align = "CENTER", color = true, sortable = true, sortKey = "shardStatus", currencyID = 3310},
	{name = "|A:GreatVault-32x32:18:18|a" .. DFCNSL["DELVE_COUNT"], width = 80, align = "CENTER", color = true, sortable = true, sortKey = "delveCount"},
	{name = "|T1064187:14:14|t" .. DFCNSL["TREASURE_MAP"], width = 90, align = "CENTER", color = true, sortable = true, sortKey = "treasureStatus", itemID = 252415},
	{name = "|T5872049:14:14|t" .. DFCNSL["GOLDEN_CACHE"], width = 90, align = "CENTER", color = true, sortable = true, sortKey = "goldenCacheStatus"}, 
	{name = "|T5931199:14:14|t" .. DFCNSL["CURRENCY_MANA_CRYSTAL"], width = 110, align = "CENTER", color = true, sortable = true, sortKey = "currency_3356", currencyID = 3356},
	{name = "|T7658128:14:14|t" .. (DFCNSL["CURRENCY_VOID_CORE"]), width = 110, align = "CENTER", color = true, sortable = true, sortKey = "currency_3418", currencyID = 3418},
	{name = DFCNSL["OVERALL_ITEM_LEVEL"], width = 90, align = "CENTER", color = false, sortable = true, sortKey = "overallItemLevel"},
	{name = "", width = 15, align = "CENTER", color = false, sortable = false} 
}

local CURRENCY_COLUMN_LAYOUT = {
	{name = "|T236544:14:14|t" .. DFCNSL["CHARACTER_NAME"], width = 230, align = "LEFT", color = true, sortable = true, sortKey = "name"}
}

for _, currencyID in ipairs(CURRENCY_IDS) do
	local currencyInfo = C_CurrencyInfo.GetCurrencyInfo(currencyID)
	if currencyInfo then
		local displayName = CURRENCY_NAMES[currencyID] or currencyInfo.name		
		table.insert(CURRENCY_COLUMN_LAYOUT, {
			name = "|T" .. currencyInfo.iconFileID .. ":14:14|t" .. displayName,
			width = 106,
			align = "CENTER",
			color = true,
			sortable = true,
			sortKey = "currency_" .. currencyID,
			currencyID = currencyID
		})
	end
end

table.insert(CURRENCY_COLUMN_LAYOUT, {name = "", width = 15, align = "CENTER", color = false, sortable = false})

local COLUMN_TOOLTIPS = {
	["|T236544:14:14|t" .. DFCNSL["CHARACTER_NAME"]] = DFCNSL["TOOLTIP_CHARACTER_NAME"],
	["|T5747318:14:14|t" .. DFCNSL["PROF_CONCENTRATION"]] = DFCNSL["TOOLTIP_PROF_CONCENTRATION"],
	["|T4622270:14:14|t" .. DFCNSL["KEY_ACQUISITION"]] = DFCNSL["TOOLTIP_KEY_ACQUISITION"],
	["|T133016:14:14|t" .. DFCNSL["SHARD_ACQUISITION"]] = DFCNSL["TOOLTIP_SHARD_ACQUISITION"],
	["|A:GreatVault-32x32:18:18|a" .. DFCNSL["DELVE_COUNT"]] = DFCNSL["TOOLTIP_DELVE_COUNT"],
	["|T1064187:14:14|t" .. DFCNSL["TREASURE_MAP"]] = DFCNSL["TOOLTIP_TREASURE_MAP"],
	["|T5872049:14:14|t" .. DFCNSL["GOLDEN_CACHE"]] = DFCNSL["TOOLTIP_GOLDEN_CACHE"],
	["|T5931199:14:14|t" .. DFCNSL["CURRENCY_MANA_CRYSTAL"]] = DFCNSL["TOOLTIP_MANA_CRYSTAL"],
	[DFCNSL["OVERALL_ITEM_LEVEL"]] = DFCNSL["TOOLTIP_OVERALL_ITEM_LEVEL"]
}

for _, currencyID in ipairs(CURRENCY_IDS) do
	local currencyInfo = C_CurrencyInfo.GetCurrencyInfo(currencyID)
	if currencyInfo then
		local displayName = CURRENCY_NAMES[currencyID] or currencyInfo.name
		COLUMN_TOOLTIPS["|T" .. currencyInfo.iconFileID .. ":14:14|t" .. displayName] = currencyInfo.description
	end
end

local CONCENTRATION_MAX = 1000
local CONCENTRATION_RECOVERY_RATE = 345
local CONCENTRATION_CURRENCY_IDS = {
	[164] = 3162,
	[171] = 3161,
	[202] = 3164,
	[197] = 3168,
	[333] = 3163,
	[165] = 3167,
	[755] = 3166,
	[773] = 3165 
}

local NO_CONCENTRATION_PROFESSIONS = {
	[393] = true,
	[186] = true,
	[182] = true
}

local isUpdateLevel = false

local function TreasureStatusToValue(status)
	if status == DFCNSL["IN_BAG"] then
		return 2
	elseif status == DFCNSL["USED"] then
		return 1
	else
		return 0
	end
end

function DFCN_Status:GetLastResetTime()
	local currentTime = GetPreciseTime()
	local t = date("*t", currentTime)	
	local resetWeekday = GetResetWeekday()	
	local daysToReset
	if t.wday <= resetWeekday then
		daysToReset = resetWeekday - t.wday
	else
		daysToReset = resetWeekday - t.wday + 7
	end	
	local resetTime = time({
		year = t.year, 
		month = t.month, 
		day = t.day + daysToReset,
		hour = 7,
		min = 0,
		sec = 0
	})	
	if currentTime >= resetTime then
		return resetTime
	end	
	return resetTime - 7 * 86400
end

local SLOTS_TO_CHECK = {1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}

function DFCN_Status:CalculateGearLevelStatus()
	local allHero, allMyth = true, true
	for _, slotID in ipairs(SLOTS_TO_CHECK) do
		local itemLink = GetInventoryItemLink("player", slotID)
		if not itemLink then
			if slotID ~= 17 then
				allHero = false
				break
			end
		else
			local level = nil
			local upgradeInfo = C_Item.GetItemUpgradeInfo(itemLink)
			if upgradeInfo and upgradeInfo.trackStringID and upgradeInfo.trackStringID > 0 then
				if upgradeInfo.trackStringID == 978 then
					level = "mythic"
				elseif upgradeInfo.trackStringID == 974 then
					level = "heroic"
				end
			else
				local _, _, _, itemLevel = GetItemInfo(itemLink)
				if not itemLevel then
					_, _, _, itemLevel = GetItemInfo(itemLink)
				end
				if itemLevel then
					if itemLevel >= 272 then
						level = "mythic"
					elseif itemLevel >= 259 and itemLevel < 272 then
						level = "heroic"
					end
				end
			end

			if level == "heroic" then
				allMyth = false
			elseif level ~= "mythic" then
				allHero = false
				allMyth = false
				break
			end
		end
	end
	if not allHero then return nil end
	local avgEquipped = GetAverageItemLevel()
	if type(avgEquipped) ~= "number" then return nil end
	if allMyth and avgEquipped >= 272 then
		return "myth"
	elseif allHero and avgEquipped >= 259 then
		return "hero"
	end
	return nil
end

function DFCN_Status:CheckAllCharactersReset()
	local resetTime = self:GetLastResetTime()
	local resetCount = 0	
	for charKey, charData in pairs(DFCN_Status.db.characters) do
		if charData.lastUpdated and charData.level and charData.level >= 78 then
			if charData.lastUpdated < resetTime then
				charData.treasureStatus = DFCNSL["NOT_OBTAINED"]
				charData.goldenCacheStatus = "0/4"
				charData.delveCount = 0
				charData.worldSlotsUnlocked = 0				
				if not charData.shardStatus then
					charData.shardStatus = {
						totalShards = 0,
						seasonEarned = 0,
						weeklyEarned = 0
					}
				else
					charData.shardStatus.weeklyEarned = 0
				end				
				if charData.currencies and charData.currencies[3356] then
					charData.currencies[3356].weeklyEarned = 0
				elseif charData.currencies then
					charData.currencies[3356] = {
						quantity = 0,
						totalEarned = 0,
						weeklyEarned = 0,
					}
				end				
				resetCount = resetCount + 1				
				charData.lastUpdated = resetTime
			end
		end
	end	
	if resetCount > 0 then
		print("|T135994:14:14|t |cFF00BFFF[" .. DFCNSL["INFO"] .. "]|r |c000EEEEE" .. string.format(DFCNSL["CHARACTER_RESET"], resetCount))
	end
end

function DFCN_Status:UpdateConcentrationTracker()
	local now = GetPreciseTime()
	local elapsed = now - self.concentrationTracker.lastUpdated	
	for profKey, profData in pairs(self.concentrationTracker.values) do
		if profData.hasConcentration then
			local newValue = profData.value + (elapsed / CONCENTRATION_RECOVERY_RATE)
			if newValue > CONCENTRATION_MAX then
				newValue = CONCENTRATION_MAX
			end			
			profData.value = newValue
		end
	end	
	self.concentrationTracker.lastUpdated = now
end

function DFCN_Status:GetCharacterKey()
	local name, realm = UnitFullName("player")
	if not realm then
		realm = GetRealmName()
	end
	if realm == "Area52" then
		realm = "Area 52"
	end
	return name .. "-" .. realm
end

function DFCN_Status:SaveOnLogout()
	self:UpdateConcentrationTracker()	
	local charKey = self:GetCharacterKey()
	if not DFCN_Status.db.characters[charKey] then 
		return 
	end	
	DFCN_Status.db.characters[charKey].concentration = {
		prof1 = CopyTable(self.concentrationTracker.values.prof1 or {value = 0.0, name = DFCNSL["NO_PROFESSION"], hasConcentration = false}),
		prof2 = CopyTable(self.concentrationTracker.values.prof2 or {value = 0.0, name = DFCNSL["NO_PROFESSION"], hasConcentration = false}),
		lastUpdated = GetPreciseTime()
	}
	if DFCN_Status.db.characters[charKey].restXP then
		DFCN_Status.db.characters[charKey].restXP.lastUpdated = GetPreciseTime()
	end
end

function DFCN_Status:GetOverallItemLevel()
	local overall, equipped = GetAverageItemLevel()
	local value
	if equipped and equipped > 0 then
		value = equipped
	else
		value = overall
	end
	if not value or value <= 0 then
		return nil
	end
	return tonumber(string.format("%.2f", value))
end

function DFCN_Status:GetBeltItemLevel()
	local slotID = 15
	local beltLink = GetInventoryItemLink("player", slotID)	
	local currentAttempt = itemInfoRetryAttempts	
	if not beltLink then
		if currentAttempt < MAX_RETRY_ATTEMPTS then
			itemInfoRetryAttempts = itemInfoRetryAttempts + 1
			C_Timer.After(1, function() 
				DFCN_Status:GetBeltItemLevel() 
			end)
		end
		return 0
	end
	itemInfoRetryAttempts = 0	
	local name, _, quality, itemLevel = GetItemInfo(beltLink)
	if itemLevel and itemLevel > 0 then
		return math.floor(itemLevel + 0.5)
	end	
	local itemString = beltLink:match("item[%-?%d:]+")
	if itemString then
		local parts = {strsplit(":", itemString)}
		if #parts >= 12 then
			local ilvl = tonumber(parts[12])
			if ilvl and ilvl > 0 then
				return ilvl
			end
		end
	end	
	local itemQuality = GetInventoryItemQuality("player", slotID)
	if itemQuality then
		return itemQuality
	end	
	return 0
end

function DFCN_Status:GetTreasureStatus()
	local hasItem = GetItemCount(252415) > 0
	local questDone = C_QuestLog.IsQuestFlaggedCompleted(86371)	
	if hasItem then
		return DFCNSL["IN_BAG"]
	elseif questDone then
		return DFCNSL["USED"]
	else
		return DFCNSL["NOT_OBTAINED"]
	end
end

function DFCN_Status:GetGoldenCacheStatus(charData)
	local widgetInfo = C_UIWidgetManager.GetSpellDisplayVisualizationInfo(7591)	
	if widgetInfo and widgetInfo.spellInfo and widgetInfo.spellInfo.tooltip then
		local tooltipText = widgetInfo.spellInfo.tooltip
		local currentCount = tonumber(string.match(tooltipText, "(%d)/4"))		
		if currentCount then
			return currentCount .. "/4"
		end
	end	
	return charData and charData.goldenCacheStatus or "0/4"
end

function DFCN_Status:GetLevel8DelvesDoneCount()
	local progressData = C_WeeklyRewards.GetSortedProgressForActivity(6, true)
	if not progressData or #progressData == 0 then
		return 0
	end
	local total = 0
	for _, entry in ipairs(progressData) do
		if entry.difficulty >= 8 then
			total = total + entry.numPoints
		end
	end
	return total
end

function GetNumUnlockedWorldSlots()
	local activities = C_WeeklyRewards.GetActivities(6)
	if not activities then return 0 end
	local count = 0
	for _, slot in ipairs(activities) do
		if slot.progress >= slot.threshold then
			count = count + 1
		end
	end
	return count
end

function DFCN_Status:IsItemDataReady()
	return GetItemInfo(6948) ~= nil
end

function DFCN_Status:GetConcentrationData()
	self:UpdateConcentrationTracker()	
	local concentrationData = {
		lastUpdated = GetPreciseTime(),
		prof1 = CopyTable(self.concentrationTracker.values.prof1 or {value = 0.0, name = DFCNSL["NO_PROFESSION"], hasConcentration = false}),
		prof2 = CopyTable(self.concentrationTracker.values.prof2 or {value = 0.0, name = DFCNSL["NO_PROFESSION"], hasConcentration = false})
	}	
	return concentrationData
end

function DFCN_Status:GetCharacterRestXP(charData, isCurrentChar)
	local maxLevel = GetMaxPlayerLevel()
	if isCurrentChar then
		if UnitLevel("player") >= maxLevel then
			return nil, nil, false
		end
	else
		if charData and charData.level and charData.level >= maxLevel then
			return nil, nil, false
		end
	end
	if isCurrentChar then
		local playerLevel = UnitLevel("player")
		local maxLevel = GetMaxPlayerLevel()
		if playerLevel >= maxLevel then
			return nil, nil, false
		end
		local exhaustion = GetXPExhaustion() or 0
		local maxXP = UnitXPMax("player")
		if maxXP == 0 then
			return nil, nil, false
		end
		local percent = (exhaustion / maxXP) * 100
		local _, _, raceID = UnitRace("player")
		local isPandaren = (raceID == 24 or raceID == 25 or raceID == 26)
		local secondsToFull = SecondsToFullRest(exhaustion, IsResting(), maxXP, isPandaren)
		return percent, secondsToFull, exhaustion
	end
	if charData.restXP and charData.restXP.value then
		local saved = charData.restXP
		if not saved.maxXP or not saved.lastUpdated or saved.maxXP == 0 then
			charData.restXP = nil
			return nil, nil, false
		end
		local currentExhaustion = EstimateOfflineRestXP(
			saved.value,
			saved.lastUpdated,
			saved.wasResting,
			saved.maxXP,
			saved.isPandaren
		)
		local percent = (currentExhaustion / saved.maxXP) * 100
		local secondsToFull = SecondsToFullRest(currentExhaustion, saved.wasResting, saved.maxXP, saved.isPandaren)
		return percent, secondsToFull, currentExhaustion
	end
	return nil, nil, false
end

function DFCN_Status:SyncProfessionData()
	self.concentrationTracker.values = {
		prof1 = {value = 0.0, name = DFCNSL["NO_PROFESSION"], hasConcentration = false},
		prof2 = {value = 0.0, name = DFCNSL["NO_PROFESSION"], hasConcentration = false}
	}
	local prof1, prof2 = GetProfessions()
	local professions = {prof1, prof2}
	
	for i, slot in ipairs(professions) do
		if slot then
			local name, _, skillLevel, maxSkillLevel, _, _, skillLine, _, _, _, fullName = GetProfessionInfo(slot)
			
			if skillLine and NO_CONCENTRATION_PROFESSIONS[skillLine] then
				if not fullName or not string.find(fullName, DFCNSL["MidnightKeyword"], 1, true) then
					skillLevel = 0
					maxSkillLevel = 100
				end
			end			
			local currencyId = CONCENTRATION_CURRENCY_IDS[skillLine]
			local hasConcentration = false
			local value = 0.0
			if currencyId and not NO_CONCENTRATION_PROFESSIONS[skillLine] then
				local currencyInfo = C_CurrencyInfo.GetCurrencyInfo(currencyId)
				if currencyInfo and currencyInfo.discovered then
					hasConcentration = true
					value = currencyInfo.quantity
				end
			end
			self.concentrationTracker.values["prof"..i] = {
				value = value,
				name = name,
				currencyId = currencyId,
				skillLine = skillLine,
				hasConcentration = hasConcentration,
				skillLevel = skillLevel,
				maxSkillLevel = maxSkillLevel
			}
		end
	end	
	self.concentrationTracker.lastUpdated = GetPreciseTime()
end

function DFCN_Status:GetCurrencyData()
	local currencyData = {}	
	for _, currencyID in ipairs(CURRENCY_IDS) do
		local currencyInfo = C_CurrencyInfo.GetCurrencyInfo(currencyID)
		if currencyInfo then
			currencyData[currencyID] = {
				quantity = currencyInfo.quantity,
				totalEarned = currencyInfo.totalEarned or 0,
			}
			if currencyID == 3212 then
				currencyData[currencyID].quantity = C_Item.GetItemCount(STARLIGHT_DUST_ITEM_ID, true, false, true, true)
			end
		end
	end
	local manaCrystalInfo = C_CurrencyInfo.GetCurrencyInfo(3356)
	if manaCrystalInfo then
		currencyData[3356] = {
			quantity = manaCrystalInfo.quantity,
			totalEarned = manaCrystalInfo.totalEarned or 0,
			weeklyEarned = manaCrystalInfo.quantityEarnedThisWeek or 0,
		}
	end
	local voidCoreInfo = C_CurrencyInfo.GetCurrencyInfo(3418)
	if voidCoreInfo then
		currencyData[3418] = {
			quantity = voidCoreInfo.quantity,
			totalEarned = voidCoreInfo.totalEarned or 0,
			maxQuantity = voidCoreInfo.maxQuantity or 0,
		}
	end	
	return currencyData
end

function DFCN_Status:UpdateCharacterData(silent)
	local playerLevel = UnitLevel("player")
	if playerLevel < 78 then 
		return 
	end	
	if not self:IsItemDataReady() then
		C_Timer.After(1, function()
			DFCN_Status:UpdateCharacterData(silent)
		end)
		return
	end	
	local charKey = self:GetCharacterKey()
	if not DFCN_Status.db.characters[charKey] then
		DFCN_Status.db.characters[charKey] = {}
		DFCN_Status.db.characters[charKey].keyStatus = {
			totalKeys = 0,
			completedQuests = 0
		}
		DFCN_Status.db.characters[charKey].shardStatus = {
			totalShards = 0,
			seasonEarned = 0
		}
		DFCN_Status.db.characters[charKey].currencies = {}
	end
	local charData = DFCN_Status.db.characters[charKey]
	local keyStatus = self:GetKeyStatus()
	charData.keyStatus = keyStatus
	local shardStatus = self:GetShardStatus()
	charData.shardStatus = shardStatus	
	self:SyncProfessionData()	
	local ilvl = self:GetOverallItemLevel()
	if ilvl and ilvl > 0 then
		charData.overallItemLevel = ilvl
	end
	charData.beltItemLevel = self:GetBeltItemLevel()
	charData.treasureStatus = self:GetTreasureStatus()
	charData.goldenCacheStatus = self:GetGoldenCacheStatus(charData)
	charData.class = select(2, UnitClass("player"))
	charData.lastUpdated = GetPreciseTime()
	charData.name = UnitName("player")
	local realm = GetRealmName()
	if realm == "Area52" then
		realm = "Area 52"
	end
	charData.realm = realm
	charData.gearLevelStatus = self:CalculateGearLevelStatus()
	charData.delveCount = self:GetLevel8DelvesDoneCount()
	charData.worldSlotsUnlocked = GetNumUnlockedWorldSlots()
	charData.level = playerLevel	
	charData.concentration = self:GetConcentrationData()
	local maxLevel = GetMaxPlayerLevel()
	if playerLevel < maxLevel then
		local maxXP = UnitXPMax("player")
		if maxXP and maxXP > 0 then
			local exhaustion = GetXPExhaustion() or 0
			local _, _, raceID = UnitRace("player")
			local isPandaren = (raceID == 24 or raceID == 25 or raceID == 26)
			charData.restXP = {
				value = exhaustion,
				lastUpdated = GetPreciseTime(),
				wasResting = IsResting(),
				maxXP = maxXP,
				level = playerLevel,
				isPandaren = isPandaren
			}
		else
			charData.restXP = nil
		end
	else
		charData.restXP = nil
	end
	charData.currencies = self:GetCurrencyData()
	if charKey == self:GetCharacterKey() then
		charData.hasWeeklyRewards = C_WeeklyRewards.HasAvailableRewards()
	end
end

function DFCN_Status:GetCurrentConcentration(profData, lastUpdated)
	if not profData or not profData.hasConcentration then 
		return nil, false
	end	
	local currentTime = GetPreciseTime()
	local elapsed = currentTime - lastUpdated	
	local current = math.min(CONCENTRATION_MAX, profData.value + (elapsed / CONCENTRATION_RECOVERY_RATE))	
	return current, true
end

function DFCN_Status:GetKeyStatus()
	local keyInfo = C_CurrencyInfo.GetCurrencyInfo(3028)
	local totalKeys = keyInfo and keyInfo.quantity or 0
	return {
		totalKeys = totalKeys
	}
end

function DFCN_Status:GetShardStatus()
	local currencyInfo = C_CurrencyInfo.GetCurrencyInfo(3310)
	local totalShards = currencyInfo and currencyInfo.quantity or 0
	local seasonEarned = currencyInfo and currencyInfo.totalEarned or 0
	local weeklyEarned = currencyInfo and currencyInfo.quantityEarnedThisWeek or 0
	return {
		totalShards = totalShards,
		seasonEarned = seasonEarned,
		weeklyEarned = weeklyEarned,
	}
end

function DFCN_Status:CreateMiniMapButton()
	if not DFCN_Status.db.minimap then
		DFCN_Status.db.minimap = {
			hide = false,
			position = nil
		}
	end	
	local LDB = LibStub and LibStub:GetLibrary("LibDataBroker-1.1", true)
	if LDB then
		local minimapData = LDB:NewDataObject("DFCN_CharacterStatus", {
			type = "data source",
			text = DFCNSL["MULTI_CHAR_MANAGER"],
			icon = miniMapIcon,
			OnClick = function(_, button)
				if button == "LeftButton" then
					if DFCN_Status.uiFrame and DFCN_Status.uiFrame:IsVisible() then
						DFCN_Status.uiFrame:Hide()
					else
						DFCN_Status:ShowUI()
					end
				end
			end,
			OnTooltipShow = function(tooltip)
				tooltip:SetText("|c000EEE00" .. DFCNSL["MULTI_CHAR_MANAGER"] .. "|r|cFFFFA500 By|r|c000EEEEE DFCN")
				tooltip:AddLine(" ")
				tooltip:AddLine(DFCNSL["LEFT_CLICK"], 1, 1, 1)
				tooltip:AddLine(DFCNSL["MACRO_COMMAND"], 0.5, 1, 1)
				tooltip:AddLine(DFCNSL["MINIMAP_ICON"], 0.5, 1, 1)
				tooltip:AddLine(DFCNSL["ESC_TO_CLOSE"], 0.8, 0.8, 0.8)
			end
		})		
		local LibDBIcon = LibStub and LibStub:GetLibrary("LibDBIcon-1.0", true)
		if LibDBIcon then
			LibDBIcon:Register("DFCN_CharacterStatus", minimapData, DFCN_Status.db.minimap)
			if DFCN_Status.db.minimap.hide then
				LibDBIcon:Hide("DFCN_CharacterStatus")
			else
				LibDBIcon:Show("DFCN_CharacterStatus")
			end
		end
	end
end

function DFCN_Status:CreateUI()
	if DFCN_Status.uiFrame then 
		DFCN_Status.uiFrame:Show()
		return 
	end	
	table.insert(UISpecialFrames, "CharacterStatusFrame")
	local f = CreateFrame("Frame", "CharacterStatusFrame", UIParent)
	f:SetSize(1380, 430)
	f:SetPoint("CENTER")
	f:SetFrameStrata("HIGH")
	f:SetMovable(true)
	f:EnableMouse(true)
	f:RegisterForDrag("LeftButton")
	f:SetScript("OnDragStart", f.StartMoving)
	f:SetScript("OnDragStop", f.StopMovingOrSizing)
	f:SetScript("OnKeyDown", function(self, key)
		if InCombatLockdown() then return end
		self:SetPropagateKeyboardInput(true)
		local hotkey = DFCN_Status.db.hotkey or "SPACE"
		if key == hotkey then
			DFCN_Status.db.viewMode = DFCN_Status.db.viewMode == "delves" and "currency" or "delves"
			DFCN_Status.titleText:SetText(DFCN_Status.db.viewMode == "delves" and 
				"|T135994:14:14|t " .. DFCNSL["MULTI_CHAR_MANAGER"] .. " - " .. DFCNSL["DELVES_STATS"] or 
				"|T135994:14:14|t " .. DFCNSL["MULTI_CHAR_MANAGER"] .. " - " .. DFCNSL["CURRENCY_STATS"])
			DFCN_Status.toggleViewBtn.text:SetText(DFCN_Status.db.viewMode == "delves" and DFCNSL["SWITCH_TO_CURRENCY"] or DFCNSL["SWITCH_TO_DELVES"])
			DFCN_Status:UpdateUI()
			if DFCN_Status.db.enableDockMode and not DFCN_Status.manualOpen then
				DFCN_Status:CancelDockHide()
				DFCN_Status:StartDockHideTimer()
			end
		end
	end)
	f:SetScript("OnHide", function()
		if fontOffsetSlider and fontOffsetSlider:IsShown() then
			fontOffsetSlider:Hide()
		end
		if DFCN_Status.db.enableDockMode and DFCN_Status.manualOpen then
			if DFCN_Status.dockTrigger then
				DFCN_Status.dockTrigger:Show()
			end
			DFCN_Status.manualOpen = false
		end
	end)	
	local bg = f:CreateTexture(nil, "BACKGROUND")
	bg:SetAllPoints(true)
	bg:SetColorTexture(0, 0, 0, 0.8)	
	local titleBar = CreateFrame("Frame", nil, f)
	titleBar:SetSize(1340, 24)
	titleBar:SetPoint("TOP", 0, -2)
	titleBar:EnableMouse(true)
	titleBar:RegisterForDrag("LeftButton")
	titleBar:SetScript("OnDragStart", function(self) 
		if f:IsMovable() then
			local x, y = GetCursorPosition()
			local scale = f:GetEffectiveScale()
			f:StartMoving()
			f.dragOffsetX = (x / scale) - f:GetLeft()
			f.dragOffsetY = f:GetTop() - (y / scale)
		end
	end)	
	titleBar:SetScript("OnDragStop", function(self)
		f:StopMovingOrSizing()
		f.dragOffsetX = nil
		f.dragOffsetY = nil
	end)	
	f:SetScript("OnUpdate", function(self, elapsed)
		if self.dragOffsetX then
			local x, y = GetCursorPosition()
			local scale = self:GetEffectiveScale()
			local left = (x / scale) - self.dragOffsetX
			local top = (y / scale) + self.dragOffsetY
			left = math.max(0, math.min(left, GetScreenWidth() - self:GetWidth()))
			top = math.min(GetScreenHeight(), math.max(top, self:GetHeight()))			
			self:ClearAllPoints()
			self:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top)
		end
	end)	
	local titleBarBg = titleBar:CreateTexture(nil, "BACKGROUND")
	titleBarBg:SetAllPoints(true)
	titleBarBg:SetColorTexture(0, 0, 0, 0.0)	
	local titleText = titleBar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	titleText:SetFont(GameFontNormal:GetFont(), 16)
	titleText:SetPoint("CENTER", 0, -10)
	titleText:SetText(DFCN_Status.db.viewMode == "delves" and 
	"|T135994:14:14|t " .. DFCNSL["MULTI_CHAR_MANAGER"] .. " - " .. DFCNSL["DELVES_STATS"] or 
	"|T135994:14:14|t " .. DFCNSL["MULTI_CHAR_MANAGER"] .. " - " .. DFCNSL["CURRENCY_STATS"])
	DFCN_Status.titleText = titleText
	titleText:SetTextColor(1, 0.8, 0)
	titleText:EnableMouse(true)
	titleText:SetScript("OnMouseUp", function()
		DFCN_Status.db.viewMode = DFCN_Status.db.viewMode == "delves" and "currency" or "delves"		
		DFCN_Status.titleText:SetText(DFCN_Status.db.viewMode == "delves" and 
			"|T135994:14:14|t " .. DFCNSL["MULTI_CHAR_MANAGER"] .. " — " .. DFCNSL["DELVES_STATS"] or 
			"|T135994:14:14|t " .. DFCNSL["MULTI_CHAR_MANAGER"] .. " — " .. DFCNSL["CURRENCY_STATS"])
		DFCN_Status.toggleViewBtn.text:SetText(DFCN_Status.db.viewMode == "delves" and DFCNSL["SWITCH_TO_CURRENCY"] or DFCNSL["SWITCH_TO_DELVES"])
		DFCN_Status:UpdateUI()
	end)
	titleText:SetScript("OnEnter", function(self)
		self:SetTextColor(1, 0, 1)
		GameTooltip:SetOwner(self, "ANCHOR_TOP")
		GameTooltip:SetText(DFCNSL["CLICK_OR_SPACE"], 1, 0, 1)
		GameTooltip:Show()
	end)
	titleText:SetScript("OnLeave", function(self)
		self:SetTextColor(1, 0.8, 0)
		GameTooltip_Hide()
	end)   
	local toggleViewBtn = CreateFrame("Button", nil, titleBar)
	toggleViewBtn:SetSize(120, 24)
	toggleViewBtn:SetPoint("RIGHT", -30, -5)	
	toggleViewBtn.text = toggleViewBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	toggleViewBtn.text:SetPoint("CENTER")
	toggleViewBtn.text:SetText(DFCN_Status.db.viewMode == "delves" and DFCNSL["SWITCH_TO_CURRENCY"] or DFCNSL["SWITCH_TO_DELVES"])
	toggleViewBtn.text:SetTextColor(1, 1, 1)	
	local blankTex = toggleViewBtn:CreateTexture()
	blankTex:SetColorTexture(0, 0, 0, 0)
	blankTex:SetAllPoints()
	toggleViewBtn:SetNormalTexture(blankTex)
	toggleViewBtn:SetPushedTexture(blankTex)
	toggleViewBtn:SetHighlightTexture(blankTex)	
	toggleViewBtn:SetScript("OnEnter", function(self)
		self.text:SetTextColor(1, 0.8, 0)
	end)	
	toggleViewBtn:SetScript("OnLeave", function(self)
		self.text:SetTextColor(1, 1, 1)
	end)	
	toggleViewBtn:SetScript("OnClick", function() 
		DFCN_Status.db.viewMode = DFCN_Status.db.viewMode == "delves" and "currency" or "delves"
		toggleViewBtn.text:SetText(DFCN_Status.db.viewMode == "delves" and DFCNSL["SWITCH_TO_CURRENCY"] or DFCNSL["SWITCH_TO_DELVES"])		
		DFCN_Status.titleText:SetText(DFCN_Status.db.viewMode == "delves" and 
			"|T135994:14:14|t " .. DFCNSL["MULTI_CHAR_MANAGER"] .. " - " .. DFCNSL["DELVES_STATS"] or 
			"|T135994:14:14|t " .. DFCNSL["MULTI_CHAR_MANAGER"] .. " - " .. DFCNSL["CURRENCY_STATS"])	
		DFCN_Status:UpdateUI()
	end)	
	DFCN_Status.toggleViewBtn = toggleViewBtn
	local fontAdjustBtn = CreateFrame("Button", nil, titleBar)
	fontAdjustBtn:SetSize(40, 24)
	fontAdjustBtn:SetPoint("LEFT", 2, 0)
	fontAdjustBtn.text = fontAdjustBtn:CreateFontString(nil, "OVERLAY")
	fontAdjustBtn.text:SetFont(GameFontNormal:GetFont(), 12)
	fontAdjustBtn.text:SetText(DFCNSL["FONT_SIZE"])
	fontAdjustBtn.text:SetPoint("CENTER")
	fontAdjustBtn.text:SetTextColor(1, 1, 1)
	DFCN_Status.fontAdjustBtn = fontAdjustBtn
	local blankTex = fontAdjustBtn:CreateTexture()
	blankTex:SetColorTexture(0, 0, 0, 0)
	blankTex:SetAllPoints()
	fontAdjustBtn:SetNormalTexture(blankTex)
	fontAdjustBtn:SetPushedTexture(blankTex)
	fontAdjustBtn:SetHighlightTexture(blankTex)
	fontAdjustBtn:SetScript("OnEnter", function(self)
		self.text:SetTextColor(0.2, 1, 0.2)
	end)
	fontAdjustBtn:SetScript("OnLeave", function(self)
		self.text:SetTextColor(1, 1, 1)
	end)
	local fontOffsetSlider = CreateFrame("Slider", "DFCN_StatusFontOffsetSlider", f, "OptionsSliderTemplate")
	fontOffsetSlider:SetPoint("BOTTOM", fontAdjustBtn, "TOP", 100, 15)
	fontOffsetSlider:SetSize(250, 15)
	fontOffsetSlider:SetMinMaxValues(-5, 10)
	fontOffsetSlider:SetValueStep(1)
	fontOffsetSlider:SetObeyStepOnDrag(true)
	fontOffsetSlider:SetValue(DFCN_Status.db.fontOffset or 0)
	fontOffsetSlider:Hide()
	DFCN_Status.fontOffsetSlider = fontOffsetSlider
	fontOffsetSlider.Low = _G[fontOffsetSlider:GetName().."Low"]
	fontOffsetSlider.High = _G[fontOffsetSlider:GetName().."High"]
	fontOffsetSlider.Text = _G[fontOffsetSlider:GetName().."Text"]
	fontOffsetSlider.Low:SetText("-5")
	fontOffsetSlider.High:SetText("+10")
	fontOffsetSlider.Text:SetText(string.format(DFCNSL["FONT_SIZE_SLIDER"], DFCN_Status.db.fontOffset or 0))
	fontAdjustBtn:SetScript("OnClick", function()
		if fontOffsetSlider:IsShown() then
			fontOffsetSlider:Hide()
		else
			fontOffsetSlider:Show()
		end
	end)
	fontOffsetSlider:SetScript("OnValueChanged", function(self, value)
		value = math.floor(value + 0.5)		
		local displayText
		if value > 0 then
			displayText = string.format(DFCNSL["FONT_SIZE_POSITIVE"], value)
		elseif value < 0 then
			displayText = string.format(DFCNSL["FONT_SIZE_NEGATIVE"], value)
		else
			displayText = DFCNSL["FONT_SIZE_ZERO"]
		end		
		self.Text:SetText(displayText)
		DFCN_Status.db.fontOffset = value
		DFCN_Status:ApplyFontSettings()
	end)
	f:SetScript("OnHide", function()
		if fontOffsetSlider and fontOffsetSlider:IsShown() then
			fontOffsetSlider:Hide()
		end
		if DFCN_Status.db.enableDockMode and DFCN_Status.manualOpen then
			if DFCN_Status.dockTrigger then
				DFCN_Status.dockTrigger:Show()
			end
			DFCN_Status.manualOpen = false
		end
	end)	
	local closeButton = CreateFrame("Button", nil, titleBar)
	closeButton:SetSize(24, 24)
	closeButton:SetPoint("RIGHT", 5, -1)
	closeButton.text = closeButton:CreateFontString(nil, "OVERLAY")
	closeButton.text:SetFont(GameFontNormal:GetFont(), 20, "OUTLINE")
	closeButton.text:SetText("×")
	closeButton.text:SetPoint("CENTER")
	closeButton.text:SetTextColor(1, 1, 1)	
	local blankTex = closeButton:CreateTexture()
	blankTex:SetColorTexture(0, 0, 0, 0)
	blankTex:SetAllPoints()
	closeButton:SetNormalTexture(blankTex)
	closeButton:SetPushedTexture(blankTex)
	closeButton:SetHighlightTexture(blankTex)	
	closeButton:SetScript("OnEnter", function(self)
		self.text:SetTextColor(1, 0.2, 0.2)
	end)	
	closeButton:SetScript("OnLeave", function(self)
		self.text:SetTextColor(1, 1, 1)
	end)	
	closeButton:SetScript("OnClick", function() 
		DFCN_Status:UpdateCharacterData()
		f:Hide()
	end)	
	local content = CreateFrame("Frame", nil, f)
	content:SetPoint("TOPLEFT", 10, -40)
	content:SetPoint("BOTTOMRIGHT", -10, 40)	
	local contentBg = content:CreateTexture(nil, "BACKGROUND")
	contentBg:SetColorTexture(0, 0, 0, 0)
	contentBg:SetAllPoints()	
	DFCN_Status.content = content
	local scrollFrame = CreateFrame("ScrollFrame", nil, content)
	scrollFrame:SetPoint("TOPLEFT", 0, -30)
	scrollFrame:SetPoint("BOTTOMRIGHT", 0, 5)
	scrollFrame:SetClipsChildren(true)	
	local scrollChild = CreateFrame("Frame", nil, scrollFrame)
	scrollChild:SetSize(1390, 400)
	scrollFrame:SetScrollChild(scrollChild)	
	local scrollBar = CreateFrame("Slider", nil, scrollFrame, "UIPanelScrollBarTemplate")
	scrollBar:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", -12, -16)
	scrollBar:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", -20, 16)
	scrollBar:SetMinMaxValues(0, 0)
	scrollBar:SetValue(0)
	scrollBar:SetValueStep(20)
	scrollBar:SetWidth(10)
	scrollBar:Hide()	
	scrollBar:SetScript("OnValueChanged", function(self, value)
		scrollFrame:SetVerticalScroll(value)
	end)	
	scrollFrame:EnableMouseWheel(true)
	scrollFrame:SetScript("OnMouseWheel", function(self, delta)
		local current = scrollBar:GetValue()
		local minVal, maxVal = scrollBar:GetMinMaxValues()		
		if delta < 0 then
			current = math.min(maxVal, current + 60)
		else
			current = math.max(minVal, current - 60)
		end		
		scrollBar:SetValue(current)
	end)	
	DFCN_Status.uiFrame = f
	DFCN_Status.scrollFrame = scrollFrame
	DFCN_Status.scrollChild = scrollChild
	DFCN_Status.scrollBar = scrollBar	
	local updateBtn = CreateFrame("Button", nil, f)
	updateBtn:SetSize(100, 25)
	updateBtn:SetPoint("BOTTOMRIGHT", -15, 10)	
	updateBtn.BorderTop = updateBtn:CreateTexture(nil, "BORDER")
	updateBtn.BorderTop:SetPoint("TOPLEFT", 0, 0)
	updateBtn.BorderTop:SetPoint("TOPRIGHT", 0, 0)
	updateBtn.BorderTop:SetHeight(1)
	updateBtn.BorderTop:SetColorTexture(1, 1, 1, 0.7)	
	updateBtn.BorderBottom = updateBtn:CreateTexture(nil, "BORDER")
	updateBtn.BorderBottom:SetPoint("BOTTOMLEFT", 0, 0)
	updateBtn.BorderBottom:SetPoint("BOTTOMRIGHT", 0, 0)
	updateBtn.BorderBottom:SetHeight(1)
	updateBtn.BorderBottom:SetColorTexture(1, 1, 1, 0.7)	
	updateBtn.BorderLeft = updateBtn:CreateTexture(nil, "BORDER")
	updateBtn.BorderLeft:SetPoint("TOPLEFT", 0, 0)
	updateBtn.BorderLeft:SetPoint("BOTTOMLEFT", 0, 0)
	updateBtn.BorderLeft:SetWidth(1)
	updateBtn.BorderLeft:SetColorTexture(1, 1, 1, 0.7)	
	updateBtn.BorderRight = updateBtn:CreateTexture(nil, "BORDER")
	updateBtn.BorderRight:SetPoint("TOPRIGHT", 0, 0)
	updateBtn.BorderRight:SetPoint("BOTTOMRIGHT", 0, 0)
	updateBtn.BorderRight:SetWidth(1)
	updateBtn.BorderRight:SetColorTexture(1, 1, 1, 0.7)	
	updateBtn.text = updateBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	updateBtn.text:SetPoint("CENTER")
	updateBtn.text:SetText(DFCNSL["MANUAL_UPDATE"])
	updateBtn.text:SetTextColor(1, 1, 1)
	updateBtn:SetScript("OnMouseDown", function(self)
		self.text:SetPoint("CENTER", 1, -1)
	end)	
	updateBtn:SetScript("OnMouseUp", function(self)
		self.text:SetPoint("CENTER", 0, 0)
	end)	
	updateBtn:SetScript("OnClick", function()
		local now = GetTime()
		if now - lastUpdateTime < 1 then
			return
		end
		lastUpdateTime = now		
		DFCN_Status:UpdateCharacterData()
		DFCN_Status:UpdateUI()
		print("|T135994:14:14|t |cFF00BFFF[" .. DFCNSL["INFO"] .. "] " .. DFCNSL["DATA_UPDATED"])
	end)	
	updateBtn:SetScript("OnEnter", function(self)
		updateBtn.BorderTop:SetColorTexture(1, 1, 1, 0.9)
		updateBtn.BorderBottom:SetColorTexture(1, 1, 1, 0.9)
		updateBtn.BorderLeft:SetColorTexture(1, 1, 1, 0.9)
		updateBtn.BorderRight:SetColorTexture(1, 1, 1, 0.9)
		updateBtn.text:SetTextColor(1, 0.8, 0)
	end)	
	updateBtn:SetScript("OnLeave", function(self)
		updateBtn.BorderTop:SetColorTexture(1, 1, 1, 0.7)
		updateBtn.BorderBottom:SetColorTexture(1, 1, 1, 0.7)
		updateBtn.BorderLeft:SetColorTexture(1, 1, 1, 0.7)
		updateBtn.BorderRight:SetColorTexture(1, 1, 1, 0.7)
		updateBtn.text:SetTextColor(1, 1, 1)
	end)	
	DFCN_Status.updateBtn = updateBtn
	local playerLevel = UnitLevel("player")
	if playerLevel < 78 then
		updateBtn:Hide()
	end	
	local lastUpdateTime = 0
	updateBtn:SetScript("OnClick", function()
		local now = GetTime()
		if now - lastUpdateTime < 1 then			
			return
		end
		lastUpdateTime = now		
		DFCN_Status:UpdateCharacterData()
		DFCN_Status:UpdateUI()
		print("|T135994:14:14|t |cFF00BFFF[" .. DFCNSL["INFO"] .. "] " .. DFCNSL["DATA_UPDATED"])
	end)	
	f.tipText = f:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
	f.tipText:SetPoint("BOTTOM", 0, 25)
	self:ApplyFontSettings()
end

function DFCN_Status:ApplyFontSettings()
	local offset = self.db.fontOffset or 0	
	if self.titleText then
		self.titleText:SetFont(GameFontNormal:GetFont(), 16 + offset)
	end	
	for _, headerText in pairs(self.headerTexts or {}) do
		headerText:SetFont(GameFontNormal:GetFont(), 14 + offset)
	end	
	if self.scrollChild and self.scrollChild.rows then
		for _, row in ipairs(self.scrollChild.rows) do
			for i = 1, #DELVES_COLUMN_LAYOUT do
				if row["col"..i] then
					row["col"..i]:SetFont(GameFontNormal:GetFont(), 13 + offset)
				end
			end
		end
	end	
	if self.toggleViewBtn and self.toggleViewBtn.text then
		self.toggleViewBtn.text:SetFont(GameFontNormal:GetFont(), 12 + offset)
	end	
	if self.updateBtn and self.updateBtn.text then
		self.updateBtn.text:SetFont(GameFontNormal:GetFont(), 12 + offset)
	end
	local fontAdjustBtn = self.fontAdjustBtn
	if fontAdjustBtn and fontAdjustBtn.text then
		fontAdjustBtn.text:SetFont(GameFontNormal:GetFont(), 12 + offset)
	end	
	if self.fontOffsetSlider then
		if self.fontOffsetSlider.Low then
			self.fontOffsetSlider.Low:SetFont(GameFontNormal:GetFont(), 10 + offset)
		end
		if self.fontOffsetSlider.High then
			self.fontOffsetSlider.High:SetFont(GameFontNormal:GetFont(), 10 + offset)
		end
		if self.fontOffsetSlider.Text then
			self.fontOffsetSlider.Text:SetFont(GameFontNormal:GetFont(), 11 + offset)
		end
	end
	if self.scrollChild and self.scrollChild.rows then
		for _, row in ipairs(self.scrollChild.rows) do
			if row.deleteBtn and row.deleteBtn.text then
				row.deleteBtn.text:SetFont(GameFontNormal:GetFont(), 14 + offset, "OUTLINE")
			end
		end
	end
end

function DFCN_Status:UpdateHeaderTexts()
	if not DFCN_Status.headerTexts then
		DFCN_Status.headerTexts = {}
		return
	end	
	local currentLayout = DFCN_Status.db.viewMode == "delves" and DELVES_COLUMN_LAYOUT or CURRENCY_COLUMN_LAYOUT	
	for _, col in ipairs(currentLayout) do
		if col.sortable and DFCN_Status.headerTexts[col.sortKey] then
			local indicator = ""
			if DFCN_Status.db.sortBy == col.sortKey then
				indicator = DFCN_Status.db.ascending and "↑" or "↓"
			end
			DFCN_Status.headerTexts[col.sortKey]:SetText(col.name .. indicator)
		end
	end
end

function DFCN_Status:UpdateUI()
	if DFCN_Status.header then
		for _, headerText in pairs(DFCN_Status.headerTexts or {}) do
			if headerText and headerText.SetScript then
				headerText:SetScript("OnEnter", nil)
				headerText:SetScript("OnLeave", nil)
				headerText:SetScript("OnMouseDown", nil)
			end
		end		
		DFCN_Status.header:Hide()
		DFCN_Status.header:SetParent(nil)
		DFCN_Status.header = nil
		DFCN_Status.headerTexts = {}
	end	
	if DFCN_Status.scrollChild.rows then
		for i, row in ipairs(DFCN_Status.scrollChild.rows) do
			if row.deleteBtn then
				row.deleteBtn:SetScript("OnClick", nil)
				row.deleteBtn:SetScript("OnEnter", nil)
				row.deleteBtn:SetScript("OnLeave", nil)
			end
			for j = 1, #CURRENCY_COLUMN_LAYOUT do
				if row["col"..j] then
					row["col"..j]:SetScript("OnEnter", nil)
					row["col"..j]:SetScript("OnLeave", nil)
					row["col"..j]:SetScript("OnMouseDown", nil)
				end
			end			
			row:Hide()
			row:SetParent(nil)
		end
		DFCN_Status.scrollChild.rows = {}
	end
	DFCN_Status.headerTexts = DFCN_Status.headerTexts or {}	
	if DFCN_Status.header then
		DFCN_Status.header:Hide()
		DFCN_Status.header = nil
	end	
	DFCN_Status.header = CreateFrame("Frame", nil, DFCN_Status.content)
	DFCN_Status.header:SetSize(1390, 25)
	DFCN_Status.header:SetPoint("TOPLEFT", 5, -5)	
	local currentLayout = DFCN_Status.db.viewMode == "delves" and DELVES_COLUMN_LAYOUT or CURRENCY_COLUMN_LAYOUT
	local totalWidth = 0
	DFCN_Status.headerTexts = {}	
	for i, col in ipairs(currentLayout) do
		local headerText = DFCN_Status.header:CreateFontString(nil, "OVERLAY", "GameFontNormal")
		headerText:SetPoint("LEFT", DFCN_Status.header, "LEFT", totalWidth + (i == 1 and 0 or 10), 0)
		headerText:SetWidth(col.width)
		headerText:SetJustifyH(col.align)
		headerText:SetTextColor(COLORS.TEXT_HIGHLIGHT.r, COLORS.TEXT_HIGHLIGHT.g, COLORS.TEXT_HIGHLIGHT.b)
		headerText:SetText(col.name) 		
		totalWidth = totalWidth + col.width + (i == 1 and 0 or 10)		
		headerText:EnableMouse(true)		
		if col.itemID then
			headerText:SetScript("OnEnter", function(self)
				GameTooltip:SetOwner(self, "ANCHOR_TOP")
				GameTooltip:SetItemByID(col.itemID)
				GameTooltip:Show()
			end)
		elseif col.currencyID then
			headerText:SetScript("OnEnter", function(self)
				GameTooltip:SetOwner(self, "ANCHOR_TOP")
				GameTooltip:SetCurrencyByID(col.currencyID)
				GameTooltip:Show()
			end)
		else
			headerText:SetScript("OnEnter", function(self)
				GameTooltip:SetOwner(self, "ANCHOR_TOP")
				GameTooltip:SetText(COLUMN_TOOLTIPS[col.name] or "N/A", 1, 1, 1)
				GameTooltip:Show()
			end)
		end		
		headerText:SetScript("OnLeave", GameTooltip_Hide)		
		if col.sortable then
			headerText:SetScript("OnMouseDown", function()
				if DFCN_Status.db.sortBy == col.sortKey then
					DFCN_Status.db.ascending = not DFCN_Status.db.ascending
				else
					DFCN_Status.db.sortBy = col.sortKey
					DFCN_Status.db.ascending = false
				end
				DFCN_Status:UpdateUI()
			end)			
			DFCN_Status.headerTexts[col.sortKey] = headerText
		end
	end	
	local line = DFCN_Status.header:CreateTexture(nil, "BACKGROUND")
	line:SetColorTexture(1, 1, 1, 0.3)
	line:SetSize(totalWidth, 1)
	line:SetPoint("BOTTOMLEFT", DFCN_Status.header, "BOTTOMLEFT", 0, 0)	
	self:UpdateHeaderTexts()
	local playerLevel = UnitLevel("player")
	if playerLevel >= 78 then
		DFCN_Status.updateBtn:Show()
	else
		DFCN_Status.updateBtn:Hide()
	end
	if not DFCN_Status.uiFrame or not DFCN_Status.scrollChild then
		self:CreateUI()
	end	
	if DFCN_Status.toggleViewBtn then
		DFCN_Status.toggleViewBtn.text:SetText(DFCN_Status.db.viewMode == "delves" and DFCNSL["SWITCH_TO_CURRENCY"] or DFCNSL["SWITCH_TO_DELVES"])
	end	
	self:UpdateHeaderTexts()	
	if DFCN_Status.scrollChild.rows then
		for i, row in ipairs(DFCN_Status.scrollChild.rows) do
			row:Hide()
			row:SetParent(nil)
		end
	end
	DFCN_Status.scrollChild.rows = {}	
	local sortedChars = {}
	for charKey, charData in pairs(DFCN_Status.db.characters) do
		table.insert(sortedChars, {
			key = charKey,
			charData = charData
		})
	end	
	local sortKey = DFCN_Status.db.sortBy
	for _, charInfo in ipairs(sortedChars) do
		local charData = charInfo.charData		
		if sortKey == "prof1Concentration" then
			charInfo.sortValue = DFCN_Status:GetCurrentConcentration(charData.concentration.prof1, charData.concentration.lastUpdated) or 0
		elseif sortKey == "prof2Concentration" then
			charInfo.sortValue = DFCN_Status:GetCurrentConcentration(charData.concentration.prof2, charData.concentration.lastUpdated) or 0
		elseif sortKey == "goldenCacheStatus" then
			charInfo.sortValue = tonumber(charData.goldenCacheStatus:match("^(%d+)/")) or 0
		elseif sortKey == "name" then
			charInfo.sortValue = charData.class or ""
		elseif sortKey == "keyStatus" then
			charInfo.sortValue = charData.keyStatus and charData.keyStatus.totalKeys or 0
		elseif sortKey == "shardStatus" then
			charInfo.sortValue = charData.shardStatus and charData.shardStatus.weeklyEarned or 0
		elseif sortKey == "treasureStatus" then
			charInfo.sortValue = TreasureStatusToValue(charData.treasureStatus)
		elseif string.find(sortKey, "currency_") then
			local currencyID = tonumber(string.match(sortKey, "currency_(%d+)"))
			if currencyID and charData.currencies and charData.currencies[currencyID] then
				charInfo.sortValue = charData.currencies[currencyID].quantity or 0
			else
				charInfo.sortValue = -1
			end
		else
			charInfo.sortValue = charData[sortKey] or 0
		end
	end	
	table.sort(sortedChars, function(a, b)
		local aHasVault = a.charData.hasWeeklyRewards and 1 or 0
		local bHasVault = b.charData.hasWeeklyRewards and 1 or 0
		if DFCN_Status.db.sortBy == "name" and not DFCN_Status.db.ascending then
			if aHasVault ~= bHasVault then
				return aHasVault > bHasVault
			end
		end		
		if a.sortValue ~= b.sortValue then
			if DFCN_Status.db.ascending then
				return a.sortValue < b.sortValue
			else
				return a.sortValue > b.sortValue
			end
		end		
		return a.key < b.key
	end)	
	local rowIndex = 1
	local rowY = -5
	local function ShowProfConcentrationTooltip(self, profData, lastUpdated)
		if not profData or not profData.hasConcentration then
			GameTooltip:SetOwner(self, "ANCHOR_RIGHT")			
			local displayName = profData.name
			local skillLevel = profData.skillLevel
			local maxSkillLevel = profData.maxSkillLevel			
			if profData.currencyId and not profData.hasConcentration then
				skillLevel = 0
				if not maxSkillLevel or maxSkillLevel <= 0 then
					maxSkillLevel = 100
				end
			end			
			if displayName ~= DFCNSL["NO_PROFESSION"] then
				if skillLevel and maxSkillLevel and maxSkillLevel > 0 then
					displayName = string.format("%s[%d/%d]", displayName, skillLevel, maxSkillLevel)
				end
			end			
			local coloredName = "|cffFFD700" .. displayName .. "|r"
			GameTooltip:SetText(coloredName .. " " .. DFCNSL["CONCENTRATION_VALUE"], 1, 1, 1)
			GameTooltip:AddLine(" ")			
			if profData.name == DFCNSL["NO_PROFESSION"] then
				GameTooltip:AddLine(DFCNSL["NOT_LEARNED"], 1, 0, 0)
			elseif profData.currencyId and not profData.hasConcentration then
				GameTooltip:AddLine(DFCNSL["NOT_LEARNED"], 1, 0, 0)
			elseif NO_CONCENTRATION_PROFESSIONS[profData.skillLine] then
				GameTooltip:AddLine(DFCNSL["NO_CONCENTRATION_TIP"], 1, 0, 0)
			end			
			GameTooltip:Show()
			return
		end		
		GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
		local skillText = ""
		if profData.skillLevel and profData.maxSkillLevel and profData.skillLevel > 0 then
			skillText = " [" .. profData.skillLevel .. "/" .. profData.maxSkillLevel .. "]"
		end
		local coloredName = "|cffFFD700" .. profData.name .. skillText .. "|r"
		GameTooltip:SetText(coloredName .. " " .. DFCNSL["CONCENTRATION_VALUE"], 1, 1, 1)
		GameTooltip:AddLine(" ")		
		local current = DFCN_Status:GetCurrentConcentration(profData, lastUpdated)
		local timeToMax = DFCNSL["CONCENTRATION_FULL"]
		local fullTimeText = ""
		if current < CONCENTRATION_MAX then
			local pointsNeeded = CONCENTRATION_MAX - current
			local secondsNeeded = pointsNeeded * CONCENTRATION_RECOVERY_RATE
			timeToMax = FormatTime(secondsNeeded)			
			local fullTimestamp = GetPreciseTime() + secondsNeeded
			if locale == "zhCN" or locale == "zhTW" then
				fullTimeText = date("%m-%d %H:%M", fullTimestamp)
			else
				fullTimeText = date("%d/%m %H:%M", fullTimestamp)
			end
		else
			fullTimeText = DFCNSL["CONCENTRATION_FULL"]
		end		
		local r, g, b = GetConcentrationColor(current).r, GetConcentrationColor(current).g, GetConcentrationColor(current).b
		GameTooltip:AddDoubleLine(DFCNSL["CURRENT_VALUE"], string.format("%.2f/%d", current, CONCENTRATION_MAX), 1, 1, 1, r, g, b)
		GameTooltip:AddDoubleLine(DFCNSL["TIME_TO_FULL"], timeToMax, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8)		
		GameTooltip:AddDoubleLine(DFCNSL["ESTIMATED_FULL_TIME"], fullTimeText, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8)		
		GameTooltip:AddLine(" ")
		local lastUpdatedFormat
		if locale == "zhCN" or locale == "zhTW" then
			lastUpdatedFormat = date("%m-%d %H:%M", lastUpdated)
		else
			lastUpdatedFormat = date("%d/%m %H:%M", lastUpdated)
		end
		GameTooltip:AddLine(DFCNSL["LAST_LOGIN_TIME"] .. ": " .. lastUpdatedFormat, 0.6, 0.6, 0.6)
		GameTooltip:Show()
	end	
	local currentLayout = DFCN_Status.db.viewMode == "delves" and DELVES_COLUMN_LAYOUT or CURRENCY_COLUMN_LAYOUT	
	for _, charInfo in ipairs(sortedChars) do
		local charData = charInfo.charData
		if charData then
			local row = DFCN_Status.scrollChild.rows[rowIndex]
			if not row then
				row = CreateFrame("Frame", nil, DFCN_Status.scrollChild)
				row:SetSize(1370, 20)
				DFCN_Status.scrollChild.rows[rowIndex] = row
				row.BorderTop = row:CreateTexture(nil, "BORDER")
				row.BorderTop:SetPoint("TOPLEFT", row, "TOPLEFT", 0, 0)
				row.BorderTop:SetPoint("TOPRIGHT", row, "TOPRIGHT", 0, 0)
				row.BorderTop:SetHeight(1)
				row.BorderTop:SetColorTexture(0, 0, 0, 0)				
				row.BorderBottom = row:CreateTexture(nil, "BORDER")
				row.BorderBottom:SetPoint("BOTTOMLEFT", row, "BOTTOMLEFT", 0, 0)
				row.BorderBottom:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", 0, 0)
				row.BorderBottom:SetHeight(1)
				row.BorderBottom:SetColorTexture(0, 0, 0, 0)				
				local prevCol
				for i, col in ipairs(currentLayout) do
					local colText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
					colText:SetWidth(col.width)
					colText:SetJustifyH(col.align)					
					if i == 1 then
						colText:SetPoint("LEFT", row, "LEFT", 0, 0)
					else
						colText:SetPoint("LEFT", prevCol, "RIGHT", 10, 0)
					end					
					row["col"..i] = colText
					prevCol = colText
				end				
				local deleteBtn = CreateFrame("Button", nil, row)
				deleteBtn:SetSize(20, 20)
				deleteBtn:SetPoint("CENTER", row["col"..#currentLayout], "CENTER", 0, 0)				
				deleteBtn.text = deleteBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
				deleteBtn.text:SetPoint("CENTER")
				deleteBtn.text:SetText("x")
				deleteBtn.text:SetTextColor(1, 0.3, 0.3)
				deleteBtn.text:SetFont(GameFontNormal:GetFont(), 14, "OUTLINE")				
				deleteBtn:SetScript("OnEnter", function(self)
					self.text:SetTextColor(1, 0.1, 0.1)
					GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
					GameTooltip:SetText(DFCNSL["DELETE_CHAR_TOOLTIP"], 1, 0.1, 0.1)
					GameTooltip:Show()
				end)				
				deleteBtn:SetScript("OnLeave", function(self)
					self.text:SetTextColor(1, 0.3, 0.3)
					GameTooltip_Hide()
				end)				
				row.deleteBtn = deleteBtn
			end
			row:SetPoint("TOPLEFT", DFCN_Status.scrollChild, "TOPLEFT", 5, rowY)
			row:Show()			
			row.deleteBtn:SetScript("OnClick", function()
				StaticPopupDialogs["DFCN_DELETE_CHARACTER"] = {
					text = string.format(DFCNSL["DELETE_CHAR_CONFIRM"], charInfo.key),
					button1 = DFCNSL["DELETE"],
					button2 = DFCNSL["CANCEL"],
					OnAccept = function()
						DFCN_Status.db.characters[charInfo.key] = nil
						DFCN_Status:UpdateUI()
						print("|T135994:14:14|t |cFFFF0000[" .. DFCNSL["DELETE"] .. "] " .. string.format(DFCNSL["CHARACTER_DELETED"], charInfo.key) .. "|r")
					end,
					timeout = 0,
					whileDead = true,
					hideOnEescape = true,
					preferredIndex = 3,
					showAlert = true
				}
				StaticPopup_Show("DFCN_DELETE_CHARACTER")
			end)			
			for i = 1, #currentLayout do
				if row["col"..i] then
					row["col"..i]:SetTextColor(1, 1, 1)
				end
			end
			local enhancedColor = ENHANCED_CLASS_COLORS[charData.class] or {r=1, g=1, b=1}
			local currentCharKey = DFCN_Status:GetCharacterKey()
			if charInfo.key == currentCharKey then
				row.BorderTop:SetColorTexture(1, 0.8, 0, 0.5)
				row.BorderBottom:SetColorTexture(1, 0.8, 0, 0.5)
			else
				row.BorderTop:SetColorTexture(0, 0, 0, 0)
				row.BorderBottom:SetColorTexture(0, 0, 0, 0)
			end
			local displayCharKey = string.gsub(charInfo.key, "-", " - ")			
			local isCurrentChar = (charInfo.key == currentCharKey)
			local restPercent, restSecondsToFull, restHas = self:GetCharacterRestXP(charData, isCurrentChar)
			local restText = ""
			if restPercent and restPercent > 0 then
				local percentInt = math.floor(restPercent + 0.5)
				local maxPercent = (charData.restXP and charData.restXP.isPandaren) and 300 or 150
				local redThreshold = maxPercent
				local orangeThreshold = math.floor(maxPercent * 80 / 150 + 0.5)
				local r, g, b
				if percentInt >= redThreshold then
					r, g, b = 1, 0.25, 0.25
				elseif percentInt > orangeThreshold then
					r, g, b = 1, 0.65, 0
				else
					r, g, b = 0.25, 1, 0.25
				end
				restText = string.format(
					"|cff%02x%02x%02x[%d%%]|r",
					math.floor(r * 255),
					math.floor(g * 255),
					math.floor(b * 255),
					percentInt
				)
			end
			if charData.hasWeeklyRewards then
				displayCharKey = "|A:GreatVault-32x32:18:18|a" .. displayCharKey
			end
			if row and row.col1 then
				row.col1:SetText(displayCharKey .. restText)
				row.col1:SetTextColor(enhancedColor.r, enhancedColor.g, enhancedColor.b)
			end
			local _displayCharKey = displayCharKey
			local _charData = charData
			local _restPercent = restPercent
			local _restSecondsToFull = restSecondsToFull
			local _enhancedColor = enhancedColor
			row.col1:SetScript("OnEnter", function(self)
				local charData = _charData
				local restPercent = _restPercent
				local restSecondsToFull = _restSecondsToFull
				local enhancedColor = _enhancedColor
				local displayCharKey = _displayCharKey
				GameTooltip:SetOwner(self, "ANCHOR_TOP")
				local hasRestData = (restPercent and restPercent > 0)
				local hasVaultWarning = charData and charData.hasWeeklyRewards
				if not hasRestData and not hasVaultWarning then
					GameTooltip:Hide()
					return
				end
				if hasVaultWarning and not hasRestData then
					GameTooltip:AddLine(DFCNSL["GREAT_VAULT_NOT_COLLECTED"], 1, 0, 0)
					GameTooltip:Show()
					return
				end
				if hasRestData then
					local charLevel = (charData and charData.level) or "?"
					GameTooltip:AddLine(
						string.format("%s (Lv.%s)", displayCharKey, charLevel),
						enhancedColor.r, enhancedColor.g, enhancedColor.b
					)
					if hasVaultWarning then
						GameTooltip:AddLine(" ")
						GameTooltip:AddLine(DFCNSL["GREAT_VAULT_NOT_COLLECTED"], 1, 0, 0)
					end
					GameTooltip:AddLine(" ")
					GameTooltip:AddLine(DFCNSL["REST_XP"], 1, 1, 1)
					local maxPercent = (charData.restXP and charData.restXP.isPandaren) and 300 or 150
					local redThreshold = maxPercent
					local orangeThreshold = math.floor(maxPercent * 80 / 150 + 0.5)
					local percentInt = math.floor(restPercent + 0.5)
					local rightR, rightG, rightB
					if percentInt >= redThreshold then
						rightR, rightG, rightB = 1, 0, 0
					elseif percentInt > orangeThreshold then
						rightR, rightG, rightB = 1, 0.65, 0
					else
						rightR, rightG, rightB = 0.25, 1, 0.25
					end
					GameTooltip:AddDoubleLine(
						DFCNSL["CURRENT_VALUE"],
						string.format("%d%% / %d%%", percentInt, maxPercent),
						1, 1, 1,
						rightR, rightG, rightB
					)
					if restSecondsToFull and restSecondsToFull > 0 then
						local timeToFull
						if FormatTime then
							timeToFull = FormatTime(restSecondsToFull)
						else
							timeToFull = tostring(math.floor(restSecondsToFull))
						end
						GameTooltip:AddDoubleLine(
							DFCNSL["TIME_TO_FULL_REST"],
							timeToFull,
							1, 1, 1,
							0.8, 0.8, 0.8
						)
						local fullTimestamp = time() + restSecondsToFull
						local fullTimeText
						if locale == "zhCN" or locale == "zhTW" then
							fullTimeText = date("%m-%d %H:%M", fullTimestamp)
						else
							fullTimeText = date("%d/%m %H:%M", fullTimestamp)
						end
						GameTooltip:AddDoubleLine(
							DFCNSL["ESTIMATED_FULL_TIME"],
							fullTimeText,
							1, 1, 1,
							0.8, 0.8, 0.8
						)
					else
						GameTooltip:AddLine(DFCNSL["REST_XP_FULL"], 1, 0, 0)
					end
					if charData.restXP and not charData.restXP.wasResting then
						GameTooltip:AddLine("|TInterface\\Icons\\trade_archaeology_theinnkeepersdaughter:36:36|t")
					end
					if charData.restXP and charData.restXP.isPandaren then
						GameTooltip:AddLine("|TInterface\\Icons\\pandarenracial_innerpeace:36:36|t")
					end
					if charData and charData.restXP and charData.restXP.lastUpdated then
						local lastUpdatedFormat

						if locale == "zhCN" or locale == "zhTW" then
							lastUpdatedFormat = date("%m-%d %H:%M", charData.restXP.lastUpdated)
						else
							lastUpdatedFormat = date("%d/%m %H:%M", charData.restXP.lastUpdated)
						end
						GameTooltip:AddLine(" ")
						GameTooltip:AddLine(
							DFCNSL["LAST_LOGIN_TIME"] .. ": " .. lastUpdatedFormat,
							0.6, 0.6, 0.6
						)
					end
				end
				GameTooltip:Show()
			end)
			row.col1:SetScript("OnLeave", GameTooltip_Hide)			
			if DFCN_Status.db.viewMode == "delves" then
				local prof1Current, hasConcentration1 = DFCN_Status:GetCurrentConcentration(charData.concentration.prof1, charData.concentration.lastUpdated)
				local prof1Name = charData.concentration.prof1.name
				local prof1Text = ""
				if prof1Name == DFCNSL["NO_PROFESSION"] then
					prof1Text = DFCNSL["NO_PROFESSION"]
					row.col2:SetText(prof1Text)
					row.col2:SetTextColor(COLORS.TEXT_GRAY.r, COLORS.TEXT_GRAY.g, COLORS.TEXT_GRAY.b)
				elseif hasConcentration1 then
					local displayValue = math.floor(prof1Current + 0.5)
					prof1Text = string.format("%s (%d)", ShortenProfName(prof1Name), displayValue)
					row.col2:SetText(prof1Text)
					local color = GetConcentrationColor(prof1Current)
					local skillLevel = charData.concentration.prof1.skillLevel
					local maxSkillLevel = charData.concentration.prof1.maxSkillLevel
					local isLowSkill = false
					if maxSkillLevel and maxSkillLevel > 0 and skillLevel and skillLevel / maxSkillLevel < 0.2 then
						isLowSkill = true
					elseif not (skillLevel and maxSkillLevel and maxSkillLevel > 0) then
						isLowSkill = true
					end
					if isLowSkill then
						row.col2:SetTextColor(COLORS.TEXT_GRAY.r, COLORS.TEXT_GRAY.g, COLORS.TEXT_GRAY.b)
					else
						row.col2:SetTextColor(color.r, color.g, color.b)
					end
				else
					prof1Text = ShortenProfName(prof1Name)
					row.col2:SetText(prof1Text)
					row.col2:SetTextColor(COLORS.TEXT_GRAY.r, COLORS.TEXT_GRAY.g, COLORS.TEXT_GRAY.b)
				end
				local prof2Current, hasConcentration2 = DFCN_Status:GetCurrentConcentration(charData.concentration.prof2, charData.concentration.lastUpdated)
				local prof2Name = charData.concentration.prof2.name
				local prof2Text = ""
				if prof2Name == DFCNSL["NO_PROFESSION"] then
					prof2Text = DFCNSL["NO_PROFESSION"]
					row.col3:SetText(prof2Text)
					row.col3:SetTextColor(COLORS.TEXT_GRAY.r, COLORS.TEXT_GRAY.g, COLORS.TEXT_GRAY.b)
				elseif hasConcentration2 then
					local displayValue = math.floor(prof2Current + 0.5)
					prof2Text = string.format("%s (%d)", ShortenProfName(prof2Name), displayValue)
					row.col3:SetText(prof2Text)
					local color = GetConcentrationColor(prof2Current)
					local skillLevel = charData.concentration.prof2.skillLevel
					local maxSkillLevel = charData.concentration.prof2.maxSkillLevel
					local isLowSkill = false
					if maxSkillLevel and maxSkillLevel > 0 and skillLevel and skillLevel / maxSkillLevel < 0.2 then
						isLowSkill = true
					elseif not (skillLevel and maxSkillLevel and maxSkillLevel > 0) then
						isLowSkill = true
					end
					if isLowSkill then
						row.col3:SetTextColor(COLORS.TEXT_GRAY.r, COLORS.TEXT_GRAY.g, COLORS.TEXT_GRAY.b)
					else
						row.col3:SetTextColor(color.r, color.g, color.b)
					end
				else
					prof2Text = ShortenProfName(prof2Name)
					row.col3:SetText(prof2Text)
					row.col3:SetTextColor(COLORS.TEXT_GRAY.r, COLORS.TEXT_GRAY.g, COLORS.TEXT_GRAY.b)
				end				
				local keyStatus = charData.keyStatus or {totalKeys = 0}
				local keyText = tostring(keyStatus.totalKeys)
				row.col4:SetText(keyText)
				if keyStatus.totalKeys > 0 then
					row.col4:SetTextColor(COLORS.TEXT_GREEN.r, COLORS.TEXT_GREEN.g, COLORS.TEXT_GREEN.b)
				else
					row.col4:SetTextColor(COLORS.TEXT_GRAY.r, COLORS.TEXT_GRAY.g, COLORS.TEXT_GRAY.b)
				end
				local shardStatus = charData.shardStatus or {totalShards = 0, seasonEarned = 0, weeklyEarned = 0}
				local shardCurrencyInfo = C_CurrencyInfo.GetCurrencyInfo(3310)
				local maxShards = shardCurrencyInfo and shardCurrencyInfo.maxWeeklyQuantity or 600
				local weeklyEarned = shardStatus.weeklyEarned or shardStatus.totalShards
				local shardText = weeklyEarned .. "/" .. maxShards
				row.col5:SetText(shardText)
				if weeklyEarned >= maxShards then
					row.col5:SetTextColor(COLORS.TEXT_GREEN.r, COLORS.TEXT_GREEN.g, COLORS.TEXT_GREEN.b)
				else
					row.col5:SetTextColor(COLORS.TEXT_RED.r, COLORS.TEXT_RED.g, COLORS.TEXT_RED.b)
				end
				row.col5:SetScript("OnEnter", function(self)
					GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
					GameTooltip:SetText(DFCNSL["SHARD_ACQUISITION"], 1, 1, 1)
					GameTooltip:AddLine(" ")
					GameTooltip:AddDoubleLine(DFCNSL["WEEKLY_EARNED"], tostring(weeklyEarned), 1, 1, 1, 1, 1, 1)
					GameTooltip:AddDoubleLine(DFCNSL["WEEKLY_CAP"], tostring(maxShards), 1, 1, 1, 1, 1, 1)
					if weeklyEarned >= maxShards then
						GameTooltip:AddLine(DFCNSL["REACHED_WEEKLY_CAP"], 1, 0, 0)
					else
						local needed = maxShards - weeklyEarned
						GameTooltip:AddLine(string.format(DFCNSL["NEED_MORE_WEEKLY"], needed), 0, 1, 0)
					end
					GameTooltip:Show()
				end)
				row.col5:SetScript("OnLeave", GameTooltip_Hide)
				local delveCount = charData.delveCount or 0
				local slotsUnlocked = charData.worldSlotsUnlocked or 0
				row.col6:SetText(delveCount .. " | " .. slotsUnlocked)
				if delveCount >= 8 then
					row.col6:SetTextColor(COLORS.TEXT_GREEN.r, COLORS.TEXT_GREEN.g, COLORS.TEXT_GREEN.b)
				elseif delveCount >= 4 then
					row.col6:SetTextColor(1, 1, 0)
				elseif delveCount >= 2 then
					row.col6:SetTextColor(1, 0.65, 0)
				else
					row.col6:SetTextColor(COLORS.TEXT_RED.r, COLORS.TEXT_RED.g, COLORS.TEXT_RED.b)
				end
				row.col7:SetText(charData.treasureStatus)
				if charData.treasureStatus == DFCNSL["IN_BAG"] then
					row.col7:SetTextColor(COLORS.TEXT_HIGHLIGHT.r, COLORS.TEXT_HIGHLIGHT.g, COLORS.TEXT_HIGHLIGHT.b)
				elseif charData.treasureStatus == DFCNSL["USED"] then
					row.col7:SetTextColor(COLORS.TEXT_GREEN.r, COLORS.TEXT_GREEN.g, COLORS.TEXT_GREEN.b)
				else
					row.col7:SetTextColor(COLORS.TEXT_RED.r, COLORS.TEXT_RED.g, COLORS.TEXT_RED.b)
				end
				row.col8:SetText(charData.goldenCacheStatus)
				if charData.goldenCacheStatus == "4/4" then
					row.col8:SetTextColor(COLORS.TEXT_GREEN.r, COLORS.TEXT_GREEN.g, COLORS.TEXT_GREEN.b)
				elseif charData.goldenCacheStatus == "3/4" then
					row.col8:SetTextColor(COLORS.TEXT_YELLOW.r, COLORS.TEXT_YELLOW.g, COLORS.TEXT_YELLOW.b)
				elseif charData.goldenCacheStatus == "2/4" then
					row.col8:SetTextColor(1, 0.65, 0)
				else
					row.col8:SetTextColor(COLORS.TEXT_RED.r, COLORS.TEXT_RED.g, COLORS.TEXT_RED.b)
				end
				local manaCrystalData = charData.currencies and charData.currencies[3356]
				local manaCrystalQuantity = manaCrystalData and manaCrystalData.quantity or nil
				if manaCrystalQuantity == nil then
					row.col9:SetText("n/a")
					row.col9:SetTextColor(COLORS.TEXT_GRAY.r, COLORS.TEXT_GRAY.g, COLORS.TEXT_GRAY.b)
				else
					local weeklyEarned = manaCrystalData.weeklyEarned or 0
					row.col9:SetText(manaCrystalQuantity .. "/" .. weeklyEarned)
					local manaCurrencyInfo = C_CurrencyInfo.GetCurrencyInfo(3356)
					local maxQuantity = manaCurrencyInfo and manaCurrencyInfo.maxQuantity
					local maxWeeklyQuantity = manaCurrencyInfo and manaCurrencyInfo.maxWeeklyQuantity
					local weeklyEarned = manaCrystalData.weeklyEarned or 0
					local isCapped = (maxWeeklyQuantity and maxWeeklyQuantity > 0 and weeklyEarned >= maxWeeklyQuantity)
								  or (maxQuantity and maxQuantity > 0 and manaCrystalQuantity >= maxQuantity)
					if isCapped then
						row.col9:SetTextColor(COLORS.TEXT_GREEN.r, COLORS.TEXT_GREEN.g, COLORS.TEXT_GREEN.b)
					else
						row.col9:SetTextColor(COLORS.TEXT_RED.r, COLORS.TEXT_RED.g, COLORS.TEXT_RED.b)
					end
				end
				row.col9:SetScript("OnEnter", function(self)
					GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
					GameTooltip:SetText(DFCNSL["CURRENCY_MANA_CRYSTAL"], 1, 1, 1)
					GameTooltip:AddLine(" ")					
					if manaCrystalQuantity == nil then
						GameTooltip:AddLine(DFCNSL["CURRENCY_TOOLTIP_UNKNOWN"], 1, 0, 0)
						GameTooltip:AddLine(DFCNSL["CURRENCY_TOOLTIP_LOGIN"], 0.8, 0.8, 0.8)
					else
						GameTooltip:AddDoubleLine(DFCNSL["QUANTITY_HELD"], tostring(manaCrystalQuantity), 1, 1, 1, 1, 1, 1)						
						local currentCurrencyInfo = C_CurrencyInfo.GetCurrencyInfo(3356)
						local weeklyEarned = (charData.currencies and charData.currencies[3356] and charData.currencies[3356].weeklyEarned) or 0						
						GameTooltip:AddDoubleLine(DFCNSL["WEEKLY_EARNED"], tostring(weeklyEarned), 1, 1, 1, 1, 1, 1)						
						if currentCurrencyInfo and currentCurrencyInfo.maxWeeklyQuantity and currentCurrencyInfo.maxWeeklyQuantity > 0 then
							GameTooltip:AddDoubleLine(DFCNSL["WEEKLY_CAP"], tostring(currentCurrencyInfo.maxWeeklyQuantity), 1, 1, 1, 1, 1, 1)
							if weeklyEarned >= currentCurrencyInfo.maxWeeklyQuantity then
								GameTooltip:AddLine(DFCNSL["REACHED_WEEKLY_CAP"], 1, 0, 0)
							else
								local needed = currentCurrencyInfo.maxWeeklyQuantity - weeklyEarned
								GameTooltip:AddLine(string.format(DFCNSL["NEED_MORE_WEEKLY"], needed), 0, 1, 0)
							end
						end						
						if currentCurrencyInfo and currentCurrencyInfo.maxQuantity and currentCurrencyInfo.maxQuantity > 0 then
							GameTooltip:AddDoubleLine(DFCNSL["HOLDING_CAP"], tostring(currentCurrencyInfo.maxQuantity), 1, 1, 1, 1, 1, 1)
							if manaCrystalQuantity >= currentCurrencyInfo.maxQuantity then
								GameTooltip:AddLine(DFCNSL["REACHED_CAP"], 1, 0, 0)
							else
								local needed = currentCurrencyInfo.maxQuantity - manaCrystalQuantity
								GameTooltip:AddLine(string.format(DFCNSL["HOLD_MORE"], needed), 0, 1, 0)
							end
						end
					end
					GameTooltip:Show()
				end)
				row.col9:SetScript("OnLeave", GameTooltip_Hide)
				local voidCoreData = charData.currencies and charData.currencies[3418]
				local voidCoreQuantity = voidCoreData and voidCoreData.quantity or 0
				local voidCoreTotalEarned = voidCoreData and voidCoreData.totalEarned or 0
				local voidInfo = C_CurrencyInfo.GetCurrencyInfo(3418)
				local maxVoid = voidInfo and voidInfo.maxQuantity or 2
				row.col10:SetText(voidCoreQuantity .. "/" .. maxVoid)
				if maxVoid > 0 and voidCoreTotalEarned >= maxVoid then
					row.col10:SetTextColor(COLORS.TEXT_GREEN.r, COLORS.TEXT_GREEN.g, COLORS.TEXT_GREEN.b)
				else
					row.col10:SetTextColor(COLORS.TEXT_RED.r, COLORS.TEXT_RED.g, COLORS.TEXT_RED.b)
				end
				row.col10:SetScript("OnEnter", function(self)
					GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
					GameTooltip:SetText(DFCNSL["CURRENCY_VOID_CORE"], 1, 1, 1)
					GameTooltip:AddLine(" ")
					GameTooltip:AddDoubleLine(DFCNSL["QUANTITY_HELD"], tostring(voidCoreQuantity), 1, 1, 1, 1, 1, 1)
					if maxVoid > 0 then
						GameTooltip:AddDoubleLine(DFCNSL["SEASON_CAP"], tostring(maxVoid), 1, 1, 1, 1, 1, 1)
						if voidCoreTotalEarned >= maxVoid then
							GameTooltip:AddLine(DFCNSL["REACHED_CAP"], 1, 0, 0)
						else
							local needed = maxVoid - voidCoreTotalEarned
							GameTooltip:AddLine(string.format(DFCNSL["NEED_MORE"], needed), 0, 1, 0)
						end
					end
					GameTooltip:Show()
				end)
				row.col10:SetScript("OnLeave", GameTooltip_Hide)				
				local ilvl = charData.overallItemLevel
				local prefix = ""
				if charData.gearLevelStatus == "myth" then
					prefix = "|A:GO-icon-Lead-Applied:20:20|a"
				elseif charData.gearLevelStatus == "hero" then
					prefix = "|A:GO-icon-Header-Assist-Applied:20:20|a"
				end
				if ilvl then
					row.col11:SetText(prefix .. string.format("%.2f", ilvl))
				else
					row.col11:SetText(prefix .. "N/A")
				end
				if charData.gearLevelStatus == "myth" then
					row.col11:SetTextColor(COLORS.TEXT_MYTH.r, COLORS.TEXT_MYTH.g, COLORS.TEXT_MYTH.b)
				elseif charData.gearLevelStatus == "hero" then
					row.col11:SetTextColor(COLORS.TEXT_HERO.r, COLORS.TEXT_HERO.g, COLORS.TEXT_HERO.b)
				else
					row.col11:SetTextColor(COLORS.TEXT_PURPLE.r, COLORS.TEXT_PURPLE.g, COLORS.TEXT_PURPLE.b)
				end			
				row.col2:SetScript("OnEnter", function(self)
					ShowProfConcentrationTooltip(self, charData.concentration.prof1, charData.concentration.lastUpdated)
				end)				
				row.col3:SetScript("OnEnter", function(self)
					ShowProfConcentrationTooltip(self, charData.concentration.prof2, charData.concentration.lastUpdated)
				end)				
				row.col2:SetScript("OnLeave", GameTooltip_Hide)
				row.col3:SetScript("OnLeave", GameTooltip_Hide)
			else
				for i = 2, #CURRENCY_COLUMN_LAYOUT - 1 do
					local col = CURRENCY_COLUMN_LAYOUT[i]
					if col.currencyID then
						local currencyData = charData.currencies and charData.currencies[col.currencyID]
						local quantity = currencyData and currencyData.quantity or 0
						local totalEarned = currencyData and currencyData.totalEarned or 0
						local currencyText = currencyData and tostring(quantity) or DFCNSL["UNKNOWN"]
						local textColor = COLORS.TEXT_GREEN						
						local currentCurrencyInfo = C_CurrencyInfo.GetCurrencyInfo(col.currencyID)
						if currentCurrencyInfo and currentCurrencyInfo.maxQuantity and currentCurrencyInfo.maxQuantity >= 0 then
							if col.currencyID == 3376 then
								if quantity >= currentCurrencyInfo.maxQuantity then
									textColor = COLORS.TEXT_RED
								else
									textColor = COLORS.TEXT_GREEN
								end
							elseif col.currencyID == 3212 then
								if currentCurrencyInfo.maxQuantity and totalEarned >= currentCurrencyInfo.maxQuantity then
									textColor = COLORS.TEXT_RED
								end
							else
								if totalEarned >= currentCurrencyInfo.maxQuantity then
									textColor = COLORS.TEXT_RED
								end
							end
						end
						if col.currencyID == 2803 then
							if quantity > 0 then
								textColor = COLORS.TEXT_GREEN
							else
								textColor = COLORS.TEXT_RED
							end
						end						
						row["col"..i]:SetText(currencyText)
						row["col"..i]:SetTextColor(textColor.r, textColor.g, textColor.b)						
						row["col"..i]:SetScript("OnEnter", function(self)
							GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
							local currentCurrencyInfo = C_CurrencyInfo.GetCurrencyInfo(col.currencyID)
							if currencyData then
								GameTooltip:SetText(CURRENCY_NAMES[col.currencyID] or currentCurrencyInfo.name, 1, 1, 1)
								GameTooltip:AddLine(" ")
								GameTooltip:AddDoubleLine(DFCNSL["QUANTITY_HELD"], tostring(quantity), 1, 1, 1, 1, 1, 1)
								if col.currencyID == 3376 then
									if currentCurrencyInfo and currentCurrencyInfo.maxQuantity then
										GameTooltip:AddDoubleLine(DFCNSL["SEASON_CAP"], tostring(currentCurrencyInfo.maxQuantity), 1, 1, 1, 1, 1, 1)
										if quantity >= currentCurrencyInfo.maxQuantity then
											GameTooltip:AddLine(DFCNSL["REACHED_CAP"], 1, 0, 0)
										else
											local needed = currentCurrencyInfo.maxQuantity - quantity
											GameTooltip:AddLine(string.format(DFCNSL["HOLD_MORE"], needed), 0, 1, 0)
										end
									end
								elseif col.currencyID == 3378 then
									if currentCurrencyInfo.maxQuantity and currentCurrencyInfo.maxQuantity >= 0 then
										GameTooltip:AddDoubleLine(DFCNSL["HOLDING_CAP"], tostring(currentCurrencyInfo.maxQuantity), 1, 1, 1, 1, 1, 1)
										if quantity >= currentCurrencyInfo.maxQuantity then
											GameTooltip:AddLine(DFCNSL["REACHED_CAP"], 1, 0, 0)
										else
											local needed = currentCurrencyInfo.maxQuantity - quantity
											GameTooltip:AddLine(string.format(DFCNSL["HOLD_MORE"], needed), 0, 1, 0)
										end
									end
								elseif col.currencyID == 3356 then
									local weeklyEarned = (charData.currencies and charData.currencies[3356] and charData.currencies[3356].weeklyEarned) or 0
									local currentCurrencyInfo = C_CurrencyInfo.GetCurrencyInfo(3356)									
									GameTooltip:AddDoubleLine(DFCNSL["WEEKLY_EARNED"], tostring(weeklyEarned), 1, 1, 1, 1, 1, 1)									
									if currentCurrencyInfo and currentCurrencyInfo.maxWeeklyQuantity and currentCurrencyInfo.maxWeeklyQuantity > 0 then
										GameTooltip:AddDoubleLine(DFCNSL["WEEKLY_CAP"], tostring(currentCurrencyInfo.maxWeeklyQuantity), 1, 1, 1, 1, 1, 1)
										if weeklyEarned >= currentCurrencyInfo.maxWeeklyQuantity then
											GameTooltip:AddLine(DFCNSL["REACHED_WEEKLY_CAP"], 1, 0, 0)
										else
											local needed = currentCurrencyInfo.maxWeeklyQuantity - weeklyEarned
											GameTooltip:AddLine(string.format(DFCNSL["NEED_MORE_WEEKLY"], needed), 0, 1, 0)
										end
									end									
									if currentCurrencyInfo and currentCurrencyInfo.maxQuantity and currentCurrencyInfo.maxQuantity > 0 then
										GameTooltip:AddDoubleLine(DFCNSL["HOLDING_CAP"], tostring(currentCurrencyInfo.maxQuantity), 1, 1, 1, 1, 1, 1)
										if quantity >= currentCurrencyInfo.maxQuantity then
											GameTooltip:AddLine(DFCNSL["REACHED_CAP"], 1, 0, 0)
										else
											local needed = currentCurrencyInfo.maxQuantity - quantity
											GameTooltip:AddLine(string.format(DFCNSL["HOLD_MORE"], needed), 0, 1, 0)
										end
									end
								else
									local specialCurrencies = {[2803] = true, [1166] = true}
									if not specialCurrencies[col.currencyID] then
										GameTooltip:AddDoubleLine(DFCNSL["SEASON_EARNED"], tostring(totalEarned), 1, 1, 1, 1, 1, 1)
										if currentCurrencyInfo.maxQuantity and currentCurrencyInfo.maxQuantity >= 0 and 
										   not specialCurrencies[col.currencyID] then
											GameTooltip:AddDoubleLine(DFCNSL["SEASON_CAP"], tostring(currentCurrencyInfo.maxQuantity), 1, 1, 1, 1, 1, 1)
										end
										if currentCurrencyInfo.maxQuantity and currentCurrencyInfo.maxQuantity >= 0 then
											if totalEarned >= currentCurrencyInfo.maxQuantity then
												GameTooltip:AddLine(DFCNSL["REACHED_CAP"], 1, 0, 0)
											else
												GameTooltip:AddLine(string.format(DFCNSL["NEED_MORE"], currentCurrencyInfo.maxQuantity - totalEarned), 0, 1, 0)
											end
										end
									end
								end
							else
								GameTooltip:SetText(CURRENCY_NAMES[col.currencyID] or DFCNSL["CURRENCY"], 1, 1, 1)
								GameTooltip:AddLine(" ")
								GameTooltip:AddLine(DFCNSL["CURRENCY_TOOLTIP_UNKNOWN"], 1, 0, 0)
								GameTooltip:AddLine(DFCNSL["CURRENCY_TOOLTIP_LOGIN"], 0.8, 0.8, 0.8)
							end
							GameTooltip:Show()
						end)						
						row["col"..i]:SetScript("OnLeave", GameTooltip_Hide)
					end
				end
			end			
			rowIndex = rowIndex + 1
			rowY = rowY - 20
		end
	end	
	local totalHeight = 20 * (#sortedChars)
	local visibleHeight = 280	
	DFCN_Status.scrollChild:SetHeight(totalHeight)	
	local maxScroll = math.max(0, totalHeight - visibleHeight)
	DFCN_Status.scrollBar:SetMinMaxValues(0, maxScroll)	
	if maxScroll > 0 then
		DFCN_Status.scrollBar:Show()
		DFCN_Status.scrollBar:SetValue(0)		
		DFCN_Status.scrollBar.ScrollUpButton:Enable()
		DFCN_Status.scrollBar.ScrollDownButton:Enable()
	else
		DFCN_Status.scrollBar:Hide()
	end	
	if DFCN_Status.uiFrame and DFCN_Status.uiFrame.tipText then
		local charKey = self:GetCharacterKey()
		local charData = DFCN_Status.db.characters[charKey]
		local lastUpdate = charData and charData.lastUpdated or 0
		local region = GetCurrentRegion()
		local regionText = ""
		if region == 1 then
			regionText = DFCNSL["REGION_US"]
		elseif region == 2 then
			regionText = DFCNSL["REGION_KR"]
		elseif region == 3 then
			regionText = DFCNSL["REGION_EU"]
		elseif region == 4 then
			regionText = DFCNSL["REGION_TW"]
		elseif region == 5 then
			regionText = DFCNSL["REGION_CN"]
		else
			regionText = DFCNSL["REGION_PTR"]
		end		
		local resetWeekday = GetResetWeekday()
		local resetDayText = ""
		if resetWeekday == 1 then
			resetDayText = DFCNSL["SUNDAY"]
		elseif resetWeekday == 2 then
			resetDayText = DFCNSL["MONDAY"]
		elseif resetWeekday == 3 then
			resetDayText = DFCNSL["TUESDAY"]
		elseif resetWeekday == 4 then
			resetDayText = DFCNSL["WEDNESDAY"]
		elseif resetWeekday == 5 then
			resetDayText = DFCNSL["THURSDAY"]
		elseif resetWeekday == 6 then
			resetDayText = DFCNSL["FRIDAY"]
		elseif resetWeekday == 7 then
			resetDayText = DFCNSL["SATURDAY"]
		else
			resetDayText = DFCNSL["UNKNOWN"]
		end		
		local playerLevel = UnitLevel("player")
		if playerLevel >= 78 then
			if time() - lastUpdate < 60 then
				DFCN_Status.uiFrame.tipText:SetText(string.format(DFCNSL["CHARACTER_LEVEL_68_PLUS"] .. " |cFFFFFF00%s|r |cFF808080" .. DFCNSL["CURRENT_REGION_FORMAT"] .. "_" .. DFCN_Status.VERSION .. "|r", DFCNSL["DATA_AUTO_UPDATED"], regionText, resetDayText))
				DFCN_Status.uiFrame.tipText:SetTextColor(0, 1, 0)
			else
				DFCN_Status.uiFrame.tipText:SetText(string.format(DFCNSL["CHARACTER_LEVEL_68_PLUS"] .. " |cFFFF9900%s|r |cFF808080" .. DFCNSL["CURRENT_REGION_FORMAT"] .. "_" .. DFCN_Status.VERSION .. "|r", DFCNSL["DATA_MAY_BE_OUTDATED"], regionText, resetDayText))
				DFCN_Status.uiFrame.tipText:SetTextColor(1, 0.65, 0)
			end
		else
			DFCN_Status.uiFrame.tipText:SetText(string.format(DFCNSL["CHARACTER_LEVEL_BELOW_68"] .. " |cFFFF0000%s|r |cFF808080" .. DFCNSL["CURRENT_REGION_FORMAT"] .. "_" .. DFCN_Status.VERSION .. "|r", DFCNSL["VIEW_ONLY_SUPPORT"], regionText, resetDayText))
			DFCN_Status.uiFrame.tipText:SetTextColor(1, 0.5, 0)
		end
	end	
	for i = rowIndex, #DFCN_Status.scrollChild.rows do
		if DFCN_Status.scrollChild.rows[i] then
			DFCN_Status.scrollChild.rows[i]:Hide()
			DFCN_Status.scrollChild.rows[i].deleteBtn:SetScript("OnClick", nil)
		end
	end
	self:ApplyFontSettings()
end

function DFCN_Status:CreateDockTrigger()
	if self.dockTrigger then
		if self.dockTrigger.tooltipHideTimer then
			self.dockTrigger.tooltipHideTimer:Cancel()
			self.dockTrigger.tooltipHideTimer = nil
		end
		self.dockTrigger:Hide()
		self.dockTrigger = nil
	end
	if not self.db.enableDockMode then return end
	local trigger = CreateFrame("Button", nil, UIParent)
	trigger:SetFrameStrata("TOOLTIP")
	trigger:EnableMouse(true)
	trigger:SetMovable(true)
	trigger:RegisterForDrag("LeftButton")
	local bg = trigger:CreateTexture(nil, "BACKGROUND")
	bg:SetAllPoints()
	bg:SetColorTexture(0.08, 0.08, 0.12, 0.95)
	local borderTop = trigger:CreateTexture(nil, "OVERLAY")
	borderTop:SetColorTexture(1, 0.8, 0.2, 0.8)
	borderTop:SetHeight(1)
	local borderBottom = trigger:CreateTexture(nil, "OVERLAY")
	borderBottom:SetColorTexture(1, 0.8, 0.2, 0.8)
	borderBottom:SetHeight(1)
	local borderLeft = trigger:CreateTexture(nil, "OVERLAY")
	borderLeft:SetColorTexture(1, 0.8, 0.2, 0.8)
	borderLeft:SetWidth(1)
	local borderRight = trigger:CreateTexture(nil, "OVERLAY")
	borderRight:SetColorTexture(1, 0.8, 0.2, 0.8)
	borderRight:SetWidth(1)
	trigger.borderTop = borderTop
	trigger.borderBottom = borderBottom
	trigger.borderLeft = borderLeft
	trigger.borderRight = borderRight
	local glow = trigger:CreateTexture(nil, "OVERLAY")
	glow:SetAllPoints()
	glow:SetColorTexture(1, 0.9, 0.3, 0)
	trigger.glow = glow
	local arrowSize = 14
	trigger.arrowUp = trigger:CreateTexture(nil, "OVERLAY")
	trigger.arrowUp:SetAtlas("NPE_ArrowUP")
	trigger.arrowUp:SetSize(arrowSize, arrowSize)
	trigger.arrowUp:Hide()
	trigger.arrowDown = trigger:CreateTexture(nil, "OVERLAY")
	trigger.arrowDown:SetAtlas("NPE_ArrowDown")
	trigger.arrowDown:SetSize(arrowSize, arrowSize)
	trigger.arrowDown:Hide()
	trigger.arrowLeft = trigger:CreateTexture(nil, "OVERLAY")
	trigger.arrowLeft:SetAtlas("NPE_ArrowLeft")
	trigger.arrowLeft:SetSize(arrowSize, arrowSize)
	trigger.arrowLeft:Hide()
	trigger.arrowRight = trigger:CreateTexture(nil, "OVERLAY")
	trigger.arrowRight:SetAtlas("NPE_ArrowRight")
	trigger.arrowRight:SetSize(arrowSize, arrowSize)
	trigger.arrowRight:Hide()
	local function updateBorderAnchors()
		local w, h = trigger:GetWidth(), trigger:GetHeight()
		borderTop:ClearAllPoints(); borderTop:SetPoint("TOPLEFT", trigger, "TOPLEFT", 0, 0); borderTop:SetPoint("TOPRIGHT", trigger, "TOPRIGHT", 0, 0)
		borderBottom:ClearAllPoints(); borderBottom:SetPoint("BOTTOMLEFT", trigger, "BOTTOMLEFT", 0, 0); borderBottom:SetPoint("BOTTOMRIGHT", trigger, "BOTTOMRIGHT", 0, 0)
		borderLeft:ClearAllPoints(); borderLeft:SetPoint("TOPLEFT", trigger, "TOPLEFT", 0, 0); borderLeft:SetPoint("BOTTOMLEFT", trigger, "BOTTOMLEFT", 0, 0)
		borderRight:ClearAllPoints(); borderRight:SetPoint("TOPRIGHT", trigger, "TOPRIGHT", 0, 0); borderRight:SetPoint("BOTTOMRIGHT", trigger, "BOTTOMRIGHT", 0, 0)
	end
	trigger.updateBorderAnchors = updateBorderAnchors
	trigger.tooltipHideTimer = nil
	trigger:SetScript("OnEnter", function()
		if DFCN_Status.manualOpen then return end
		if trigger.glow then trigger.glow:SetColorTexture(1, 0.9, 0.3, 0.35) end
		DFCN_Status:CancelDockHide()
		DFCN_Status:ShowWindowFromDock()
		GameTooltip:SetOwner(trigger, "ANCHOR_TOP")
		GameTooltip:SetText(DFCNSL["MULTI_CHAR_MANAGER"] .. "\n" .. DFCNSL["LEFT_CLICK_DRAG"], 1, 1, 1)
		GameTooltip:Show()
		if trigger.tooltipHideTimer then
			trigger.tooltipHideTimer:Cancel()
			trigger.tooltipHideTimer = nil
		end
		trigger.tooltipHideTimer = C_Timer.NewTimer(1.5, function()
			if GameTooltip:GetOwner() == trigger then
				GameTooltip_Hide()
			end
			trigger.tooltipHideTimer = nil
		end)
	end)
	trigger:SetScript("OnLeave", function()
		if trigger.glow then trigger.glow:SetColorTexture(1, 0.9, 0.3, 0) end
		if DFCN_Status.manualOpen then return end
		DFCN_Status:StartDockHideTimer()
		if trigger.tooltipHideTimer then
			trigger.tooltipHideTimer:Cancel()
			trigger.tooltipHideTimer = nil
		end
		GameTooltip_Hide()
	end)
	trigger:SetScript("OnDragStart", function(self)
		DFCN_Status:HideWindowToDock()
		self.isDragging = true
		if self:IsMovable() then 
			self:StartMoving() 
		end 
	end)
	trigger:SetScript("OnDragStop", function(self)
		self:StopMovingOrSizing()
		self.isDragging = false
		local mouseX, mouseY = GetCursorPosition()
		local scale = UIParent:GetEffectiveScale()
		mouseX = mouseX / scale
		mouseY = mouseY / scale
		local screenW, screenH = GetScreenWidth(), GetScreenHeight()
		local centerX, centerY = self:GetCenter()
		if not centerX or not centerY then
			centerX, centerY = mouseX, mouseY
		end
		local distLeft = centerX
		local distRight = screenW - centerX
		local distTop = screenH - centerY
		local distBottom = centerY
		local minDist = math.min(distLeft, distRight, distTop, distBottom)
		local newEdge
		if minDist == distLeft then
			newEdge = "LEFT"
		elseif minDist == distRight then
			newEdge = "RIGHT"
		elseif minDist == distTop then
			newEdge = "TOP"
		else
			newEdge = "BOTTOM"
		end
		DFCN_Status.db.lastEdge = newEdge
		local width, height
		if newEdge == "LEFT" or newEdge == "RIGHT" then
			width = 10; height = 100
		else
			width = 100; height = 10
		end
		self:SetSize(width, height)
		local newCenterX, newCenterY
		if newEdge == "LEFT" or newEdge == "RIGHT" then
			newCenterY = math.max(height/2, math.min(centerY, screenH - height/2))
			newCenterX = (newEdge == "LEFT") and (width/2) or (screenW - width/2)
		else
			newCenterX = math.max(width/2, math.min(centerX, screenW - width/2))
			newCenterY = (newEdge == "TOP") and (screenH - height/2) or (height/2)
		end
		self:ClearAllPoints()
		if newEdge == "LEFT" then
			self:SetPoint("LEFT", UIParent, "LEFT", 0, newCenterY - screenH/2)
		elseif newEdge == "RIGHT" then
			self:SetPoint("RIGHT", UIParent, "RIGHT", 0, newCenterY - screenH/2)
		elseif newEdge == "TOP" then
			self:SetPoint("TOP", UIParent, "TOP", newCenterX - screenW/2, 0)
		elseif newEdge == "BOTTOM" then
			self:SetPoint("BOTTOM", UIParent, "BOTTOM", newCenterX - screenW/2, 0)
		end
		local finalCenterX, finalCenterY = self:GetCenter()
		if finalCenterX and finalCenterY then
			if newEdge == "LEFT" or newEdge == "RIGHT" then
				DFCN_Status.db.dockTriggerOffset = finalCenterY / screenH
			else
				DFCN_Status.db.dockTriggerOffset = finalCenterX / screenW
			end
		end
		self.arrowUp:Hide()
		self.arrowDown:Hide()
		self.arrowLeft:Hide()
		self.arrowRight:Hide()
		if newEdge == "LEFT" then
			self.arrowRight:Show()
			self.arrowRight:SetPoint("CENTER", self, "CENTER", 1, 0)
		elseif newEdge == "RIGHT" then
			self.arrowLeft:Show()
			self.arrowLeft:SetPoint("CENTER", self, "CENTER", -1, 0)
		elseif newEdge == "TOP" then
			self.arrowDown:Show()
			self.arrowDown:SetPoint("CENTER", self, "CENTER", 0, 0)
		elseif newEdge == "BOTTOM" then
			self.arrowUp:Show()
			self.arrowUp:SetPoint("CENTER", self, "CENTER", 0, 0)
		end
		if DFCN_Status.isDockActive and not DFCN_Status.manualOpen then
			DFCN_Status:CancelDockHide()
			DFCN_Status:ShowWindowFromDock()
		end
	end)
	self.dockTrigger = trigger
	self:ApplyDockTriggerPosition(trigger)
	self:HideWindowToDock()
end

function DFCN_Status:ApplyDockTriggerPosition(trigger)
	local edge = self.db.lastEdge or "RIGHT"
	local offset = self.db.dockTriggerOffset or 0.5
	local screenW, screenH = GetScreenWidth(), GetScreenHeight()
	local width, height
	if edge == "LEFT" or edge == "RIGHT" then
		width = 10
		height = 100
	else
		width = 100
		height = 10
	end
	trigger:SetSize(width, height)
	trigger:ClearAllPoints()
	if edge == "RIGHT" then
		local centerY = math.max(height/2, math.min(offset * screenH, screenH - height/2))
		trigger:SetPoint("RIGHT", UIParent, "RIGHT", 0, centerY - screenH/2)
	elseif edge == "LEFT" then
		local centerY = math.max(height/2, math.min(offset * screenH, screenH - height/2))
		trigger:SetPoint("LEFT", UIParent, "LEFT", 0, centerY - screenH/2)
	elseif edge == "TOP" then
		local centerX = math.max(width/2, math.min(offset * screenW, screenW - width/2))
		trigger:SetPoint("TOP", UIParent, "TOP", centerX - screenW/2, 0)
	elseif edge == "BOTTOM" then
		local centerX = math.max(width/2, math.min(offset * screenW, screenW - width/2))
		trigger:SetPoint("BOTTOM", UIParent, "BOTTOM", centerX - screenW/2, 0)
	end
	trigger.arrowUp:Hide()
	trigger.arrowDown:Hide()
	trigger.arrowLeft:Hide()
	trigger.arrowRight:Hide()
	if edge == "LEFT" then
		trigger.arrowRight:Show()
		trigger.arrowRight:SetPoint("CENTER", trigger, "CENTER", 1, 0)
	elseif edge == "RIGHT" then
		trigger.arrowLeft:Show()
		trigger.arrowLeft:SetPoint("CENTER", trigger, "CENTER", -1, 0)
	elseif edge == "TOP" then
		trigger.arrowDown:Show()
		trigger.arrowDown:SetPoint("CENTER", trigger, "CENTER", 0, 0)
	elseif edge == "BOTTOM" then
		trigger.arrowUp:Show()
		trigger.arrowUp:SetPoint("CENTER", trigger, "CENTER", 0, 0)
	end
	if trigger.updateBorderAnchors then
		trigger.updateBorderAnchors()
	end
end

function DFCN_Status:ShowWindowFromDock()
	if self.dockTrigger and self.dockTrigger.isDragging then
		return
	end
	if not self.uiFrame then
		self:CreateUI()
	end
	self:UpdateCharacterData()
	self:UpdateUI()
	self.isDockActive = true
	local edge = self.db.lastEdge or "RIGHT"
	local gap = 5
	local trigger = self.dockTrigger
	if not trigger then return end
	self.uiFrame:ClearAllPoints()
	if edge == "RIGHT" then
		self.uiFrame:SetPoint("RIGHT", trigger, "LEFT", -gap, 0)
	elseif edge == "LEFT" then
		self.uiFrame:SetPoint("LEFT", trigger, "RIGHT", gap, 0)
	elseif edge == "TOP" then
		self.uiFrame:SetPoint("TOP", trigger, "BOTTOM", 0, -gap)
	elseif edge == "BOTTOM" then
		self.uiFrame:SetPoint("BOTTOM", trigger, "TOP", 0, gap)
	end
	local screenW, screenH = GetScreenWidth(), GetScreenHeight()
	local left, bottom = self.uiFrame:GetLeft(), self.uiFrame:GetBottom()
	if not left then
		self.uiFrame:SetPoint("CENTER", trigger, "CENTER", 0, 0)
		left, bottom = self.uiFrame:GetLeft(), self.uiFrame:GetBottom()
	end
	local right = left + self.uiFrame:GetWidth()
	local top = bottom + self.uiFrame:GetHeight()
	local dx, dy = 0, 0
	if left < 0 then dx = -left end
	if right > screenW then dx = screenW - right end
	if bottom < 0 then dy = -bottom end
	if top > screenH then dy = screenH - top end
	if dx ~= 0 or dy ~= 0 then
		self.uiFrame:ClearAllPoints()
		if edge == "RIGHT" then
			self.uiFrame:SetPoint("RIGHT", trigger, "LEFT", -gap + dx, dy)
		elseif edge == "LEFT" then
			self.uiFrame:SetPoint("LEFT", trigger, "RIGHT", gap + dx, dy)
		elseif edge == "TOP" then
			self.uiFrame:SetPoint("TOP", trigger, "BOTTOM", dx, -gap + dy)
		elseif edge == "BOTTOM" then
			self.uiFrame:SetPoint("BOTTOM", trigger, "TOP", dx, gap + dy)
		end
	end
	self.uiFrame:Show()
	self.uiFrame:SetMovable(false)
	self.uiFrame:RegisterForDrag()
	self.uiFrame:SetScript("OnLeave", function()
		if DFCN_Status.manualOpen then return end
		DFCN_Status:StartDockHideTimer()
	end)
	self.uiFrame:SetScript("OnEnter", function()
		if DFCN_Status.manualOpen then return end
		DFCN_Status:CancelDockHide()
	end)
end

function DFCN_Status:HideWindowToDock()
	if not self.uiFrame then return end
	self.isDockActive = false
	self.uiFrame:Hide()
	self.uiFrame:SetScript("OnLeave", nil)
	self.uiFrame:SetScript("OnEnter", nil)
	if self.dockTrigger and self.dockTrigger.tooltipHideTimer then
		self.dockTrigger.tooltipHideTimer:Cancel()
		self.dockTrigger.tooltipHideTimer = nil
	end
end

function DFCN_Status:StartDockHideTimer()
	if self.dockHideTimer then
		self.dockHideTimer:Cancel()
	end
	self.dockHideTimer = C_Timer.NewTimer(1, function()
		if not DFCN_Status.isDockActive then return end
		local mx, my = GetCursorPosition()
		local scale = UIParent:GetEffectiveScale()
		mx = mx / scale
		my = my / scale
		local frame = DFCN_Status.uiFrame
		local trigger = DFCN_Status.dockTrigger
		local shouldHide = true
		if frame and frame:IsShown() then
			local l = frame:GetLeft()
			local b = frame:GetBottom()
			local r = l + frame:GetWidth()
			local t = b + frame:GetHeight()
			if mx >= l and mx <= r and my >= b and my <= t then
				shouldHide = false
			end
		end
		if shouldHide and trigger and trigger:IsShown() then
			local l = trigger:GetLeft()
			local b = trigger:GetBottom()
			local r = l + trigger:GetWidth()
			local t = b + trigger:GetHeight()
			if mx >= l and mx <= r and my >= b and my <= t then
				shouldHide = false
			end
		end
		if shouldHide then
			DFCN_Status:HideWindowToDock()
		end
		DFCN_Status.dockHideTimer = nil
	end)
end

function DFCN_Status:CancelDockHide()
	if self.dockHideTimer then
		self.dockHideTimer:Cancel()
		self.dockHideTimer = nil
	end
end

function DFCN_Status:ShowUI()
	local playerLevel = UnitLevel("player")
	isUpdateLevel = (playerLevel >= 78)	
	if isUpdateLevel then
		self:UpdateCharacterData()
	end
	if self.db.enableDockMode then
		self.manualOpen = true
		if self.dockTrigger then self.dockTrigger:Hide() end
	end	
	self:CreateUI()
	self:UpdateUI()
	if self.db.enableDockMode and self.manualOpen then
		self.uiFrame:SetMovable(true)
		self.uiFrame:RegisterForDrag("LeftButton")
		self.uiFrame:SetScript("OnLeave", nil)
		self.uiFrame:SetScript("OnEnter", nil)
	else
		self.uiFrame:SetMovable(true)
		self.uiFrame:RegisterForDrag("LeftButton")
	end	
	DFCN_Status.uiFrame:Show()
end

local frame = CreateFrame("Frame")
frame:RegisterEvent("ADDON_LOADED")
frame:RegisterEvent("PLAYER_LOGIN")
frame:RegisterEvent("PLAYER_LOGOUT")
frame:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
frame:RegisterEvent("CURRENCY_DISPLAY_UPDATE")
frame:RegisterEvent("ZONE_CHANGED_NEW_AREA")
frame:RegisterEvent("WEEKLY_REWARDS_UPDATE")
frame:RegisterEvent("PLAYER_UPDATE_RESTING")
frame:SetScript("OnEvent", function(self, event, ...)
	if event == "ADDON_LOADED" and ... == "OrzUI" then
		--print("|T135994:14:14|t |cFF00BFFF[" .. DFCNSL["INFO"] .. "]|r|cFF00FF00 " .. DFCNSL["MULTI_CHAR_MANAGER"] .. " |cFFFFA500By|r|c000EEEEE DFCN|r |c000EEE00" .. DFCNSL["ADDON_LOADED"] .. "|r")
		if not DFCN_StatusDB then
			DFCN_StatusDB = {
				characters = {},
				version = dbVersion,
				sortBy = "overallItemLevel",
				ascending = false,
				viewMode = "delves",
				fontOffset = 0,
				hotkey = "SPACE",
				enableDockMode = true,
				lastEdge = "RIGHT",
				dockTriggerOffset = 0.5,
			}
			print("|T135994:14:14|t |cFF00BFFF[" .. DFCNSL["INFO"] .. "] " .. string.format(DFCNSL["DATABASE_CREATED"], dbVersion) .. "|r")
		else
			DFCN_StatusDB.fontOffset = DFCN_StatusDB.fontOffset or 0
			DFCN_StatusDB.hotkey = DFCN_StatusDB.hotkey or "SPACE"
			local oldVersion = DFCN_StatusDB.version or 1
			if oldVersion < dbVersion then
				DFCN_StatusDB.characters = {}				
				DFCN_StatusDB.sortBy = DFCN_StatusDB.sortBy or "overallItemLevel"
				DFCN_StatusDB.ascending = (DFCN_StatusDB.ascending == nil) and false or DFCN_StatusDB.ascending
				DFCN_StatusDB.viewMode = DFCN_StatusDB.viewMode or "delves"
				DFCN_StatusDB.fontOffset = DFCN_StatusDB.fontOffset or 0				
				if not DFCN_StatusDB.minimap then
					DFCN_StatusDB.minimap = { hide = false, position = nil }
				end				
				DFCN_StatusDB.version = dbVersion				
				print("|T135994:14:14|t |cFF00BFFF[" .. DFCNSL["UPGRADE"] .. "]|r |cFFFF0000" .. DFCNSL["DATABASE_CLEARED"] .. "|r")
			end
			DFCN_StatusDB.sortBy = DFCN_StatusDB.sortBy or "overallItemLevel"
			DFCN_StatusDB.ascending = (DFCN_StatusDB.ascending == nil) and false or DFCN_StatusDB.ascending
			DFCN_StatusDB.viewMode = DFCN_StatusDB.viewMode or "delves"			
			for _, charData in pairs(DFCN_StatusDB.characters or {}) do
				if not charData.keyStatus then
					charData.keyStatus = {
						totalKeys = 0,
						completedQuests = 0
					}
				end
				if not charData.shardStatus then
					charData.shardStatus = {
						totalShards = 0,
						seasonEarned = 0
					}
				end
				if not charData.concentration then
					charData.concentration = {
						lastUpdated = GetPreciseTime(),
						prof1 = {value = 0.0, name = DFCNSL["NO_PROFESSION"], hasConcentration = false},
						prof2 = {value = 0.0, name = DFCNSL["NO_PROFESSION"], hasConcentration = false}
					}
				end
				if not charData.currencies then
					charData.currencies = {}
				end
				if charData.worldSlotsUnlocked == nil then
					charData.worldSlotsUnlocked = 0
				end
			end
		end		
		DFCN_Status.db = DFCN_StatusDB
		DFCN_Status:CheckAllCharactersReset()		
		DFCN_Status:CreateMiniMapButton()
		DFCN_Status:CreateOptionsPanel()
	elseif event == "PLAYER_LOGIN" then
		local serverTime = GetServerTime()
		local localTime = time()
		DFCN_Status.serverTimeOffset = serverTime - localTime		
		local playerLevel = UnitLevel("player")
		isUpdateLevel = (playerLevel >= 78)		
		DFCN_Status.concentrationTracker = {
			values = {},
			lastUpdated = GetPreciseTime()
		}		
		if isUpdateLevel then
			C_Timer.After(1, function()
				DFCN_Status:UpdateCharacterData()
			end)
			
			DFCN_Status.updateTicker = C_Timer.NewTicker(60, function()
				DFCN_Status:UpdateCharacterData()
			end)
		end
		DFCN_Status:CreateDockTrigger()
	elseif event == "PLAYER_EQUIPMENT_CHANGED" and isUpdateLevel then
		C_Timer.After(2, function()
			DFCN_Status:UpdateCharacterData()
			if DFCN_Status.uiFrame and DFCN_Status.uiFrame:IsVisible() then
				DFCN_Status:UpdateUI()
			end
		end)   
	elseif event == "PLAYER_LOGOUT" then
		DFCN_Status:SaveOnLogout()
	elseif event == "WEEKLY_REWARDS_UPDATE" then
		if isUpdateLevel then
			C_Timer.After(1, function()
				DFCN_Status:UpdateCharacterData()
			end)
		end
	elseif event == "ZONE_CHANGED_NEW_AREA" then
		if isUpdateLevel then
			C_Timer.After(1, function()
				DFCN_Status:UpdateCharacterData()
			end)
		end
	elseif event == "PLAYER_UPDATE_RESTING" then
		local playerLevel = UnitLevel("player")
		local maxLevel = GetMaxPlayerLevel()
		if playerLevel >= maxLevel then
			return
		end
		if playerLevel < 78 then
			return
		end
		C_Timer.After(1, function()
			if isUpdateLevel then
				DFCN_Status:UpdateCharacterData()
			end
		end)
	elseif event == "CURRENCY_DISPLAY_UPDATE" then
		local now = GetTime()
		if now - lastCurrencyUpdate > CURRENCY_UPDATE_DELAY then
			lastCurrencyUpdate = now
			if isUpdateLevel then
				C_Timer.After(1, function()
					DFCN_Status:UpdateCharacterData()
					if DFCN_Status.uiFrame and DFCN_Status.uiFrame:IsVisible() then
						DFCN_Status:UpdateUI()
					end
				end)
			end
		end
	end
end)

SLASH_CHARACTERSTATUS1 = "/dfcnchar"
SlashCmdList["CHARACTERSTATUS"] = function(msg)
	local command = strlower(msg or "")	
	if command == "" then
		if DFCN_Status.uiFrame and DFCN_Status.uiFrame:IsVisible() then
			DFCN_Status.uiFrame:Hide()
		else
			DFCN_Status:ShowUI()
		end
	elseif command == "minimap on" then
		DFCN_Status.db.minimap.hide = false
		local LibDBIcon = LibStub and LibStub:GetLibrary("LibDBIcon-1.0", true)
		if LibDBIcon then
			LibDBIcon:Show("DFCN_CharacterStatus")
		end
		print("|T135994:14:14|t |cFF00BFFF[" .. DFCNSL["INFO"] .. "] " .. DFCNSL["MINIMAP_SHOWN"] .. "|r")
	elseif command == "minimap off" then
		DFCN_Status.db.minimap.hide = true
		local LibDBIcon = LibStub and LibStub:GetLibrary("LibDBIcon-1.0", true)
		if LibDBIcon then
			LibDBIcon:Hide("DFCN_CharacterStatus")
		end
		print("|T135994:14:14|t |cFF00BFFF[" .. DFCNSL["INFO"] .. "] " .. DFCNSL["MINIMAP_HIDDEN"] .. "|r")
	else
		print(DFCNSL["COMMAND_OPEN_CLOSE"])
		print(DFCNSL["COMMAND_MINIMAP_ON"])
		print(DFCNSL["COMMAND_MINIMAP_OFF"])
	end
end

function DFCN_Status:CreateOptionsPanel()
	if not DFCN_Status.db.minimap then
		DFCN_Status.db.minimap = {
			hide = false,
			position = nil
		}
	end	
	local panel = CreateFrame("Frame")
	panel.name = DFCNSL["MULTI_CHAR_MANAGER"].."|T135994:14:14|t"   --"|T135994:14:14|t |cFF00FF00" .. DFCNSL["MULTI_CHAR_MANAGER_SHORT"] .. " |cFFFFA500DFCN|r"
	local title = panel:CreateFontString("ARTWORK", nil, "GameFontNormalLarge")
	title:SetFont(GameFontNormal:GetFont(), 16)
	title:SetPoint("TOPLEFT", 16, -16)
	title:SetText("|T135994:18:18|t " .. DFCNSL["MULTI_CHAR_MANAGER"])
	local minimapCheckbox = CreateFrame("CheckButton", nil, panel, "UICheckButtonTemplate")
	minimapCheckbox:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -30)   
	minimapCheckbox:SetScript("OnClick", function(self)
		DFCN_Status.db.minimap.hide = not self:GetChecked()
		local LibDBIcon = LibStub and LibStub:GetLibrary("LibDBIcon-1.0", true)
		if LibDBIcon then
			if self:GetChecked() then
				LibDBIcon:Show("DFCN_CharacterStatus")
				print("|T135994:14:14|t |cFF00BFFF[" .. DFCNSL["INFO"] .. "] " .. DFCNSL["MINIMAP_SHOWN"] .. "|r")
			else
				LibDBIcon:Hide("DFCN_CharacterStatus")
				print("|T135994:14:14|t |cFF00BFFF[" .. DFCNSL["INFO"] .. "] " .. DFCNSL["MINIMAP_HIDDEN"] .. "|r")
			end
		end
	end)	
	minimapCheckbox.text = minimapCheckbox:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	minimapCheckbox.text:SetPoint("LEFT", minimapCheckbox, "RIGHT", 5, 0)
	minimapCheckbox.text:SetFont(GameFontNormal:GetFont(), 16)
	minimapCheckbox.text:SetText(DFCNSL["SHOW_MINIMAP_ICON"])	
	local togglePanelBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
	togglePanelBtn:SetPoint("TOPLEFT", minimapCheckbox, "BOTTOMLEFT", 0, -20)
	togglePanelBtn:SetSize(200, 40)
	local function UpdateButtonText()
		if DFCN_Status.uiFrame and DFCN_Status.uiFrame:IsShown() then
			togglePanelBtn:SetText(DFCNSL["CLOSE_PANEL"])
		else
			togglePanelBtn:SetText(DFCNSL["OPEN_PANEL"])
		end
	end
	UpdateButtonText()	
	togglePanelBtn:SetScript("OnClick", function()
		if DFCN_Status.uiFrame and DFCN_Status.uiFrame:IsShown() then
			DFCN_Status.uiFrame:Hide()
		else
			DFCN_Status:ShowUI()
		end
		UpdateButtonText()
	end)	
	local function RefreshPanel()
		UpdateButtonText()
		minimapCheckbox:SetChecked(not DFCN_Status.db.minimap.hide)
	end	
	panel:SetScript("OnShow", RefreshPanel)	
	C_Timer.After(0, function()
		if panel:IsVisible() then
			RefreshPanel()
		end
	end)	
	local commandTip = panel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	commandTip:SetPoint("TOPLEFT", togglePanelBtn, "BOTTOMLEFT", 0, -10)
	commandTip:SetText(DFCNSL["MACRO_COMMAND"])
	commandTip:SetTextColor(0.8, 0.8, 0.8)	
	local downloadLabel = panel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	downloadLabel:SetPoint("TOPLEFT", commandTip, "BOTTOMLEFT", 0, -20)
	downloadLabel:SetText(DFCNSL["ORIGINAL_DOWNLOAD"])
	downloadLabel:SetTextColor(1, 1, 1)	
	local downloadEditBox = CreateFrame("EditBox", nil, panel, "InputBoxTemplate")
	downloadEditBox:SetPoint("TOPLEFT", downloadLabel, "BOTTOMLEFT", 0, -5)
	downloadEditBox:SetSize(300, 20)
	downloadEditBox:SetAutoFocus(false)
	downloadEditBox:SetText("https://bbs.nga.cn/read.php?tid=44921579")
	downloadEditBox:SetCursorPosition(0)	
	local copyTip = panel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
	copyTip:SetPoint("TOPLEFT", downloadEditBox, "BOTTOMLEFT", 0, -5)
	copyTip:SetText(DFCNSL["CLICK_TO_COPY"])
	copyTip:SetTextColor(0.7, 0.7, 0.7)	
	downloadEditBox:SetScript("OnEscapePressed", function(self)
		self:ClearFocus()
	end)	
	downloadEditBox:SetScript("OnEditFocusGained", function(self)
		self:HighlightText()
	end)	
	downloadEditBox:SetScript("OnMouseUp", function(self)
		if not self:HasFocus() then
			self:SetFocus()
			self:HighlightText()
		end
	end)
	local hotkeyFrame = CreateFrame("Frame", nil, panel)
	hotkeyFrame:SetPoint("TOPLEFT", copyTip, "BOTTOMLEFT", 0, -15)
	hotkeyFrame:SetSize(400, 50)
	local hotkeyLabel = hotkeyFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	hotkeyLabel:SetPoint("TOPLEFT", 0, 0)
	hotkeyLabel:SetText(DFCNSL["HOTKEY_SETTING"])
	hotkeyLabel:SetTextColor(1, 1, 1)
	local hotkeyButton = CreateFrame("Button", nil, hotkeyFrame, "UIPanelButtonTemplate")
	hotkeyButton:SetPoint("TOPLEFT", hotkeyLabel, "BOTTOMLEFT", 0, -5)
	hotkeyButton:SetSize(180, 35)
	hotkeyButton:SetText(DFCN_Status.db.hotkey or "SPACE")
	local isCapturing = false
	local captureText = hotkeyButton:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	captureText:SetPoint("LEFT", hotkeyButton, "RIGHT", 10, 0)
	captureText:SetText("")
	captureText:SetTextColor(0.8, 0.8, 0)
	hotkeyButton:SetScript("OnClick", function()
		if isCapturing then
			isCapturing = false
			hotkeyButton:SetText(DFCN_Status.db.hotkey or "SPACE")
			captureText:SetText("")
			return
		end
		isCapturing = true
		hotkeyButton:SetText(DFCNSL["PRESS_NEW_HOTKEY"])
		captureText:SetText(DFCNSL["CLICK_BUTTON_AGAIN_TO_CANCEL"])
	end)
	local captureFrame = CreateFrame("Frame")
	captureFrame:SetScript("OnKeyDown", function(self, key)
		if not isCapturing then return end
		if key == "ESCAPE" then
			isCapturing = false
			hotkeyButton:SetText(DFCN_Status.db.hotkey or "SPACE")
			captureText:SetText("")
			return
		end
		if key == "LSHIFT" or key == "RSHIFT" or key == "LCTRL" or key == "RCTRL" or key == "LALT" or key == "RALT" then
			captureText:SetText(DFCNSL["MODIFIER_NOT_ALLOWED"])
			return
		end
		DFCN_Status.db.hotkey = key
		hotkeyButton:SetText(key)
		captureText:SetText(DFCNSL["HOTKEY_SET_SUCCESS"])
		isCapturing = false
		C_Timer.After(3, function()
			if captureText:GetText() == DFCNSL["HOTKEY_SET_SUCCESS"] then
				captureText:SetText("")
			end
		end)
	end)
	captureFrame:SetPropagateKeyboardInput(true)
	captureFrame:EnableKeyboard(true)
	captureFrame:SetScript("OnHide", function()
		isCapturing = false
	end)
	local dockCheckbox = CreateFrame("CheckButton", nil, panel, "UICheckButtonTemplate")
	dockCheckbox:SetPoint("TOPLEFT", hotkeyButton, "BOTTOMLEFT", 0, -20)
	dockCheckbox.text = dockCheckbox:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	dockCheckbox.text:SetPoint("LEFT", dockCheckbox, "RIGHT", 5, 0)
	dockCheckbox.text:SetText(DFCNSL["DOCK_MODE"])
	dockCheckbox:SetScript("OnClick", function(self)
		DFCN_Status.db.enableDockMode = self:GetChecked()
		if DFCN_Status.db.enableDockMode then
			DFCN_Status:CreateDockTrigger()
		else
			if DFCN_Status.dockTrigger then
				DFCN_Status.dockTrigger:Hide()
				DFCN_Status.dockTrigger = nil
			end
			DFCN_Status:CancelDockHide()
			DFCN_Status:HideWindowToDock()
		end
	end)
	dockCheckbox:SetChecked(DFCN_Status.db.enableDockMode)
	local originalOnShow = panel:GetScript("OnShow")
	panel:SetScript("OnShow", function()
		if originalOnShow then originalOnShow() end
		if not captureFrame:IsShown() then
			captureFrame:Show()
		end
		hotkeyButton:SetText(DFCN_Status.db.hotkey or "SPACE")
		minimapCheckbox:SetChecked(not DFCN_Status.db.minimap.hide)
		dockCheckbox:SetChecked(DFCN_Status.db.enableDockMode)
		if type(UpdateButtonText) == "function" then UpdateButtonText() end
	end)
	local originalOnHide = panel:GetScript("OnHide")
	panel:SetScript("OnHide", function()
		if originalOnHide then originalOnHide() end
		captureFrame:Hide()
		isCapturing = false
	end)	
	if InterfaceOptions_AddCategory then
		InterfaceOptions_AddCategory(panel)
	elseif Settings and Settings.RegisterAddOnCategory and Settings.RegisterCanvasLayoutCategory then
		local category = Settings.RegisterCanvasLayoutCategory(panel, panel.name)
		Settings.RegisterAddOnCategory(category)
	end	
	return panel
end