local FollowText = require "widgets/followtext"

Line = Class(function(self, message, duration, noanim)
    self.message = message
    self.duration = duration
    self.noanim = noanim
end)


local Talker = Class(function(self, inst)
    self.inst = inst
    self.task = nil
    self.ignoring = false
end)

function Talker:IgnoreAll()
    self.ignoring = true
end

function Talker:StopIgnoringAll()
    self.ignoring = false
end

local function IsBonesawCmd(str)
    for i,v in pairs(BONESAW_MATCH_STRINGS) do
        if string.match(str, v) ~= nil then
            return true
        end
    end
    return false
end

local function sayfn(inst, script, nobroadcast, colour)
    -- Early out if this is an emote
    for i,v in pairs(script) do
        local chat_string_lower = string.lower(v.message)
        local bonesaw = IsBonesawCmd(chat_string_lower)
        local emoteAnim = nil
        local emoteFX = nil
        for i,v in pairs(EMOTES) do
            if v.command == chat_string_lower or (bonesaw and table.contains(BONESAW_CMDS, v.command)) then
                if TheWorld.ismastersim then
                    -- Send the anim event if we're the server
                    inst:PushEvent("emote", {anim=v.anim, fx=v.fx, fxoffset=v.fxoffset, fxdelay=v.fxdelay, randomanim=v.randomanim})
                end
                return
            end
        end
    end

    -- If it's not an emote, continue
    
    if not inst.components.talker.widget then
        -- KAJ: TODO - HACK FOR PAX
        if (ThePlayer and ThePlayer.HUD) then
            inst.components.talker.widget = ThePlayer.HUD:AddChild(FollowText(inst.components.talker.font or TALKINGFONT, inst.components.talker.fontsize or 35))
        end
    end

    if inst.components.talker.widget then
        inst.components.talker.widget.symbol = inst.components.talker.symbol
        --#srosen this was originally a hack for PAX, but this might be a totally acceptable way to do this...
        if inst:HasTag("playerghost") then
            inst.components.talker.widget:SetOffset(Vector3(0, -700, 0))
        elseif inst:HasTag("sharp") then
			-- don't do anything, it's already set, dammit
		else
            inst.components.talker.widget:SetOffset(inst.components.talker.offset or Vector3(0, -400, 0))
        end
        inst.components.talker.widget:SetTarget(inst)
        if colour and not inst:HasTag("sharp") then
            inst.components.talker.widget.text:SetColour(colour[1], colour[2], colour[3], colour[4])
        else
            if inst.components.talker.colour then
                inst.components.talker.widget.text:SetColour(inst.components.talker.colour.x, inst.components.talker.colour.y, inst.components.talker.colour.z, 1)
            end
        end
    end
    
    for k,line in ipairs(script) do
        
        if line.message then
            if inst.components.talker.widget then
                inst.components.talker.widget.text:SetString(line.message)
            end
            inst:PushEvent("ontalk", {noanim = line.noanim})
            if nobroadcast == nil then
                local whisper = true
                TheNet:Talker( line.message, inst.entity )
            end
        else
            if inst.components.talker.widget then
                inst.components.talker.widget:Hide()
            end
        end
        Sleep(line.duration)
    
    end
    
    if inst.components.talker.widget then
        inst.components.talker.widget:Kill()    
        inst.components.talker.widget = nil
    end
    inst:PushEvent("donetalking")

end

function Talker:OnRemoveEntity()
	self:ShutUp()	
end

function Talker:ShutUp()
    if self.task then
        scheduler:KillTask(self.task)
        
        if self.widget then
            self.widget:Kill()
            self.widget = nil
        end
        self.inst:PushEvent("donetalking")
        self.task = nil
    end
end

function Talker:Say(script, time, noanim, force, nobroadcast, colour)
   if TheWorld.ismastersim then
        if self.inst.components.health and  self.inst.components.health:IsDead() and not force then
            return
        end
        
        if self.inst.components.sleeper and  self.inst.components.sleeper:IsAsleep() and not force then
            return
        end
        
        if self.ignoring and not force then
            return
        end
        
    	if self.ontalk then
            self.ontalk(self.inst, script)
    	end
    end
    
    local lines = nil
    if type(script) == "string" then
        lines = {Line(script, time or 2.5, noanim)}
    else
        lines = script
    end

    self:ShutUp()
    if lines then
        self.task = self.inst:StartThread( function() sayfn(self.inst, lines, nobroadcast, colour) end)    
    end
end

return Talker