FoldableExtension = {}

function FoldableExtension.prerequisitesPresent(specializations)
  return SpecializationUtil.hasSpecialization(Foldable, specializations) and SpecializationUtil.hasSpecialization(Motorized, specializations)
end

function FoldableExtension.initSpecialization()
  local schema = Vehicle.xmlSchema
  schema:setXMLSpecializationType("FoldableExtension")

  SoundManager.registerSampleXMLPaths(schema, "vehicle.foldable.sounds", "start")
  SoundManager.registerSampleXMLPaths(schema, "vehicle.foldable.sounds", "work")
  SoundManager.registerSampleXMLPaths(schema, "vehicle.foldable.sounds", "stop")
  EffectManager.registerEffectXMLPaths(schema, "vehicle.foldable.foldableEffects")
  schema:register(XMLValueType.FLOAT, "vehicle.foldable.increaseRpm#value")
  schema:register(XMLValueType.BOOL, "vehicle.foldable#loadSavedPosition")
  schema:register(XMLValueType.STRING, "vehicle.foldable.foldingConfigurations.foldingConfiguration(?).foldingParts.loopAnimation(?)#name")
  schema:register(XMLValueType.FLOAT, "vehicle.foldable.foldingConfigurations.foldingConfiguration(?).foldingParts.loopAnimation(?)#turnOnFadeTime")
  schema:register(XMLValueType.FLOAT, "vehicle.foldable.foldingConfigurations.foldingConfiguration(?).foldingParts.loopAnimation(?)#turnOffFadeTime")
  schema:register(XMLValueType.FLOAT, "vehicle.foldable.foldingConfigurations.foldingConfiguration(?).foldingParts.loopAnimation(?)#speedScale")
end

function FoldableExtension.registerEventListeners(vehicleType)	
  SpecializationUtil.registerEventListener(vehicleType, "onLoad", FoldableExtension)
  SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", FoldableExtension)
  SpecializationUtil.registerEventListener(vehicleType, "onUpdate", FoldableExtension)
  SpecializationUtil.registerEventListener(vehicleType, "onDelete", FoldableExtension)
  SpecializationUtil.registerEventListener(vehicleType, "onStopMotor", FoldableExtension)
  SpecializationUtil.registerEventListener(vehicleType, "onFoldStateChanged", FoldableExtension)
end

function FoldableExtension.registerOverwrittenFunctions(vehicleType)
  SpecializationUtil.registerOverwrittenFunction(vehicleType, "getPtoRpm", FoldableExtension.getPtoRpm)
end

function FoldableExtension:onLoad(savegame)
  self.foldingSounds = {
    start = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.foldable.sounds", "start", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self),
    stop  = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.foldable.sounds", "stop", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self),
    work  = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.foldable.sounds", "work", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
  }

  local foldingConfigurationId = Utils.getNoNil(self.configurations.folding, 1)
  local configKey = string.format("vehicle.foldable.foldingConfigurations.foldingConfiguration(%d).foldingParts", foldingConfigurationId - 1)

  self.foldingLoopAnimations = {}

  local i = 0

  while true do
    local key = string.format(configKey .. ".loopAnimation(%d)", i)

    if not self.xmlFile:hasProperty(key) then
      break
    end

    local entry = {}
    local name = self.xmlFile:getValue(key .. "#name")

    if name ~= nil then
      entry.name = name
      entry.turnOnFadeTime = self.xmlFile:getValue(key .. "#turnOnFadeTime", 1) * 1000
      entry.turnOffFadeTime = self.xmlFile:getValue(key .. "#turnOffFadeTime", 1) * 1000
      entry.speedScale = self.xmlFile:getValue(key .. "#speedScale", 1)
      entry.speedDirection = 0
      entry.currentSpeed = 0

      table.insert(self.foldingLoopAnimations, entry)
    end

    i = i + 1
  end

  self.foldableEffects = g_effectManager:loadEffect(self.xmlFile, "vehicle.foldable.foldableEffects", self.components, self, self.i3dMappings)
  self.increaseRPM = self.xmlFile:getValue("vehicle.foldable.increaseRpm#value")
  self.loadSavedFoldPosition = self.xmlFile:getValue("vehicle.foldable#loadSavedPosition", true)
end

function FoldableExtension:onPostLoad(savegame)
  local spec = self.spec_foldable

  if savegame ~= nil and not savegame.resetVehicles then
    if not self.loadSavedFoldPosition then
      spec.loadedFoldAnimTime = spec.startAnimTime

      if spec.loadedFoldAnimTime <= 0 then
        spec.foldMoveDirection = -1
      else
        spec.foldMoveDirection = 1
      end

      Foldable.setAnimTime(self, spec.startAnimTime, false)
    end
  end
end

function FoldableExtension:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
  if self.isClient then
    if self.playAnimation ~= nil then
      for _, animation in ipairs(self.foldingLoopAnimations) do
        if animation.speedDirection ~= 0 then
          local duration = animation.turnOnFadeTime

          if animation.speedDirection == -1 then
            duration = animation.turnOffFadeTime
          end

          animation.currentSpeed = MathUtil.clamp(animation.currentSpeed + animation.speedDirection * dt/duration, 0, 1)

          self:setAnimationSpeed(animation.name, animation.currentSpeed*animation.speedScale)

          if animation.speedDirection == -1 and animation.currentSpeed == 0 then
            self:stopAnimation(animation.name, true)
          end

          if animation.currentSpeed == 1 or animation.currentSpeed == 0 then
            animation.speedDirection = 0
          end
        end
      end
    end
  end
end

function FoldableExtension:onStopMotor()
  local spec = self.spec_foldable

  if spec.requiresPower then
    if spec.foldMoveDirection ~= -1 then
      self:setFoldDirection(-1, true)
    end
  end
end

function FoldableExtension:onFoldStateChanged(direction, moveToMiddle)
  if direction == 1 then
    if self.isClient then
      if self.playAnimation ~= nil then
        for _, animation in ipairs(self.foldingLoopAnimations) do
          animation.speedDirection = 1

          self:playAnimation(animation.name, math.max(animation.currentSpeed*animation.speedScale, 0.001), self:getAnimationTime(animation.name), true)
        end
      end
    end

    g_soundManager:stopSamples(self.foldingSounds)
    g_soundManager:playSample(self.foldingSounds.start)
    g_soundManager:playSample(self.foldingSounds.work, 0, self.foldingSounds.start)

    if self.foldableEffects ~= nil then
      g_effectManager:setFillType(self.foldableEffects, FillType.WHEAT)
      g_effectManager:startEffects(self.foldableEffects)
    end
  else
    if self.isClient then
      if self.playAnimation ~= nil then
        for _, animation in ipairs(self.foldingLoopAnimations) do
          animation.speedDirection = -1
        end
      end
    end

    g_soundManager:stopSamples(self.foldingSounds)
    g_soundManager:playSample(self.foldingSounds.stop)

    if self.foldableEffects ~= nil then
      g_effectManager:stopEffects(self.foldableEffects)
    end
  end
end

function FoldableExtension:getPtoRpm(superfunc)
  local rpm = 0

  if self:getDoConsumePtoPower() then
    rpm = self.spec_powerConsumer.ptoRpm
  else
    if self:getToggledFoldDirection() ~= 1 then
      if self.increaseRPM ~= nil then
        rpm = self.increaseRPM
      end
    end
  end

  return rpm
end

function FoldableExtension:onDelete()
  if self.isClient then
    g_soundManager:deleteSamples(self.foldingSounds)

    if self.foldableEffects ~= nil then
      g_effectManager:deleteEffects(self.foldableEffects)
    end
  end
end