-- Base Proc --

Base_Proc = Base_Object:New({

	Buff = nil,
	OnSelf = false,
	SpellOnly = false,
	MaxLevel = nil,
	ChancePerLevel = 0,
	LevelPerLevel = 0,
	DurationPerLevel = 0,
	DamageTypeName = "",

	GetChance = function(self, Item)
		return math.floor(Item.Chance + Item.Upgrades * self.ChancePerLevel)
	end,

	GetLevel = function(self, Source, Item)
		Level = math.floor(Item.Level + Item.Upgrades * self.LevelPerLevel)
		if self.MaxLevel ~= nil then
			Level = math.min(Level, self.MaxLevel)
		end

		return Level
	end,

	GetModifier = function(self, Source, Item)
		return 0
	end,

	GetDuration = function(self, Source, Item)
		local Multiplier = self.DurationName and Source[self.DurationName] * 0.01 or 1
		return RoundDown1((Item.Duration + Item.Upgrades * self.DurationPerLevel) * Multiplier)
	end,

	GetTotal = function(self, Source, Item)
		return math.floor(self:GetLevel(Source, Item)) * math.floor(self:GetDuration(Source, Item))
	end,

	GetDamageText = function(self, Source, Item)
		if Item.MoreInfo == true then
			Text = "[c green]" .. FormatSI(self:GetLevel(Source, Item)) .. "[c white] stacking " .. self.DamageTypeName .. " DPS"
		else
			Text = "[c green]" .. FormatSI(self:GetTotal(Source, Item)) .. "[c white] stacking " .. self.DamageTypeName .. " damage over [c green]" .. self:GetDuration(Source, Item) .. "[c white] seconds"
		end

		return Text
	end,

	AddBuff = function(self, Change, Buff, Source, Item, Level)

		-- Get stats from item
		local Duration = self:GetDuration(Source, Item)
		local Modifier = self:GetModifier(Source, Item)

		-- Check for existing buff
		if Change.Buff == nil then
			Change.Buff = Buff
			Change.BuffLevel = Level
			Change.BuffDuration = Duration
			Change.BuffModifier = Modifier
			return
		end

		-- Buff exists, but is different
		if Change.Buff ~= Buff then
			return
		end

		-- Take better modifier
		if Change.BuffModifier and Modifier > Change.BuffModifier then
			Change.BuffModifier = Modifier
		end

		-- Take better level
		if Level > Change.BuffLevel then
			Change.BuffLevel = Level
		end

		-- Take better duration
		if Duration > Change.BuffDuration then
			Change.BuffDuration = Duration
		end
	end,

	Proc = function(self, Roll, Item, Source, Target, Result)
		if Roll <= self:GetChance(Item) then
			if self.OnSelf == true then
				self:AddBuff(Result.Source, self.Buff.Pointer, Source, Item, self:GetLevel(Source, Item))
			else
				self:AddBuff(Result.Target, self.Buff.Pointer, Source, Item, self:GetLevel(Source, Item))
			end

			return true
		end

		return false
	end,
})

-- Stun --

Proc_Stun = Base_Proc:New({

	Buff = Buff_Stunned,
	MaxChance = 75,
	ChancePerLevel = 1,
	LevelPerLevel = 0,
	DurationPerLevel = 0.05,

	GetChance = function(self, Item)
		return math.min(math.floor(Item.Chance + Item.Upgrades * self.ChancePerLevel), self.MaxChance)
	end,

	GetDuration = function(self, Source, Item)
		return Item.Duration + math.floor(10 * Item.Upgrades * self.DurationPerLevel) / 10
	end,

	GetInfo = function(self, Source, Item)
		return "[c green]" .. self:GetChance(Item) .. "%[c white] chance to stun for [c green]" .. self:GetDuration(Source, Item) .. "[c white] seconds"
	end,
})

-- Poison --

Proc_Poison = Base_Proc:New({

	Buff = Buff_Poisoned,
	ChancePerLevel = 1,
	PercentPerLevel = 10,
	DurationPerLevel = 0,
	DamageTypeName = "poison",
	DurationName = "PoisonDuration",

	GetInfo = function(self, Source, Item)
		return "[c green]" .. self:GetChance(Item) .. "%[c white] chance for " .. self:GetDamageText(Source, Item)
	end,

	GetModifier = function(self, Source, Item)
		return math.floor(25 + 2 * Item.Upgrades)
	end,

	GetLevel = function(self, Source, Item)
		return math.floor((Item.Level + Item.Level * Item.Upgrades * self.PercentPerLevel * 0.01) * Source.PoisonPower * 0.01)
	end,
})

-- Slow --

Proc_Slow = Base_Proc:New({

	Buff = Buff_Slowed,
	ChancePerLevel = 1,
	LevelPerLevel = 1,
	DurationPerLevel = 0.1,
	MaxLevel = 75,

	GetInfo = function(self, Source, Item)
		return "[c green]" .. self:GetChance(Item) .. "%[c white] chance to slow target by [c green]" .. self:GetLevel(Source, Item) .. "%[c white] for [c green]" .. self:GetDuration(Source, Item) .. "[c white] seconds"
	end,
})

-- Ice Brand Slow --

Proc_IceBrand = Proc_Slow:New({

	DurationName = "ColdDuration",

	GetModifier = function(self, Source, Item)
		return math.floor(75 + 3 * Item.Upgrades)
	end,
})

-- Ignite --

Proc_Ignite = Base_Proc:New({

	Buff = Buff_Burning,
	ChancePerLevel = 1,
	PercentPerLevel = 10,
	DurationPerLevel = 0,
	DamageTypeName = "fire",
	DurationName = "FireDuration",

	GetInfo = function(self, Source, Item)
		return "[c green]" .. self:GetChance(Item) .. "%[c white] chance for " .. self:GetDamageText(Source, Item)
	end,

	GetLevel = function(self, Source, Item)
		return math.floor((Item.Level + Item.Level * Item.Upgrades * self.PercentPerLevel * 0.01) * Source.FirePower * 0.01)
	end,
})

-- Bleed --

Proc_Bleed = Base_Proc:New({

	Buff = Buff_Bleeding,
	ChancePerLevel = 1,
	PercentPerLevel = 10,
	DurationPerLevel = 0,
	DamageTypeName = "bleed",
	DurationName = "BleedDuration",

	GetInfo = function(self, Source, Item)
		return "[c green]" .. self:GetChance(Item) .. "%[c white] chance for " .. self:GetDamageText(Source, Item)
	end,

	GetLevel = function(self, Source, Item)
		return math.floor((Item.Level + Item.Level * Item.Upgrades * self.PercentPerLevel * 0.01) * Source.BleedPower * 0.01)
	end,
})

-- Bloodlet --

Proc_Bloodlet = Base_Proc:New({

	ChancePerLevel = 1,
	PercentPerLevel = 10,
	DurationPerLevel = 0,
	HealMultiplier = 0.25,
	DamageTypeName = "bleed",
	DurationName = "BleedDuration",

	GetInfo = function(self, Source, Item)
		Chance = self:GetChance(Item)

		return "[c green]" .. Chance .. "%[c white] chance for " .. self:GetDamageText(Source, Item) .. " and " .. self:GetHealingText(Source, Item)
	end,

	GetHealingText = function(self, Source, Item)
		if Item.MoreInfo == true then
			Text = "[c green]" .. FormatSI(self:GetHealingLevel(Source, Item)) .. "[c white] stacking HPS"
		else
			Healing = self:GetHealingTotal(Source, Item)
			Duration = self:GetDuration(Source, Item)
			Text = "[c green]" .. FormatSI(Healing) .. "[c white] stacking HP over [c green]" .. Duration .. "[c white] seconds"
		end

		return Text
	end,

	GetHealingTotal = function(self, Source, Item)
		return math.floor(self:GetHealingLevel(Source, Item)) * math.floor(self:GetDuration(Source, Item))
	end,

	GetHealingLevel = function(self, Source, Item)
		return math.floor((Item.Level + Item.Level * Item.Upgrades * self.PercentPerLevel * 0.01) * Source.HealPower * 0.01 * self.HealMultiplier)
	end,

	GetLevel = function(self, Source, Item)
		return math.floor((Item.Level + Item.Level * Item.Upgrades * self.PercentPerLevel * 0.01) * Source.BleedPower * 0.01)
	end,

	Proc = function(self, Roll, Item, Source, Target, Result)
		if Roll <= self:GetChance(Item) then
			self:AddBuff(Result.Target, Buff_Bleeding.Pointer, Source, Item, self:GetLevel(Source, Item))
			self:AddBuff(Result.Source, Buff_Healing.Pointer, Source, Item, self:GetHealingLevel(Source, Item))

			return true
		end

		return false
	end,
})

-- Haste --

Proc_Haste = Base_Proc:New({

	Buff = Buff_Hasted,
	OnSelf = true,
	ChancePerLevel = 1,
	LevelPerLevel = 0.2,
	DurationPerLevel = 0.05,

	GetInfo = function(self, Source, Item)
		return "[c green]" .. self:GetChance(Item) .. "%[c white] chance for a [c green]" .. self:GetLevel(Source, Item) .. "%[c white] speed boost for [c green]" .. self:GetDuration(Source, Item) .. "[c white] seconds"
	end,
})

-- Harden --

Proc_Harden = Base_Proc:New({

	Buff = Buff_Hardened,
	OnSelf = true,
	ChancePerLevel = 1,
	LevelPerLevel = 1,
	DurationPerLevel = 0.1,

	GetInfo = function(self, Source, Item)
		return "[c green]" .. self:GetChance(Item) .. "%[c white] chance for a [c green]" .. self:GetLevel(Source, Item) .. "[c white] armor buff for [c green]" .. self:GetDuration(Source, Item) .. "[c white] seconds"
	end,
})

-- Blind --

Proc_Blind = Base_Proc:New({

	Buff = Buff_Blinded,
	ChancePerLevel = 1,
	LevelPerLevel = 1,
	DurationPerLevel = 0.1,

	GetInfo = function(self, Source, Item)
		return "[c green]" .. self:GetChance(Item) .. "%[c white] chance to inflict [c green]" .. self:GetLevel(Source, Item) .. "%[c white] blindness for [c green]" .. self:GetDuration(Source, Item) .. "[c white] seconds"
	end,
})

-- Empowered --

Proc_Empowered = Base_Proc:New({

	Buff = Buff_Empowered,
	OnSelf = true,
	ChancePerLevel = 0.5,
	LevelPerLevel = 0.2,
	DurationPerLevel = 0.1,

	GetInfo = function(self, Source, Item)
		return "[c green]" .. self:GetChance(Item) .. "%[c white] chance for a [c green]" .. self:GetLevel(Source, Item) .. "%[c white] attack damage buff for [c green]" .. self:GetDuration(Source, Item) .. "[c white] seconds"
	end,
})

-- Sanctuary --

Proc_Sanctuary = Base_Proc:New({

	Buff = Buff_Sanctuary,
	OnSelf = true,
	ChancePerLevel = 1,
	LevelPerLevel = 1,
	DurationPerLevel = 0,
	DurationName = "HealDuration",

	GetInfo = function(self, Source, Item)
		return "[c green]" .. self:GetChance(Item) .. "%[c white] chance for a level [c green]" .. self:GetLevel(Source, Item) .. "[c white] [c yellow]sanctuary[c white] buff for [c green]" .. self:GetDuration(Source, Item) .. "[c white] seconds"
	end,
})

-- Health --

Proc_Health = Base_Proc:New({

	ChancePerLevel = 1,
	LevelPerLevel = 5,
	DurationPerLevel = 0,

	GetInfo = function(self, Source, Item)
		return "[c green]" .. self:GetChance(Item) .. "%[c white] chance to restore [c green]" .. FormatSI(self:GetLevel(Source, Item)) .. "[c white] HP"
	end,

	GetLevel = function(self, Source, Item)
		return math.floor((Item.Level + math.floor(Item.Upgrades * self.LevelPerLevel)) * Source.HealPower * 0.01)
	end,

	Proc = function(self, Roll, Item, Source, Target, Result)
		if Roll <= self:GetChance(Item) then
			if Result.Source.Health == nil then
				Result.Source.Health = self:GetLevel(Source, Item)
			else
				Result.Source.Health = Result.Source.Health + self:GetLevel(Source, Item)
			end
		end

		return false
	end,
})

-- Mana --

Proc_Mana = Base_Proc:New({

	ChancePerLevel = 1,
	LevelPerLevel = 5,
	DurationPerLevel = 0,
	SpellOnly = true,

	GetInfo = function(self, Source, Item)
		return "[c green]" .. self:GetChance(Item) .. "%[c white] chance to restore [c blue]" .. FormatSI(self:GetLevel(Source, Item)) .. "[c white] MP on spell use"
	end,

	GetLevel = function(self, Source, Item)
		return math.floor((Item.Level + math.floor(Item.Upgrades * self.LevelPerLevel)) * Source.ManaPower * 0.01)
	end,

	Proc = function(self, Roll, Item, Source, Target, Result)
		if Roll <= self:GetChance(Item) then
			if Result.Source.Mana == nil then
				Result.Source.Mana = self:GetLevel(Source, Item)
			else
				Result.Source.Mana = Result.Source.Mana + self:GetLevel(Source, Item)
			end
		end

		return false
	end,
})
