--
-- SmartAttach
--
-- @author  Manuel Leithner
-- @date  13/10/17
--
-- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.

SmartAttach = {}
SmartAttach.DISTANCE_THRESHOLD = 2.5
SmartAttach.ABS_ANGLE_THRESHOLD = math.rad(150)

function SmartAttach.prerequisitesPresent(specializations)
    return true
end

function SmartAttach:load(savegame)
    self.smartAttachCallback = SmartAttach.smartAttachCallback
    self.getCanBeSmartAttached = SmartAttach.getCanBeSmartAttached
    self.doSmartAttach = SmartAttach.doSmartAttach

    self.smartAttach = {}
    self.smartAttach.inputJointDescIndex = nil
    local jointTypeStr = getXMLString(self.xmlFile, "vehicle.smartAttach#jointType")
    if jointTypeStr ~= nil then
        local jointType = AttacherJoints.jointTypeNameToInt[jointTypeStr]
        if jointType ~= nil then
            for inputJointDescIndex, inputAttacherJoint in pairs(self.inputAttacherJoints) do
                if inputAttacherJoint.jointType == jointType then
                    self.smartAttach.inputJointDescIndex = inputJointDescIndex
                    break
                end
            end
            self.smartAttach.jointType = jointType

            if self.smartAttach.inputJointDescIndex == nil then
                print("Warning: SmartAttach jointType not defined in '"..self.configFileName.."'!")
            end
        else
            print("Warning: invalid jointType " .. jointTypeStr)
        end
    end

    local triggerNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.smartAttach#trigger"))
    if triggerNode ~= nil then
        self.smartAttach.trigger = triggerNode
        addTrigger(self.smartAttach.trigger, "smartAttachCallback", self)
    end

    self.smartAttach.targetVehicle = nil
    self.smartAttach.targetVehicleCount = 0
    self.smartAttach.jointDescIndex = nil
    self.smartAttach.activatable = SmartAttachActivatable:new(self)
end

function SmartAttach:delete()
    if self.smartAttach.activatable ~= nil then
        g_currentMission:removeActivatableObject(self.smartAttach.activatable)
        self.smartAttach.activatable = nil
    end
    if self.smartAttach.trigger ~= nil then
        removeTrigger(self.smartAttach.trigger)
        self.smartAttach.trigger = nil
    end
end

function SmartAttach:mouseEvent(posX, posY, isDown, isUp, button)
end

function SmartAttach:keyEvent(unicode, sym, modifier, isDown)
end

function SmartAttach:update(dt)
end

function SmartAttach:draw()
end

function SmartAttach:doSmartAttach(targetVehicle, inputJointDescIndex, jointDescIndex, noEventSend)
    SmartAttachEvent.sendEvent(self, targetVehicle, inputJointDescIndex, jointDescIndex, noEventSend)

    if self.isServer then
        if self.attacherVehicle ~= nil then
            self.attacherVehicle:detachImplementByObject(self)
        end

        targetVehicle:attachImplement(self, inputJointDescIndex, jointDescIndex, false)
    end
end

function SmartAttach:getCanBeSmartAttached()
    local targetVehicle = self.smartAttach.targetVehicle
    if targetVehicle == nil then
        return false
    end

    local activeForInput = self:getIsActiveForInput() or self.smartAttach.targetVehicle:getIsActiveForInput()
    if not activeForInput then
       return false
    end

    local attacherJoint = targetVehicle.attacherJoints[self.smartAttach.jointDescIndex].jointTransform
    local inputAttacherJoint = self.inputAttacherJoints[self.smartAttach.inputJointDescIndex].node
    local x1,y1,z1 = getWorldTranslation(attacherJoint)
    local x2,y2,z2 = getWorldTranslation(inputAttacherJoint)

    local distance = Utils.vector2Length(x1-x2,z1-z2)
    local yRot = Utils.getYRotationBetweenNodes(attacherJoint, inputAttacherJoint);

    return distance < SmartAttach.DISTANCE_THRESHOLD and math.abs(yRot) < SmartAttach.ABS_ANGLE_THRESHOLD
end

function SmartAttach:onAttach()
    self.smartAttach.targetVehicle = nil
    self.smartAttach.targetVehicleCount = 0
end

---Trigger callback
-- @param integer triggerId id of trigger
-- @param integer otherActorId id of other actor
-- @param boolean onEnter on enter
-- @param boolean onLeave on leave
-- @param boolean onStay on stay
-- @param integer otherShapeId id of other shape
-- @includeCode
function SmartAttach:smartAttachCallback(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId)
    if onEnter then
        local vehicle = g_currentMission.nodeToVehicle[otherActorId]
        if vehicle ~= nil then
            if self.smartAttach.targetVehicle == nil then
                if vehicle ~= nil and vehicle ~= self and vehicle.attacherJoints ~= nil then
                    for i=1, table.getn(vehicle.attacherJoints) do
                        local jointDesc = vehicle.attacherJoints[i]
                        if jointDesc.jointIndex == 0 and jointDesc.jointType == self.smartAttach.jointType then
                            self.smartAttach.targetVehicle = vehicle
                            self.smartAttach.jointDescIndex = i
                            self.smartAttach.targetVehicleCount = 0

                            local name = Utils.getNoNil(self.typeDesc, "")
                            local storeItem = StoreItemsUtil.storeItemsByXMLFilename[self.configFileName:lower()]
                            if storeItem ~= nil then
                                name = storeItem.name
                            end

                            if self.attacherVehicle ~= nil then
                                self.smartAttach.activatable.activateText = string.format(g_i18n:getText("action_doSmartAttachGround"), name)
                            else
                                self.smartAttach.activatable.activateText = string.format(g_i18n:getText("action_doSmartAttachTransform"), name)
                            end

                            g_currentMission:addActivatableObject(self.smartAttach.activatable)
                            break
                        end
                    end
                end
            end

            if vehicle == self.smartAttach.targetVehicle then
                self.smartAttach.targetVehicleCount = self.smartAttach.targetVehicleCount + 1
            end
        end
    elseif onLeave then
        if self.smartAttach.targetVehicle ~= nil then
            local object = g_currentMission.nodeToVehicle[otherActorId]
            if object ~= nil and object == self.smartAttach.targetVehicle then
                self.smartAttach.targetVehicleCount = self.smartAttach.targetVehicleCount - 1
                if self.smartAttach.targetVehicleCount <= 0 then
                    self.smartAttach.targetVehicle = nil
                    g_currentMission:removeActivatableObject(self.smartAttach.activatable)
                    self.smartAttach.targetVehicleCount = 0
                end
            end
        end
    end
end


--
-- SmartAttachActivatable
--
-- @author  Manuel Leithner
-- @date  24/09/14
--
-- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.

---This is the activable class for smartAttach
-- @category Specializations
SmartAttachActivatable = {}

local SmartAttachActivatable_mt = Class(SmartAttachActivatable)

---Returns new instance of class
-- @param table smartAttachVehicle object of smartAttachVehicle
-- @return table self new instance
-- @includeCode
function SmartAttachActivatable:new(smartAttachVehicle)
    local self = {}
    setmetatable(self, SmartAttachActivatable_mt)

    self.smartAttachVehicle = smartAttachVehicle
    self.activateText = ""

    return self
end

---Returns if is activateable
-- @return boolean isActivateable is activateable
-- @includeCode
function SmartAttachActivatable:getIsActivatable()
    return self.smartAttachVehicle:getCanBeSmartAttached()
end

---Called on activate object
-- @includeCode
function SmartAttachActivatable:onActivateObject()
    local vehicle = self.smartAttachVehicle;
    vehicle:doSmartAttach(vehicle.smartAttach.targetVehicle, vehicle.smartAttach.inputJointDescIndex, vehicle.smartAttach.jointDescIndex)
end

function SmartAttachActivatable:drawActivate()
end



SmartAttachEvent = {}
SmartAttachEvent_mt = Class(SmartAttachEvent, Event)

InitEventClass(SmartAttachEvent, "SmartAttachEvent")

function SmartAttachEvent:emptyNew()
    local self = Event:new(SmartAttachEvent_mt)
    return self
end

function SmartAttachEvent:new(vehicle, targetVehicle, inputJointDescIndex, jointDescIndex)
    local self = SmartAttachEvent:emptyNew()
    self.vehicle = vehicle
    self.targetVehicle = targetVehicle
    self.inputJointDescIndex = inputJointDescIndex
    self.jointDescIndex = jointDescIndex
    return self
end

function SmartAttachEvent:readStream(streamId, connection)
    self.vehicle = readNetworkNodeObject(streamId)
    self.targetVehicle = readNetworkNodeObject(streamId)
    self.inputJointDescIndex = streamReadUIntN(streamId, 7)
    self.jointDescIndex = streamReadUIntN(streamId, 7)
    self:run(connection)
end

function SmartAttachEvent:writeStream(streamId, connection)
    writeNetworkNodeObject(streamId, self.vehicle)
    writeNetworkNodeObject(streamId, self.targetVehicle)
    streamWriteUIntN(streamId, self.inputJointDescIndex, 7)
    streamWriteUIntN(streamId, self.jointDescIndex, 7)
end

function SmartAttachEvent:run(connection)
    self.vehicle:doSmartAttach(self.targetVehicle, self.inputJointDescIndex, self.jointDescIndex, true)

    if not connection:getIsServer() then
        g_server:broadcastEvent(SmartAttachEvent:new(self.vehicle, self.targetVehicle,  self.inputJointDescIndex, self.jointDescIndex), nil, connection, self.vehicle)
    end
end

function SmartAttachEvent.sendEvent(vehicle, targetVehicle, inputJointDescIndex, jointDescIndex, noEventSend)
    if noEventSend == nil or noEventSend == false then
        if g_server ~= nil then
            g_server:broadcastEvent(SmartAttachEvent:new(vehicle, targetVehicle, inputJointDescIndex, jointDescIndex), nil, nil, vehicle)
        else
            g_client:getServerConnection():sendEvent(SmartAttachEvent:new(vehicle, targetVehicle, inputJointDescIndex, jointDescIndex))
        end
    end
end