local statsText = { "힘", "민첩성", "체력", "지능", "정신력" }
local baseStats, baseHealth, baseMana, ratingBase, baseCrit, agiPerCrit, dr_K, dr_Cp, dr_Cd
local baseDodge, agiPerDodge, baseParry, baseBlock, blockValuePerStr, apPerStr, apPerAgi, apPerInt, rapPerAgi
local spellCritPerInt, baseSpellCrit, armorPenetration, healthRegenPerSpi,  baseHealthRegen, addAttackPower, addRangedAttackPower

local _G = getfenv(0)
local min = _G.min
local max = _G.max
local type = _G.type
local ceil = _G.ceil
local floor = _G.floor
local pairs = _G.pairs
local ipairs = _G.ipairs
local tinsert = _G.tinsert
local UnitHealthMax = _G.UnitHealthMax
local UnitPowerMax = _G.UnitPowerMax
local UnitRace = _G.UnitRace
local UnitClass = _G.UnitClass

local playerHealth, playerMana, prefixColor

local backup = {}

local LIS = LibStub("LibItemSpec-2.0")
local LDM = LibStub("LibDropMenu-2.0")

local function round(v)
	if type(v) == "number" then
		return floor(v + 0.5)
	else
		return v
	end
end

local function clearAllSpec()
	for p in pairs(InvenSimulatorUI.specTable) do
		InvenSimulatorUI.specTable[p] = nil
	end
end

local function clearBackup()
	for p in pairs(backup) do
		backup[p] = nil
	end
end

local function backupSpec()
	clearBackup()
	for p, v in pairs(InvenSimulatorUI.specTable) do
		backup[p] = v
	end
end

local function restoreSpec()
	clearAllSpec()
	for p, v in pairs(backup) do
		InvenSimulatorUI.specTable[p] = v
	end
end

local function getValue(spec)
	return InvenSimulatorUI.specTable[spec] or 0
end

local function getHealth()
	return max(0, (getValue("체력") - 18) * HEALTH_PER_STAMINA)
end

local function getMana()
	return max(0, (getValue("지능") - 18) * MANA_PER_INTELLECT - 10)
end

local function getRatingToEffect(value, rating)
	if rating and ratingBase[rating] then
		if type(value) == "string" and InvenSimulatorUI.specTable[value] then
			return InvenSimulatorUI.specTable[value] / ratingBase[rating] / ((82 / 52) * (131 / 63))
		elseif type(value) == "number" then
			return value / ratingBase[rating] / ((82 / 52) * (131 / 63))
		end
	end
	return 0
end

local function getCritFromAgi()
	return getValue("민첩성") / agiPerCrit + baseCrit
end

local function getSpellCritFromInt()
	return getValue("지능") * spellCritPerInt + baseSpellCrit
end

local function getCritFromRating()
	return getRatingToEffect("치명타 및 주문 극대화 적중도", "치명타 적중도")
end

local function getSpellCritFromRating()
	return getRatingToEffect("주문 극대화 적중도", "치명타 적중도")
end

local function getRangedCritFromRating()
	return getRatingToEffect("원거리 치명타 적중도", "치명타 적중도")
end

local function getParryFromRating()
	return getRatingToEffect("무기 막기 숙련도", "무기 막기 숙련도")
end

local function getMeleeHasteFromRating()
	if InvenSimulatorUI.class == "성기사" or InvenSimulatorUI.class == "죽음의 기사" or InvenSimulatorUI.class == "주술사" or InvenSimulatorUI.class == "드루이드" then
		return getRatingToEffect(InvenSimulatorUI.specTable["가속도"] * 1.298, "가속도")
	else
		return getRatingToEffect("가속도", "가속도")
	end
end

local function getRangedHasteFromRating()
	return getRatingToEffect("원거리 가속도", "가속도")
end

local function getHasteFromRating()
	return getRatingToEffect("가속도", "가속도")
end

local function getDodgeFromAgi()
	return (getValue("민첩성") - baseStats[2]) / agiPerDodge
end

local function getEffectFromDefense()
	return floor(getRatingToEffect("방어 숙련도", "방어 숙련도")) * DODGE_PARRY_BLOCK_PERCENT_PER_DEFENSE
end

local function getAddedEffectFromDefense()
	return getValue("추가 방어 숙련") * DODGE_PARRY_BLOCK_PERCENT_PER_DEFENSE
end

local function getDodgeFromDodgeRating()
	return getRatingToEffect("회피 숙련도", "회피 숙련도")
end

local function getBlockFromRating()
	return getRatingToEffect("방패 막기 숙련도", "방패 막기 숙련도")
end

local function getAvoidanceAfterDR(avoidanceBeforeDR, avoidanceType)
	if avoidanceBeforeDR > 0 then
		if avoidanceType == "무기 막기" then
			return 1 / (dr_Cp + dr_K / avoidanceBeforeDR)
		elseif avoidanceType == "피적중" then
			return 1 / (1 / 16 + dr_K / avoidanceBeforeDR)
		else
			return 1 / (dr_Cd + dr_K / avoidanceBeforeDR)
		end
	else
		return 0
	end
end

local function getDodgeAfterDR()
	return getAvoidanceAfterDR(getDodgeFromAgi() + getDodgeFromDodgeRating() + getEffectFromDefense(), "회피") + baseDodge + getAddedEffectFromDefense()
end

local function getReductionFromArmor()
	return min(0.75, max(0, (getValue("방어도") / 15232.5) / (1 + (getValue("방어도") / 15232.5)))) * 100
end

local function getHealthRegen()
	return getValue("정신력") * healthRegenPerSpi + baseHealthRegen
end

local function getManaRegenFromSpi()
	return (0.001 + getValue("정신력") * 0.003345 * (getValue("지능") ^ 0.5)) * 5
end

local function getResistLevel(resist)
	if type(resist) == "number" then
		resist = resist / max(InvenSimulatorUI.level, 20)
		if resist > 5 then
			return RESISTANCE_EXCELLENT
		elseif resist > 3.75 then
			return RESISTANCE_VERYGOOD
		elseif resist > 2.5 then
			return RESISTANCE_GOOD
		elseif resist > 1.25 then
			return RESISTANCE_FAIR
		elseif resist > 0 then
			return RESISTANCE_POOR
		else
			return RESISTANCE_NONE
		end
	else
		return RESISTANCE_NONE
	end
end

function InvenSimulatorUI:InitDataTable()
	if self.initData then return end
	self.initData = true
	self.class = UnitClass("player")
	self.race = UnitRace("player")
	if not self.database.baseStats then
		error("Inven Simulater: Missing DataBase File.")
	elseif not self.race or not self.database.baseStats[self.race] then
		error("Inven Simulater: Wrong Player Race("..(self.race or "nil")..")")
	elseif not self.class or not self.database.baseStats[self.race][self.class] then
		error("Inven Simulater: Wrong Player Class("..(self.class or "nil")..")")
	end
	baseStats = self.database.baseStats[self.race][self.class]
	baseHealth = floor(self.database.baseHealth[self.class] * (self.race == "타우렌" and 1.05 or 1))
	baseMana = self.database.baseMana[self.class]
	ratingBase = self.database.ratingBase
	baseCrit = self.database.baseCrit[self.class]
	agiPerCrit = self.database.agiPerCrit[self.class]
	agiPerDodge = 1 / self.database.dodgePerAgi[self.class]
	baseDodge = self.database.baseDodge[self.class] + (baseStats[2] / agiPerDodge)
	baseParry = self.database.baseParry[self.class]
	baseBlock = self.database.baseBlock[self.class]
	blockValuePerStr = self.database.blockValuePerStr[self.class]
	apPerStr = self.database.apPerStr[self.class]
	apPerAgi = self.database.apPerAgi[self.class]
	apPerInt = self.database.apPerInt[self.class]
	rapPerAgi = self.database.rapPerAgi[self.class]
	spellCritPerInt = self.database.spellCritPerInt[self.class]
	baseSpellCrit = self.database.baseSpellCrit[self.class]
	healthRegenPerSpi = self.database.healthRegenPerSpi[self.class]
	baseHealthRegen = self.database.baseHealthRegen[self.class]
	addAttackPower = (self.database.addAttackPower[self.class] or 0) * self.level
	addRangedAttackPower = (self.database.addRangedAttackPower[self.class] or 0) * self.level
	dr_K = self.database.dr_K[self.class]
	dr_Cp = self.database.dr_Cp[self.class]
	dr_Cd = self.database.dr_Cd[self.class]
	armorPenetration = self.database.armorPenetration
	self:ScanItem()
end

function InvenSimulatorUI:ScanItem()
	if LDM:IsOpen("InvenSimulatorItemMenu") then
		LDM:CloseMenu(1)
	end
	self.gem, self.meta, self.set, self.avgItemLevel = LIS:GetItemData(self.specTable,
		self.db.items[1],
		self.db.items[2],
		self.db.items[3],
		nil,
		self.db.items[5],
		self.db.items[6],
		self.db.items[7],
		self.db.items[8],
		self.db.items[9],
		self.db.items[10],
		self.db.items[11],
		self.db.items[12],
		self.db.items[13],
		self.db.items[14],
		self.db.items[15],
		self.db.items[16],
		self.db.items[17],
		self.db.items[18],
		nil
	)
	self.specTable["평균 아이템 레벨"] = self.avgItemLevel or 0
	self:SaveSetInfo()
	if self.gemFrames[1].hasJewelCrafting ~= self:HasTradeSkill("보석세공") then
		self.gemFrames[1].hasJewelCrafting = self:HasTradeSkill("보석세공")
		if self.gemFrames[1].hasJewelCrafting then
			self.gemFrames[1]:Show()
			self.gemFrames[1]:ClearAllPoints()
			self.gemFrames[1]:SetPoint("BOTTOMRIGHT", self.modelFrame, "BOTTOMRIGHT", -2, 34)
			self.gemFrames[2]:ClearAllPoints()
			self.gemFrames[2]:SetPoint("RIGHT", self.gemFrames[1], "LEFT", 1, 0)
		else
			self.gemFrames[1]:Hide()
			self.gemFrames[2]:ClearAllPoints()
			self.gemFrames[2]:SetPoint("BOTTOMRIGHT", self.modelFrame, "BOTTOMRIGHT", -2, 34)
		end
	end
	if self.gemFrames[1].hasJewelCrafting then
		self.numDragonEyeGem = self:GetNumDragonEyeGem(self.db.items[1])
		for i = 2, 19 do
			self.numDragonEyeGem = self.numDragonEyeGem + self:GetNumDragonEyeGem(self.db.items[i])
		end
	else
		self.numDragonEyeGem = 0
	end
	self.gemFrames[1].count:SetText(self.numDragonEyeGem)
	self.gemFrames[2].count:SetText(self.gem:match("B(%d+)") or "0")
	self.gemFrames[3].count:SetText(self.gem:match("Y(%d+)") or "0")
	self.gemFrames[4].count:SetText(self.gem:match("R(%d+)") or "0")
	self.gemFrames[5].count:SetText(self.gem:match("M(%d+)") or "0")
	backupSpec()
	self.runCalculate = nil
	self:Calculate()
end

local addSpecValue, multiSpecValue = {}, {}
local specMatch = {
	["힘"] = true, ["민첩성"] = true, ["체력"] = true, ["지능"] = true, ["정신력"] = true,
	["원거리 전투력"] = "전투력",
	["주문 공격력"] = "주문력",
	["주문 치유량"] = "주문력",
	["원거리 적중도"] = "적중도",
	["주문 적중도"] = "적중도",
	["원거리 가속도"] = "가속도",
	["주문 가속도"] = "가속도",
	["원거리 치명타 적중도"] = "치명타 및 주문 극대화 적중도",
	["시전 중 마나 회복량"] = "5초당 마나 회복량",
	["보조장비 공격력 배율"] = 0.5,
	["공격 속도 배율"] = 1,
	["원거리 공격 속도 배율"] = 1,
	["물리 공격력 배율"] = 1,
	["주장비 공격력 배율"] = 1,
	["원거리 장비 공격력 배율"] = 1,
	["치명타율"] = 0,
	["원거리 치명타율"] = 0,
	["극대화율"] = 0,
	["회피율"] = 0,
	["무기 막기"] = 0,
}
local isNumberValue = {
	["힘"] = true,
	["민첩성"] = true,
	["체력"] = true,
	["지능"] = true,
	["정신력"] = true,
	["방어도"] = true,
	["적중도"] = true,
	["숙련"] = true,
	["원거리 적중도"] = true,
	["주문 적중도"] = true,
	["가속도"] = true,
	["원거리 가속도"] = true,
	["주문 가속도"] = true,
}

local function clearValueTable()
	for p in pairs(addSpecValue) do
		addSpecValue[p] = nil
	end
	for p in pairs(multiSpecValue) do
		multiSpecValue[p] = nil
	end
end

local tp

function InvenSimulatorUI:AddSpec(s, v1, v2)
	if type(v1) == "string" and type(v2) == "number" then
		tp = self:GetTalentPoint(v1)
		if tp > 0 then
			if self.setLastCalc then
				self.specTable[s] = (self.specTable[s] or 0) + tp * v2
				if isNumberValue[s] then
					self.specTable[s] = floor(self.specTable[s])
				end
			else
				self.specTable[s] = self.specTable[s] or 0
				addSpecValue[s] = (addSpecValue[s] or 0) + tp * v2
			end
		end
	elseif type(v1) == "number" then
		if self.setLastCalc then
			self.specTable[s] = (self.specTable[s] or 0) + v1
			if isNumberValue[s] then
				self.specTable[s] = floor(self.specTable[s])
			end
		else
			self.specTable[s] = self.specTable[s] or 0
			addSpecValue[s] = (addSpecValue[s] or 0) + v1
		end
	end
	if s == "방어 숙련" then
		self.specTable["추가 방어 숙련"] = (self.specTable["추가 방어 숙련"] or 0) + v1
	end
end

function InvenSimulatorUI:MultiSpec(s, v1, v2)
	if type(v1) == "string" and type(v2) == "number" then
		tp = self:GetTalentPoint(v1)
		if tp > 0 then
			if self.setLastCalc then
				self.specTable[s] = (self.specTable[s] or 0) * (1 + tp * v2)
				if isNumberValue[s] then
					self.specTable[s] = floor(self.specTable[s])
				end
			else
				self.specTable[s] = self.specTable[s] or 0
				multiSpecValue[s] = (multiSpecValue[s] or 1) * (1 + tp * v2)
			end
		end
	elseif type(v1) == "number" then
		if self.setLastCalc then
			self.specTable[s] = (self.specTable[s] or 0) * (1 + v1)
			if isNumberValue[s] then
				self.specTable[s] = floor(self.specTable[s])
			end
		else
			self.specTable[s] = self.specTable[s] or 0
			multiSpecValue[s] = (multiSpecValue[s] or 1) * (1 + v1)
		end
	end
end

function InvenSimulatorUI:GetManaRegenFromSpi()
	return getManaRegenFromSpi()
end

function InvenSimulatorUI:SetBaseParry(parry)
	baseParry = parry
end

function InvenSimulatorUI:Calculate()
	-- 아이템 스캔이 없었을 경우 이전 아이템 스팩 테이블 복원
	if self.runCalculate then
		restoreSpec()
	end
	self.runCalculate = true
	-- 변수 초기화
	self.baseStats = self.baseStats or {}
	self.baseStats["힘"], self.baseStats["민첩성"], self.baseStats["체력"], self.baseStats["지능"], self.baseStats["정신력"] = unpack(baseStats)
	for p in pairs(self.tooltipTable) do
		self.tooltipTable[p] = nil
	end
	-- 태세 및 오라, 변신 탭 정의
	if self.InitStance then
		self:InitStance()
	end
	-- 능력치 매칭 및 초기화
	for p, v in pairs(specMatch) do
		if type(v) == "number" then
			self.specTable[p] = v
		elseif type(v) == "string" then
			self.specTable[v] = self.specTable[v] or 0
			self.specTable[p] = (self.specTable[p] or 0) + self.specTable[v]
		else
			self.specTable[p] = self.specTable[p] or 0
		end
	end
	-- 능력치를 올리는 전문기술 적용
	if self:HasTradeSkill("채광") then
		self:AddSpec("체력", 60)
	end
	if self:HasTradeSkill("무두질") then
		self.specTable["치명타 및 주문 극대화 적중도"] = self.specTable["치명타 및 주문 극대화 적중도"] + 40
		self.specTable["원거리 치명타 적중도"] = self.specTable["원거리 치명타 적중도"] + 40
	end
	-- 주문 극대화 적중도 분리
	self.specTable["주문 극대화 적중도"] = self.specTable["치명타 및 주문 극대화 적중도"]
	-- 무기가 없을 경우 맨손 공격력 적용
	if self.specTable["무기1 최소 공격력"] == nil then
		self.specTable["무기1 최소 공격력"] = 1
		self.specTable["무기1 최대 공격력"] = 1.1
		self.specTable["무기1 공격 속도"] = 2
	end
	self.setLastCalc = nil
	-- 종족 특성 적용
	if self.ApplyRaceTalent then
		self:ApplyRaceTalent()
	end
	-- 특성 적용
	if self.ApplyTalent then
		self:ApplyTalent()
	end
	-- 버프 적용
	if self.ApplyBuff then
		self:ApplyBuff()
	end
	-- 지능 증가 얼개 보석 적용
	if self.meta then
		if self.meta:find("지능 (%d+)%%") then
			self:MultiSpec("지능", tonumber(self.meta:match("지능 (%d+)%%")) / 100)
		elseif self.meta:find("지능 ++(%d+)%%") then
			self:MultiSpec("지능", tonumber(self.meta:match("지능 ++(%d+)%%")) / 100)
		end
	end
	-- 캐릭터 기본 능력치와 이이템 및 특성, 버프로 상승된 능력치 합산
	local uppermod
	for p, v in pairs(self.specTable) do
		if self.database.raceMultiSpec[self.race] and self.database.raceMultiSpec[self.race][p] then
			uppermod = (multiSpecValue[p] or 1) * self.database.raceMultiSpec[self.race][p]
			self.specTable[p] = floor(self.baseStats[p] * uppermod) + floor((v + (addSpecValue[p] or 0)) * uppermod)
		elseif self.baseStats[p] then
			uppermod = multiSpecValue[p] or 1
			self.specTable[p] = floor(self.baseStats[p] * uppermod) + floor((v + (addSpecValue[p] or 0)) * uppermod)
		elseif isNumberValue[p] then
			self.specTable[p] = floor((v + (addSpecValue[p] or 0)) * (multiSpecValue[p] or 1))
		elseif p == "아이템 방어도" and multiSpecValue[p] and multiSpecValue[p] ~= 1 then
			self.specTable[p] = round((v + (addSpecValue[p] or 0)) * multiSpecValue[p])
		else
			self.specTable[p] = (v + (addSpecValue[p] or 0)) * (multiSpecValue[p] or 1)
		end
	end
	-- 전투력
	self.specTable["전투력"] = self.specTable["전투력"] + floor(apPerStr * (self.specTable["힘"] - 10)) + floor(apPerAgi * (self.specTable["민첩성"] - 10)) + addAttackPower
	self.specTable["원거리 전투력"] = self.specTable["원거리 전투력"] + floor(rapPerAgi * (self.specTable["민첩성"] - 10)) + addRangedAttackPower
	-- 방어도
	self.specTable["방어도"] = ceil((self.specTable["방어도"] or 0) + ceil(self.specTable["아이템 방어도"] or 0) + self.specTable["민첩성"] * 2)
	-- 방패 막기
	if baseBlock > 0 then
		self.specTable["방패 막기"] = (self.specTable["방패 막기"] or 0) + baseBlock + getBlockFromRating() + getEffectFromDefense() + getAddedEffectFromDefense()
		self.specTable["방패 피해 방어량"] = (self.specTable["방패 피해 방어량"] or 0) + floor(blockValuePerStr * (self.specTable["힘"] - 20))
		if self.meta and self.meta:find("방패 피해 방어량 ++(%d+)%%") and self.specTable["방패 피해 방어량"] then
			self.specTable["방패 피해 방어량"] = self.specTable["방패 피해 방어량"] * (1 + tonumber(self.meta:match("방패 피해 방어량 ++(%d+)%%")) / 100)
		end
	else
		self.specTable["방패 막기 숙련도"] = nil
		self.specTable["방패 막기"] = nil
		self.specTable["방패 피해 방어량"] = nil
	end
	-- 생명력 및 마나 계산
	self.specTable["생명력"] = (self.specTable["생명력"] or 0 ) + baseHealth + getHealth()
	if UnitPowerMax("player", 0) > 0 then
		self.specTable["마나"] = (self.specTable["마나"] or 0) + baseMana + getMana()
		if self.meta then
			if self.meta:find("마나 (%d+)%%") then
				self.specTable["마나"] = ceil(self.specTable["마나"] * (1 + tonumber(self.meta:match("마나 (%d+)%%")) / 100))
			elseif self.meta:find("마나 ++(%d+)%%") then
				self.specTable["마나"] = ceil(self.specTable["마나"] * (1 + tonumber(self.meta:match("마나 ++(%d+)%%")) / 100))
			end
		end
		self.specTable["5초당 마나 회복량"] = (self.specTable["5초당 마나 회복량"] or 0) + getManaRegenFromSpi()
	else
		self.specTable["마나"] = nil
		self.specTable["5초당 마나 회복량"] = nil
	end
	self.setLastCalc = true
	-- 전투력, 주문력(합만 계산), 마나 리젠 버프 적용
	if self.ApplyLastBuff then
		self:ApplyLastBuff()
	end
	-- 전투력, 주문력(합만 계산), 마나 리젠, 생명력, 마나 특성 적용
	if self.ApplyLastTalent then
		self:ApplyLastTalent()
	end
	-- 전투력, 주문력(곱만 계산)
	if self.ApplyLastMultiBuff then
		self:ApplyLastMultiBuff()
	end
	if self.ApplyLastMultiTalent then
		self:ApplyLastMultiTalent()
	end
	-- 시전 중 마나 회복량은 비 시전 중 마나 회복량 보다 클 수 없음
	if self.specTable["5초당 마나 회복량"] and self.specTable["시전 중 마나 회복량"] and self.specTable["5초당 마나 회복량"] < self.specTable["시전 중 마나 회복량"] then
		self.specTable["시전 중 마나 회복량"] = self.specTable["5초당 마나 회복량"]
	end
	self.specTable["전투력"] = floor(self.specTable["전투력"])
	self.specTable["원거리 전투력"] = floor(self.specTable["원거리 전투력"])
	self.specTable["주문 공격력"] = floor(self.specTable["주문 공격력"])
	self.specTable["주문 치유량"] = floor(self.specTable["주문 치유량"])
	self.specTable["방패 피해 방어량"] = self.specTable["방패 피해 방어량"] and ceil(self.specTable["방패 피해 방어량"]) or nil
	-- 치명타 및 극대화율
	self.specTable["치명타율"] = self.specTable["치명타율"] + getCritFromAgi() + getCritFromRating()
	self.specTable["원거리 치명타율"] = self.specTable["원거리 치명타율"] + getCritFromAgi() + getRangedCritFromRating()
	self.specTable["극대화율"] = self.specTable["극대화율"] + getSpellCritFromInt() + getSpellCritFromRating()
	-- 회피율
	self.specTable["회피율"] = self.specTable["회피율"] + getDodgeAfterDR()
	-- 무기 막기
	if baseParry > 0 then
		if self.class == "죽음의 기사" then
			self.specTable["무기 막기 숙련도"] = (self.specTable["무기 막기 숙련도"] or 0) + floor(self.specTable["힘"] * 0.25)
		else
			self.specTable["무기 막기 숙련도"] = self.specTable["무기 막기 숙련도"] or 0
		end
		self.specTable["무기 막기"] = self.specTable["무기 막기"] + baseParry + getAvoidanceAfterDR(getParryFromRating() + getEffectFromDefense(), "무기 막기") + getAddedEffectFromDefense()
	else
		self.specTable["무기 막기"] = nil
		self.specTable["무기 막기 숙련도"] = nil
	end
	-- 방어 숙련
	self.specTable["방어 숙련"] = (self.specTable["방어 숙련"] or 0) + self.level * 5 + floor(getRatingToEffect("방어 숙련도", "방어 숙련도"))
	-- 숙련
	self.specTable["숙련"] = (self.specTable["숙련"] or 0) + floor(getRatingToEffect("숙련도", "숙련도"))
	if self.specTable["숙련2"] then
		self.specTable["숙련2"] = self.specTable["숙련"] + self.specTable["숙련2"]
	elseif self.specTable["무기2 최소 공격력"] then
		self.specTable["숙련2"] = self.specTable["숙련"]
	end
	if self.specTable["숙련1"] then
		self.specTable["숙련"] = self.specTable["숙련"] + self.specTable["숙련1"]
	end
	-- 무기 초당 공격력과 무기 데미지 사이 보정
	for i = 1, 3 do
		if self.specTable["무기"..i.." 초당 공격력"] then
			self.specTable["무기"..i.." 초당 공격력"] = (2 * self.specTable["무기"..i.." 초당 공격력"] * self.specTable["무기"..i.." 공격 속도"] - self.specTable["무기"..i.." 최소 공격력"] - self.specTable["무기"..i.." 최대 공격력"]) / 2
			self.specTable["무기"..i.." 최소 공격력"] = self.specTable["무기"..i.." 최소 공격력"] + self.specTable["무기"..i.." 초당 공격력"] + 0.1
			self.specTable["무기"..i.." 최대 공격력"] = self.specTable["무기"..i.." 최대 공격력"] + self.specTable["무기"..i.." 초당 공격력"]
		end
	end
	-- 주장비 계산
	self.specTable["주장비 최소 공격력"] = (self.specTable["무기1 최소 공격력"] + (self.specTable["무기 공격력"] or 0) + (self.specTable["전투력"] / ATTACK_POWER_MAGIC_NUMBER + (self.specTable["무기1 추가 초당 공격력"] or 0)) * self.specTable["무기1 공격 속도"]) * self.specTable["주장비 공격력 배율"] * self.specTable["물리 공격력 배율"]
	self.specTable["주장비 최대 공격력"] = (self.specTable["무기1 최대 공격력"] + (self.specTable["무기 공격력"] or 0) + (self.specTable["전투력"] / ATTACK_POWER_MAGIC_NUMBER + (self.specTable["무기1 추가 초당 공격력"] or 0)) * self.specTable["무기1 공격 속도"]) * self.specTable["주장비 공격력 배율"] * self.specTable["물리 공격력 배율"]
	self.specTable["주장비 공격 속도"] = self.specTable["무기1 공격 속도"] / ((1 + getMeleeHasteFromRating() / 100) * self.specTable["공격 속도 배율"])
	self.tooltipTable["공격력"] = {
		"|cffffffff주장비|r",
		ATTACK_SPEED_SECONDS..":", ("%.2f"):format(self.specTable["주장비 공격 속도"]),
		COMBATLOG_HIGHLIGHT_DAMAGE..":", ("%d - %d"):format(floor(self.specTable["주장비 최소 공격력"] / self.specTable["물리 공격력 배율"]), ceil(self.specTable["주장비 최대 공격력"] / self.specTable["물리 공격력 배율"])),
		DAMAGE_PER_SECOND..":", ("%.1f"):format((self.specTable["주장비 최소 공격력"] + self.specTable["주장비 최대 공격력"]) / 2 / self.specTable["주장비 공격 속도"])
	}
	self.specTable["주장비 최소 공격력"] = floor(self.specTable["주장비 최소 공격력"])
	self.specTable["주장비 최대 공격력"] = ceil(self.specTable["주장비 최대 공격력"])
	if self.specTable["물리 공격력 배율"] > 1 then
		self.tooltipTable["공격력"][5] = self.tooltipTable["공격력"][5]..(("|cff00ff00 x%d%%"):format(floor(self.specTable["물리 공격력 배율"] * 100 + 0.5)))
	elseif self.specTable["물리 공격력 배율"] < 1 then
		self.tooltipTable["공격력"][5] = self.tooltipTable["공격력"][5]..(("|cffff2020 x%d%%"):format(floor(self.specTable["물리 공격력 배율"] * 100 + 0.5)))
	end
	self.tooltipTable["속도"] = {
		("|cffffffff공격 속도 %.2f"):format(self.specTable["주장비 공격 속도"]),
		CR_HASTE_RATING_TOOLTIP:format(self.specTable["가속도"] or 0, getMeleeHasteFromRating())
	}
	-- 보조장비 계산
	if self.specTable["무기2 최소 공격력"] then
		self.specTable["보조장비 최소 공격력"] = (self.specTable["무기2 최소 공격력"] + (self.specTable["무기 공격력"] or 0) + (self.specTable["전투력"] / ATTACK_POWER_MAGIC_NUMBER + (self.specTable["무기2 추가 초당 공격력"] or 0)) * self.specTable["무기2 공격 속도"]) * self.specTable["보조장비 공격력 배율"] * self.specTable["물리 공격력 배율"]
		self.specTable["보조장비 최대 공격력"] = (self.specTable["무기2 최대 공격력"] + (self.specTable["무기 공격력"] or 0) + (self.specTable["전투력"] / ATTACK_POWER_MAGIC_NUMBER + (self.specTable["무기2 추가 초당 공격력"] or 0)) * self.specTable["무기2 공격 속도"]) * self.specTable["보조장비 공격력 배율"] * self.specTable["물리 공격력 배율"]
		self.specTable["보조장비 공격 속도"] = self.specTable["무기2 공격 속도"] / ((getMeleeHasteFromRating() / 100 + 1) * self.specTable["공격 속도 배율"])
		self.tooltipTable["공격력"][8] = "\n\n|cffffffff보조장비|r"
		self.tooltipTable["공격력"][9] = ""
		self.tooltipTable["공격력"][10] = ATTACK_SPEED_SECONDS..":"
		self.tooltipTable["공격력"][11] = ("%.2f"):format(self.specTable["보조장비 공격 속도"])
		self.tooltipTable["공격력"][12] = COMBATLOG_HIGHLIGHT_DAMAGE..":"
		self.tooltipTable["공격력"][13] = ("%d - %d"):format(floor(self.specTable["보조장비 최소 공격력"] / self.specTable["물리 공격력 배율"]), ceil(self.specTable["보조장비 최대 공격력"] / self.specTable["물리 공격력 배율"]))
		self.tooltipTable["공격력"][14] = DAMAGE_PER_SECOND..":"
		self.tooltipTable["공격력"][15] = ("%.1f"):format((self.specTable["보조장비 최소 공격력"] + self.specTable["보조장비 최대 공격력"]) / 2 / self.specTable["보조장비 공격 속도"])
		self.specTable["보조장비 최소 공격력"] = floor(self.specTable["보조장비 최소 공격력"])
		self.specTable["보조장비 최대 공격력"] = ceil(self.specTable["보조장비 최대 공격력"])
		if self.specTable["물리 공격력 배율"] > 1 then
			self.tooltipTable["공격력"][13] = self.tooltipTable["공격력"][13]..(("|cff00ff00 x%d%%"):format(floor(self.specTable["물리 공격력 배율"] * 100 + 0.5)))
		elseif self.specTable["물리 공격력 배율"] < 1 then
			self.tooltipTable["공격력"][13] = self.tooltipTable["공격력"][13]..(("|cffff2020 x%d%%"):format(floor(self.specTable["물리 공격력 배율"] * 100 + 0.5)))
		end
		self.tooltipTable["속도"][1] = self.tooltipTable["속도"][1]..((" / %.2f"):format(self.specTable["보조장비 공격 속도"]))
	end
	-- 원거리 장비 계산
	if self.specTable["무기3 최소 공격력"] then
		if self.class == "마법사" or self.class == "흑마법사" or self.class == "사제" then
			-- 마법봉
			self.specTable["원거리 공격력 배율"] = floor(self.specTable["물리 공격력 배율"] * self.specTable["원거리 장비 공격력 배율"] * 1000 + 0.5) / 1000
			self.specTable["원거리 장비 최소 공격력"] = self.specTable["무기3 최소 공격력"] * self.specTable["원거리 공격력 배율"]
			self.specTable["원거리 장비 최대 공격력"] = self.specTable["무기3 최대 공격력"] * self.specTable["원거리 공격력 배율"]
			self.specTable["원거리 장비 공격 속도"] = self.specTable["무기3 공격 속도"] / ((1 + getRangedHasteFromRating() / 100) * self.specTable["원거리 공격 속도 배율"])
			self.tooltipTable["원거리 공격력"] = {
				"|cffffffff원거리 장비|r",
				ATTACK_SPEED_SECONDS..":", ("%.2f"):format(self.specTable["원거리 장비 공격 속도"]),
				COMBATLOG_HIGHLIGHT_DAMAGE..":", ("%d - %d"):format(max(floor(self.specTable["원거리 장비 최소 공격력"]), 1), max(ceil(self.specTable["원거리 장비 최대 공격력"]), 1)),
				DAMAGE_PER_SECOND..":", ("%.1f"):format((self.specTable["원거리 장비 최소 공격력"] + self.specTable["원거리 장비 최대 공격력"]) / 2 * self.specTable["원거리 공격력 배율"] / self.specTable["원거리 장비 공격 속도"])
			}
			self.specTable["원거리 장비 최소 공격력"] = floor(self.specTable["원거리 장비 최소 공격력"])
			self.specTable["원거리 장비 최대 공격력"] = ceil(self.specTable["원거리 장비 최대 공격력"])
		else
			local ammodps = self:GetAmmoDPS()
			self.specTable["원거리 공격력 배율"] = floor(self.specTable["물리 공격력 배율"] * 1000 + 0.5) / 1000
			self.specTable["원거리 장비 최소 공격력"] = (self.specTable["무기3 최소 공격력"] + (self.specTable["원거리 무기 공격력"] or 0) + (self.specTable["원거리 전투력"] / ATTACK_POWER_MAGIC_NUMBER + ammodps) * self.specTable["무기3 공격 속도"]) * self.specTable["원거리 장비 공격력 배율"] * self.specTable["원거리 공격력 배율"]
			self.specTable["원거리 장비 최대 공격력"] = (self.specTable["무기3 최대 공격력"] + (self.specTable["원거리 무기 공격력"] or 0) + (self.specTable["원거리 전투력"] / ATTACK_POWER_MAGIC_NUMBER + ammodps) * self.specTable["무기3 공격 속도"]) * self.specTable["원거리 장비 공격력 배율"] * self.specTable["원거리 공격력 배율"]
			self.specTable["원거리 장비 공격 속도"] = self.specTable["무기3 공격 속도"] / ((1 + getRangedHasteFromRating() / 100) * self.specTable["원거리 공격 속도 배율"])
			self.tooltipTable["원거리 공격력"] = {
				"|cffffffff원거리 장비|r",
				ATTACK_SPEED_SECONDS..":", ("%.2f"):format(self.specTable["원거리 장비 공격 속도"]),
				COMBATLOG_HIGHLIGHT_DAMAGE..":", ("%d - %d"):format(floor(self.specTable["원거리 장비 최소 공격력"] / self.specTable["원거리 공격력 배율"]), ceil(self.specTable["원거리 장비 최대 공격력"] / self.specTable["원거리 공격력 배율"])),
				DAMAGE_PER_SECOND..":", ("%.1f"):format((self.specTable["원거리 장비 최소 공격력"] + self.specTable["원거리 장비 최대 공격력"]) / 2 / self.specTable["원거리 장비 공격 속도"])
			}
			self.specTable["원거리 장비 최소 공격력"] = floor(self.specTable["원거리 장비 최소 공격력"])
			self.specTable["원거리 장비 최대 공격력"] = ceil(self.specTable["원거리 장비 최대 공격력"])
			if self.specTable["원거리 공격력 배율"] > 1 then
				self.tooltipTable["원거리 공격력"][5] = self.tooltipTable["원거리 공격력"][5]..(("|cff00ff00 x%d%%"):format(floor(self.specTable["원거리 공격력 배율"] * 100 + 0.5)))
			elseif self.specTable["원거리 공격력 배율"] < 1 then
				self.tooltipTable["원거리 공격력"][5] = self.tooltipTable["원거리 공격력"][5]..(("|cffff2020 x%d%%"):format(floor(self.specTable["원거리 공격력 배율"] * 100 + 0.5)))
			end
		end
	end
	for p, v in pairs(self.specTable) do
		if type(v) == "number" and v < 0 then
			self.specTable[p] = 0
		end
	end
	-- 속성별 주문 공격력 및 주문 극대화율 계산
	for _, school in ipairs(self.spellSchools) do
		if self.specTable[school.." 주문 공격력"] then
			self.specTable[school.." 주문 공격력"] = self.specTable[school.." 주문 공격력"] + (self.specTable["주문 공격력"] or 0)
		end
		if self.specTable[school.." 주문 극대화율"]then
			self.specTable[school.." 주문 극대화율"] = self.specTable[school.." 주문 극대화율"] + (self.specTable["극대화율"] or 0)
		end
	end
	-- 툴팁
	self.tooltipTable["힘"] = { ("|cffffffff힘 %d"):format(self.specTable["힘"]) }
	self.tooltipTable["민첩성"] = { ("|cffffffff민첩성 %d"):format(self.specTable["민첩성"]) }
	self.tooltipTable["체력"] = { ("|cffffffff체력 %d"):format(self.specTable["체력"]), DEFAULT_STAT3_TOOLTIP:format(getHealth()) }
	self.tooltipTable["지능"] = { ("|cffffffff지능 %d"):format(self.specTable["지능"]) }
	self.tooltipTable["정신력"] = { ("|cffffffff정신력 %d"):format(self.specTable["정신력"]) }
	self.tooltipTable["치명타율"] = { ("|cffffffff치명타율 %.2f%%"):format(self.specTable["치명타율"] or 0), CR_CRIT_MELEE_TOOLTIP:format(self.specTable["치명타 및 주문 극대화 적중도"] or 0, getCritFromRating()) }
	self.tooltipTable["극대화율"] = { ("|cffffffff극대화율 %.2f%%"):format(self.specTable["극대화율"] or 0), ("극대화 적중도 %d (극대화 적중률 +%.2f%%)"):format(self.specTable["주문 극대화 적중도"] or 0, getSpellCritFromRating()) }
	self.tooltipTable["주문 가속도"] = { "|cffffffff가속도", SPELL_HASTE_TOOLTIP:format(getRatingToEffect("주문 가속도", "주문 가속도")) }
	if apPerStr > 0 then
		self.tooltipTable["힘"][2] = DEFAULT_STAT1_TOOLTIP:format(apPerStr * ((self.specTable["힘"] or 0) - 10))
		if self.class == "죽음의 기사" then
			self.tooltipTable["힘"][3] = ""
			self.tooltipTable["힘"][4] = ("무기 막기 숙련도 %d만큼 증가"):format(self.specTable["힘"] * 0.25)
		elseif blockValuePerStr > 0 then
			self.tooltipTable["힘"][3] = ""
			self.tooltipTable["힘"][4] = ("방패 피해 방어량 %d만큼 증가"):format(floor(blockValuePerStr * (getValue("힘") - 20)))
		end
	end
	if apPerAgi > 0 then
		self.tooltipTable["민첩성"][2] = DEFAULT_STAT1_TOOLTIP:format(apPerAgi * ((self.specTable["민첩성"] or 0) - 10))
		self.tooltipTable["민첩성"][3] = ""
		self.tooltipTable["민첩성"][4] = DEFAULT_STAT2_TOOLTIP:format(getCritFromAgi(), getValue("민첩성") * 2)
	else
		self.tooltipTable["민첩성"][2] = DEFAULT_STAT2_TOOLTIP:format(getCritFromAgi(), getValue("민첩성") * 2)
	end
	if apPerInt > 0 then
		tinsert(self.tooltipTable["지능"], DEFAULT_STAT1_TOOLTIP:format(apPerInt * ((self.specTable["지능"] or 0))))
		tinsert(self.tooltipTable["지능"], "")
	end
	if spellCritPerInt > 0 then
		tinsert(self.tooltipTable["지능"], DEFAULT_STAT4_TOOLTIP:format(getMana(), getSpellCritFromInt()))
	end
	self.tooltipTable["방어도"] = { ("|cffffffff방어도 %d"):format(self.specTable["방어도"] or 0), DEFAULT_STATARMOR_TOOLTIP:format(getReductionFromArmor()) }
	if self.specTable["숙련2"] then
		self.tooltipTable["숙련"] = { ("|cffffffff숙련 %d / %d"):format(self.specTable["숙련"] or 0, self.specTable["숙련2"] or 0), CR_EXPERTISE_TOOLTIP:format(("%.2f%% / %.2f%%"):format((self.specTable["숙련"] or 0) / 4, (self.specTable["숙련2"] or 0) / 4), self.specTable["숙련도"] or 0, getRatingToEffect("숙련도", "숙련도")) }
	else
		self.tooltipTable["숙련"] = { ("|cffffffff숙련 %d"):format(self.specTable["숙련"] or 0), CR_EXPERTISE_TOOLTIP:format(("%.2f%%"):format((self.specTable["숙련"] or 0) / 4), self.specTable["숙련도"] or 0, getRatingToEffect("숙련도", "숙련도")) }
	end
	self.tooltipTable["적중도"] = { ("|cffffffff적중도 %d"):format(self.specTable["적중도"] or 0), CR_HIT_MELEE_TOOLTIP:format(self.level, getRatingToEffect("적중도", "적중도"), self.specTable["방어구 관통력"] or 0, (self.specTable["방어구 관통력"] or 0) / armorPenetration) }
	self.tooltipTable["주문 적중도"] = { ("|cffffffff적중도 %d"):format(self.specTable["주문 적중도"] or 0), CR_HIT_SPELL_TOOLTIP:format(self.level, getRatingToEffect("주문 적중도", "주문 적중도"), self.specTable["주문 관통력"] or 0, self.specTable["주문 관통력"] or 0) }
	self.tooltipTable["전투력"] = { ("|cffffffff근접 전투력 %d"):format(self.specTable["전투력"] or 0), MELEE_ATTACK_POWER_TOOLTIP:format((self.specTable["전투력"] or 0) / ATTACK_POWER_MAGIC_NUMBER) }
	self.tooltipTable["정신력"][2] = DEFAULT_STAT5_TOOLTIP:format(getHealthRegen())
	if UnitPowerMax("player", 0) > 0 then
		self.tooltipTable["정신력"][3] = ""
		self.tooltipTable["정신력"][4] = MANA_REGEN_FROM_SPIRIT:format(getManaRegenFromSpi())
		self.tooltipTable["5초당 마나 회복량"] = { "|cffffffff마나 회복량|r", MANA_REGEN_TOOLTIP:format(self.specTable["5초당 마나 회복량"] or 0, self.specTable["시전 중 마나 회복량"] or 0) }
	end
	self.tooltipTable["주문 공격력"] = ("|cffffffff공격력 증가 %d"):format(self.specTable["주문 공격력"] or 0)
	self.tooltipTable["주문 치유량"] = ("|cffffffff치유량 증가 %d"):format(self.specTable["주문 치유량"] or 0)
	self.tooltipTable["탄력도"] = { ("|cffffffff탄력도 %d"):format(self.specTable["탄력도"] or 0), RESILIENCE_TOOLTIP:format(getRatingToEffect("탄력도", "탄력도"), getRatingToEffect("탄력도", "탄력도") * RESILIENCE_CRIT_CHANCE_TO_DAMAGE_REDUCTION_MULTIPLIER, getRatingToEffect("탄력도", "탄력도")) }
	self.tooltipTable["방어 숙련"] = { ("|cffffffff방어 숙련 %d"):format(self.specTable["방어 숙련"] or 0), DEFAULT_STATDEFENSE_TOOLTIP:format(getValue("방어 숙련도"), getRatingToEffect("방어 숙련도", "방어 숙련도"), getEffectFromDefense() + getAddedEffectFromDefense(), getEffectFromDefense() + getAddedEffectFromDefense()) }
	self.tooltipTable["회피율"] = { ("|cffffffff회피 확률 %.2f%%"):format(self.specTable["회피율"] or 0), CR_DODGE_TOOLTIP:format(getValue("회피 숙련도"), getDodgeFromDodgeRating()) }
	self.tooltipTable["무기 막기"] = { ("|cffffffff무기 막기 확률 %.2f%%"):format(self.specTable["무기 막기"] or 0), CR_PARRY_TOOLTIP:format(getValue("무기 막기 숙련도"), getParryFromRating()) }
	self.tooltipTable["방패 막기"] = { ("|cffffffff방패 막기 확률 %.2f%%"):format(self.specTable["방패 막기"] or 0), CR_BLOCK_TOOLTIP:format(getValue("방패 막기 숙련도"), getRatingToEffect("방패 막기 숙련도", "방패 막기 숙련도"), getValue("방패 피해 방어량")) }
	if self.tooltipTable["원거리 공격력"] then
		self.tooltipTable["원거리 속도"] = {
			("|cffffffff공격 속도 %.2f"):format(self.specTable["원거리 장비 공격 속도"]),
			CR_HASTE_RATING_TOOLTIP:format(self.specTable["원거리 가속도"] or 0, getRangedHasteFromRating())
		}
	end
	self.tooltipTable["원거리 적중도"] = { ("|cffffffff적중도 %d"):format(self.specTable["원거리 적중도"] or 0), CR_HIT_RANGED_TOOLTIP:format(self.level, getRatingToEffect("적중도", "적중도"), self.specTable["방어구 관통력"] or 0, (self.specTable["방어구 관통력"] or 0) / armorPenetration) }
	self.tooltipTable["원거리 치명타율"] = { ("|cffffffff치명타율 %.2f%%"):format(self.specTable["원거리 치명타율"] or 0), CR_CRIT_RANGED_TOOLTIP:format(self.specTable["원거리 치명타 적중도"] or 0, getCritFromRating()) }
	self.tooltipTable["원거리 전투력"] = { ("|cffffffff원거리 전투력 %d"):format(self.specTable["원거리 전투력"] or 0), RANGED_ATTACK_POWER_TOOLTIP:format((self.specTable["원거리 전투력"] or 0) / ATTACK_POWER_MAGIC_NUMBER) }
	self:UpdateStatList("left")
	self:UpdateStatList("right")
	for _, f in ipairs(self.resistFrame) do
		f.resist = self.specTable[f.name] or 0
		f.resistLevel = getResistLevel(f.resist)
		f.text:SetText(f.resist)
	end
	for i = 1, 5 do
		if self.setList[i] then
			self.setTextLine[i]:SetFormattedText("%s (%d/%d)", self.setList[i], self:GetSetItemPiece(self.setList[i]))
			self.setTextLine[i]:Show()
		else
			self.setTextLine[i]:Hide()
		end
	end
	self:UpdatePlayerHealthAndManaText()
	clearValueTable()
end

function InvenSimulatorUI:UpdatePlayerHealthAndManaText()
	playerHealth, playerMana = UnitHealthMax("player"), UnitPowerMax("player", 0)
	if playerHealth > self.specTable["생명력"] then
		prefixColor = "ff7272"
	elseif playerHealth < self.specTable["생명력"] then
		prefixColor = "00ff00"
	else
		prefixColor = "ffffff"
	end
	self.maxHelathText:SetFormattedText("생명력: |cff%s%d|r", prefixColor, self.specTable["생명력"])
	if playerMana > 0 then
		if playerMana > self.specTable["마나"] then
			prefixColor = "ff7272"
		elseif playerMana < self.specTable["마나"] then
			prefixColor = "00ff00"
		else
			prefixColor = "ffffff"
		end
		self.maxManaText:SetFormattedText("마나: |cff%s%d|r", prefixColor, self.specTable["마나"])
		self.maxManaText:Show()
		self.talentPointsText:ClearAllPoints()
		self.talentPointsText:SetPoint("TOPLEFT", self.maxManaText, "BOTTOMLEFT", 0, -1)
	else
		self.maxManaText:Hide()
		self.talentPointsText:ClearAllPoints()
		self.talentPointsText:SetPoint("TOPLEFT", self.maxHelathText, "BOTTOMLEFT", 0, -1)
	end
	self.talentPointsText:SetFormattedText("특성: %d/%d/%d", InvenSimulatorTalentFrame:GetSpentPoints(1), InvenSimulatorTalentFrame:GetSpentPoints(2), InvenSimulatorTalentFrame:GetSpentPoints(3))

	local avgcount, avglevel = 0, 0
	for i = 1, 18 do
		if i ~= 4 and GetInventoryItemLink("player", i) then
			avgcount = avgcount + 1
			avglevel = avglevel + (select(4, GetItemInfo(GetInventoryItemLink("player", i))) or 0)
		end
	end
	avglevel = avgcount > 0 and (avglevel / avgcount) or 0
	if avglevel > self.specTable["평균 아이템 레벨"] then
		prefixColor = "ff7272"
	elseif avglevel < self.specTable["평균 아이템 레벨"] then
		prefixColor = "00ff00"
	else
		prefixColor = "ffffff"
	end
	self.avgItemLevelText:SetFormattedText("평균 아이템 레벨: |cff%s%d|r", prefixColor, self.specTable["평균 아이템 레벨"])
end