  --
  -- SprayerNew
  -- Class for all sprayers
  --
  -- @author  Stefan Geiger
  -- @date  24/02/08
  --
  -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.
  
  source("dataS/scripts/vehicles/specializations/SetTurnedOnEvent.lua");
  source("dataS/scripts/vehicles/specializations/SprayerAreaEvent.lua");
  source("dataS/scripts/vehicles/specializations/SprayerSetIsFillingEvent.lua");
  SprayerNew = {};
  
  
  SprayerNew.SPRAYTYPE_UNKNOWN = 0;
  SprayerNew.NUM_SPRAYTYPES = 0;
  
  SprayerNew.sprayTypes = {};
  SprayerNew.sprayTypeIndexToDesc = {};
  
  SprayerNew.sprayTypeToFillType = {};
  SprayerNew.fillTypeToSprayType = {};
  
  function SprayerNew.registerSprayType(name, nameI18N, pricePerLiter, litersPerSqmPerSecond, partOfEconomy, hudOverlayFilename)
      local key = "SPRAYTYPE_"..string.upper(name);
      if SprayerNew[key] == nil then
          SprayerNew.NUM_SPRAYTYPES = SprayerNew.NUM_SPRAYTYPES+1;
          SprayerNew[key] = SprayerNew.NUM_SPRAYTYPES;
  
          local desc = {name = name, index = SprayerNew.NUM_SPRAYTYPES};
          desc.litersPerSqmPerSecond = litersPerSqmPerSecond;
  
  
          SprayerNew.sprayTypes[name] = desc;
          SprayerNew.sprayTypeIndexToDesc[SprayerNew.NUM_SPRAYTYPES] = desc;
  
          local fillType = Fillable.registerFillType(name, nameI18N, pricePerLiter, partOfEconomy, hudOverlayFilename)
          SprayerNew.sprayTypeToFillType[SprayerNew.NUM_SPRAYTYPES] = fillType;
          SprayerNew.fillTypeToSprayType[fillType] = SprayerNew.NUM_SPRAYTYPES;
      end;
  end;
  
  function SprayerNew.prerequisitesPresent(specializations)
      return SpecializationUtil.hasSpecialization(Fillable, specializations) and SpecializationUtil.hasSpecialization(fergusonsower, specializations);
  end;
  
  function SprayerNew:load(xmlFile)
  
      -- assert(self.setIsTurnedOn == nil, "SprayerNew needs to be the first specialization which implements setIsTurnedOn");
      -- self.setIsTurnedOn = SprayerNew.setIsTurnedOn;
      self.getIsTurnedOnAllowed = SprayerNew.getIsTurnedOnAllowed;
  
      -- assert(self.setIsSprayerFilling == nil, "SprayerNew needs to be the first specialization which implements setIsSprayerFilling");
      self.setIsSprayerFilling = SprayerNew.setIsSprayerFilling;
      self.addSprayerFillTrigger = SprayerNew.addSprayerFillTrigger;
      self.removeSprayerFillTrigger = SprayerNew.removeSprayerFillTrigger;
  
      self.fillLitersPerSecond = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.fillLitersPerSecond"), 500);
      self.isSprayerFilling = false;
  
      self.isSprayerTank = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.sprayer#isTank"), false);
      self.allowsSpraying = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.sprayer#allowsSpraying"), true);
  
      self.sprayLitersPerSecond = {};
      local i=0;
      while true do
          local key = string.format("vehicle.sprayUsages.sprayUsage(%d)", i);
          if not hasXMLProperty(xmlFile, key) then
              break;
          end;
          local fillType = getXMLString(xmlFile, key.. "#fillType");
          local litersPerSecond = getXMLFloat(xmlFile, key.. "#litersPerSecond");
          if fillType ~= nil and litersPerSecond ~= nil then
              local fillTypeInt = Fillable.fillTypeNameToInt[fillType];
              if fillTypeInt ~= nil then
                  self.sprayLitersPerSecond[fillTypeInt] = litersPerSecond;
                  if self.defaultSprayLitersPerSecond == nil then
                      self.defaultSprayLitersPerSecond = litersPerSecond;
                  end;
              else
                  print("Warning: Invalid spray usage fill type '"..fillType.."' in '" .. self.configFileName.. "'");
              end;
          end;
          i = i+1;
      end;
      if self.defaultSprayLitersPerSecond == nil then
          if self.allowsSpraying then
              print("Warning: No spray usage specified for '" .. self.configFileName.. "'. This sprayer will not use any spray.");
          end
          self.defaultSprayLitersPerSecond = 0;
      end;
  
      self.lastSprayValveUpdateFoldTime = nil;
      self.sprayValves = {};
  
      if self.isClient then
          local psFile = getXMLString(xmlFile, "vehicle.sprayParticleSystem#file");
          if psFile ~= nil then
              local i=0;
              while true do
                  local baseName = string.format("vehicle.sprayValves.sprayValve(%d)", i);
                  local node = getXMLString(xmlFile, baseName.. "#index");
                  if node == nil then
                      break;
                  end;
                  node = Utils.indexToObject(self.components, node);
                  if node ~= nil then
                      local sprayValve = {};
                      sprayValve.particleSystems = {};
                      Utils.loadParticleSystem(xmlFile, sprayValve.particleSystems, "vehicle.sprayParticleSystem", node, false, nil, self.baseDirectory);
  
                      sprayValve.foldMinLimit = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#foldMinLimit"), 0);
                      sprayValve.foldMaxLimit = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#foldMaxLimit"), 1);
  
                      table.insert(self.sprayValves, sprayValve);
                  end;
                  i = i+1;
              end;
          end;
  
          local spraySound = getXMLString(xmlFile, "vehicle.spraySound#file");
          if spraySound ~= nil and spraySound ~= "" then
              spraySound = Utils.getFilename(spraySound, self.baseDirectory);
              self.spraySound = createSample("spraySound");
              self.spraySoundEnabled = false;
              loadSample(self.spraySound, spraySound, false);
              self.spraySoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.spraySound#pitchOffset"), 1);
              self.spraySoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.spraySound#volume"), 1);
              setSamplePitch(self.spraySound, self.spraySoundPitchOffset);
          end;
      end;
  
  
  	self.turnedOnRotationNodes = {};
      local i = 0;
      while true do
          local key = string.format("vehicle.turnedOnRotationNodes.turnedOnRotationNode(%d)", i);
          if not hasXMLProperty(xmlFile, key) then
              break;
          end;
          local node = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index"));
          local rotSpeed = math.rad(Utils.getNoNil(getXMLFloat(xmlFile, key.."#rotSpeed"), 1)*0.001);
          if node ~= nil then
              table.insert(self.turnedOnRotationNodes, {node=node, rotSpeed=rotSpeed});
          end;
          i = i + 1;
      end;
  
      self.sprayerFillTriggers = {};
      self.sprayerFillActivatable = SprayerFillActivatable:new(self);
  
      self.showFieldNotOwnedWarning = false;
  
      self.isTurnedOn = false;
      self.speedViolationMaxTime = 1000;
      self.speedViolationTimer = self.speedViolationMaxTime;
  
      self.sprayerDirtyFlag = self:getNextDirtyFlag();
  end;
  
  function SprayerNew:delete()
  
      g_currentMission:removeActivatableObject(self.sprayerFillActivatable);
  
      for k,sprayValve in pairs(self.sprayValves) do
          Utils.deleteParticleSystem(sprayValve.particleSystems);
      end;
  
      if self.spraySound ~= nil then
          delete(self.spraySound);
          self.spraySoundEnabled = false;
      end;
  end;
  
  function SprayerNew:readStream(streamId, connection)
      local turnedOn = streamReadBool(streamId);
      local isSprayerFilling = streamReadBool(streamId);
      self:setIsTurnedOn(turnedOn, true);
      self:setIsSprayerFilling(isSprayerFilling, true);
  end;
  
  function SprayerNew:writeStream(streamId, connection)
      streamWriteBool(streamId, self.isTurnedOn);
      streamWriteBool(streamId, self.isSprayerFilling);
  end;
  
  function SprayerNew:readUpdateStream(streamId, timestamp, connection)
      if connection:getIsServer() then
          self.showFieldNotOwnedWarning = streamReadBool(streamId);
      end;
  end;
  
  function SprayerNew:writeUpdateStream(streamId, connection, dirtyMask)
      if not connection:getIsServer() then
          streamWriteBool(streamId, self.showFieldNotOwnedWarning);
      end;
  end;
  
  function SprayerNew:mouseEvent(posX, posY, isDown, isUp, button)
  end;
  
  function SprayerNew:keyEvent(unicode, sym, modifier, isDown)
  end;
  
  function SprayerNew:update(dt)
  	 -- if self.isChange then  
      -- if self.isClient then
          -- if self.allowsSpraying and self:getIsActiveForInput() then
              -- if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA) then
                  -- self:setIsTurnedOn(not self.isTurnedOn);
              -- end;
          -- end;
      -- end;
	-- end;
  end;
  
  function SprayerNew:updateTick(dt)
  
      if self:getIsActive() then
 	 if self.isChange then 	  
          if self:getIsTurnedOnAllowed(isTurnedOn) then
              -- SetTurnedOnEvent.sendEvent(self, isTurnedOn, noEventSend)
  
              -- self.isTurnedOn = isTurnedOn;
              if self.isClient then
                  self.lastSprayValveUpdateFoldTime = nil;
                  if not self.isTurnedOn or self.foldAnimTime == nil then
                      -- If the sprayer is foldable, the spray valves are only turned on within the update loop
                      for _,sprayValve in pairs(self.sprayValves) do
                          Utils.setEmittingState(sprayValve.particleSystems, self.isTurnedOn);
                      end
                  end
  
                  if not self.isTurnedOn and self.spraySoundEnabled then
                      stopSample(self.spraySound);
                      self.spraySoundEnabled = false;
                  end;
              end;
              self.speedViolationTimer = self.speedViolationMaxTime;
          end;	  
          local showFieldNotOwnedWarning = false;
          if self.isTurnedOn then
              if not self:getIsTurnedOnAllowed(true) then
                  self:setIsTurnedOn(false);
              end;
          end
          if self.isTurnedOn and self.allowsSpraying then
		  
  
              if self.isClient then
                  -- update valve particle systems
                  local foldAnimTime = self.foldAnimTime;
                  if foldAnimTime ~= nil and foldAnimTime ~= self.lastSprayValveUpdateFoldTime then
                      self.lastSprayValveUpdateFoldTime = foldAnimTime;
                      for _,sprayValve in pairs(self.sprayValves) do
                          Utils.setEmittingState(sprayValve.particleSystems, foldAnimTime <= sprayValve.foldMaxLimit and foldAnimTime >= sprayValve.foldMinLimit);
                      end
                  end
              end
  
              self.lastSprayingArea = 0;
  
              if self:doCheckSpeedLimit() and self.lastSpeed*3600 > 31 then
                  self.speedViolationTimer = self.speedViolationTimer - dt;
              else
                  self.speedViolationTimer = self.speedViolationMaxTime;
              end;
  
              if self.isServer then
                  if self.speedViolationTimer > 0 then
                      local litersPerSecond = self.sprayLitersPerSecond[self.currentFillType];
                      if litersPerSecond == nil then
                          litersPerSecond = self.defaultSprayLitersPerSecond;
                      end
                      local usage = litersPerSecond * dt*0.001;
  
                      local hasSpray = false;
  
                      if self.capacity == 0 or self:getIsHired() then
                          hasSpray = true;
                          local fillType = self.currentFillType;
                          if fillType == Fillable.FILLTYPE_UNKNOWN then
                              fillType = self:getFirstEnabledFillType();
                          end
                          local fillTypeDesc = Fillable.fillTypeIndexToDesc[fillType];
                          if fillTypeDesc ~= nil then
                              local delta = usage*fillTypeDesc.pricePerLiter
                              g_currentMission.missionStats.expensesTotal = g_currentMission.missionStats.expensesTotal + delta;
                              g_currentMission.missionStats.expensesSession = g_currentMission.missionStats.expensesSession + delta;
  
                              g_currentMission:addSharedMoney(-delta, "other");
  
                              -- TODO update spray stats
                              --[[g_currentMission.missionStats.sprayUsageTotal = g_currentMission.missionStats.sprayUsageTotal + usage;
                              g_currentMission.missionStats.sprayUsageSession = g_currentMission.missionStats.sprayUsageSession + usage;]]
                          end;
                      else
                          if self.fillLevel > 0 then
                              hasSpray = true;
                              self:setFillLevel(self.fillLevel - usage, self.currentFillType);
                          else
                              -- try to find another attached sprayer
                              local fillType = self.currentFillType;
                              if fillType == Fillable.FILLTYPE_UNKNOWN then
                                  fillType = self:getFirstEnabledFillType();
                              end
                              local sprayerTank = SprayerNew.findAttachedSprayerTank(self:getRootAttacherVehicle(), fillType);
                              if sprayerTank ~= nil then
                                  hasSpray = true;
                                  sprayerTank:setFillLevel(sprayerTank:getFillLevel(fillType) - usage, fillType);
                              end
                          end;
                      end;
  
                      if hasSpray then
                          local cuttingAreasSend = {};
                          for _,cuttingArea in pairs(self.cuttingAreas) do
                              if self:getIsAreaActive(cuttingArea) then
                                  local x,_,z = getWorldTranslation(cuttingArea.start);
                                  if g_currentMission:getIsFieldOwnedAtWorldPos(x,z) then
                                      local x1,_,z1 = getWorldTranslation(cuttingArea.width);
                                      local x2,_,z2 = getWorldTranslation(cuttingArea.height);
  
                                      local sqm = math.abs((z1-z)*(x2-x) - (x1-x)*(z2-z)); -- this is the cross product with y=0
  
                                      --Utils.updateSprayArea(x, z, x1, z1, x2, z2);
  
                                      self.lastSprayingArea = self.lastSprayingArea + sqm;
  
                                      table.insert(cuttingAreasSend, {x,z,x1,z1,x2,z2});
                                  else
                                      showFieldNotOwnedWarning = true;
                                  end
                              end;
                          end;
                          if (table.getn(cuttingAreasSend) > 0) then
                              SprayerAreaEvent.runLocally(cuttingAreasSend);
                              g_server:broadcastEvent(SprayerAreaEvent:new(cuttingAreasSend));
                          end;
                      end;
                  end;
              end;
  
              if self.isClient then
                  if not self.spraySoundEnabled and self.spraySound ~= nil and self:getIsActiveForSound() then
                      playSample(self.spraySound, 0, self.spraySoundVolume, 0);
                      self.spraySoundEnabled = true;
                  end;
              end;
  
              for _, node in pairs(self.turnedOnRotationNodes) do
                  rotate(node.node, 0, -dt*node.rotSpeed, 0);
              end
          else
              self.speedViolationTimer = self.speedViolationMaxTime;
          end;
  
          if self.isServer then
              if showFieldNotOwnedWarning ~= self.showFieldNotOwnedWarning then
                  self.showFieldNotOwnedWarning = showFieldNotOwnedWarning;
                  self:raiseDirtyFlags(self.sprayerDirtyFlag);
              end
          end
      end;
  
      if self.isServer and self.isSprayerFilling and self.isChange then
          local delta = 0;
          if self.sprayerFillTrigger ~= nil then
              delta = self.fillLitersPerSecond*dt*0.001;
              delta = self.sprayerFillTrigger:fillSprayer(self, delta);
          end
  
          if delta <= 0 then
              self:setIsSprayerFilling(false);
          end;
      end
	 end;
end;
  
function SprayerNew:draw()
  	 if self.isChange then  
      if self.isClient then
          if self:getIsActiveForInput(true) then
              if not self:getIsTurnedOnAllowed(true) and self.fillLevel <= 0 then
                  g_currentMission:addExtraPrintText(g_i18n:getText("FirstFillTheTool"));
              end;
  
              -- if self.allowsSpraying then
                  -- if self.isTurnedOn then
                      -- g_currentMission:addHelpButtonText(string.format(g_i18n:getText("turn_off_OBJECT"), self.typeDesc), InputBinding.IMPLEMENT_EXTRA);
                  -- else
                      -- g_currentMission:addHelpButtonText(string.format(g_i18n:getText("turn_on_OBJECT"), self.typeDesc), InputBinding.IMPLEMENT_EXTRA);
                  -- end;
              -- end
          end
  
          if math.abs(self.speedViolationTimer - self.speedViolationMaxTime) > 2 then
              g_currentMission:addWarning(g_i18n:getText("Dont_drive_to_fast") .. "\n" .. string.format(g_i18n:getText("Cruise_control_levelN"), "2"), 0.07+0.022, 0.019+0.029);
          elseif self.showFieldNotOwnedWarning then
              g_currentMission:addWarning(g_i18n:getText("You_dont_own_this_field"));
          end;
      end;
	  end;
end;
  
  function SprayerNew:onDetach()
      if self.deactivateOnDetach then
          SprayerNew.onDeactivate(self);
      else
          SprayerNew.onDeactivateSounds(self);
      end;
  end;
  
  function SprayerNew:onLeave()
      if self.deactivateOnLeave then
          SprayerNew.onDeactivate(self);
      else
          SprayerNew.onDeactivateSounds(self);
      end;
  end;
  
  function SprayerNew:onDeactivate()
      self.speedViolationTimer = self.speedViolationMaxTime;
      self.showFieldNotOwnedWarning = false;
      self:setIsTurnedOn(false, true)
      SprayerNew.onDeactivateSounds(self);
  end;
  
  function SprayerNew:onDeactivateSounds()
      if self.spraySoundEnabled then
          stopSample(self.spraySound);
          self.spraySoundEnabled = false;
      end;
  end;
  
  function SprayerNew:getIsTurnedOnAllowed(isTurnedOn)
      if not isTurnedOn or self.fillLevel > 0 or self.capacity == 0 or self:getIsHired() then
          return true;
      end;
      -- try to find tank
      local fillType = self.currentFillType;
      if fillType == Fillable.FILLTYPE_UNKNOWN then
          fillType = self:getFirstEnabledFillType();
      end
      local sprayerTank = SprayerNew.findAttachedSprayerTank(self:getRootAttacherVehicle(), fillType);
      if sprayerTank ~= nil then
          return true;
      end
  
      return false;
  end;
  
  -- function SprayerNew:setIsTurnedOn(isTurnedOn, noEventSend)
      -- if isTurnedOn ~= self.isTurnedOn then
          -- if self:getIsTurnedOnAllowed(isTurnedOn) then
              -- SetTurnedOnEvent.sendEvent(self, isTurnedOn, noEventSend)
  
              -- self.isTurnedOn = isTurnedOn;
              -- if self.isClient then
                  -- self.lastSprayValveUpdateFoldTime = nil;
                  -- if not self.isTurnedOn or self.foldAnimTime == nil then
                      -- -- If the sprayer is foldable, the spray valves are only turned on within the update loop
                      -- for _,sprayValve in pairs(self.sprayValves) do
                          -- Utils.setEmittingState(sprayValve.particleSystems, self.isTurnedOn);
                      -- end
                  -- end
  
                  -- if not self.isTurnedOn and self.spraySoundEnabled then
                      -- stopSample(self.spraySound);
                      -- self.spraySoundEnabled = false;
                  -- end;
              -- end;
              -- self.speedViolationTimer = self.speedViolationMaxTime;
          -- end;
      -- end;
  -- end;
  
  function SprayerNew:setIsSprayerFilling(isFilling, noEventSend)
      SprayerSetIsFillingEvent.sendEvent(self, isFilling, noEventSend)
      if self.isSprayerFilling ~= isFilling then
          self.isSprayerFilling = isFilling;
          if isFilling then
              -- find the first trigger which is activable
              self.sprayerFillTrigger = nil;
              for i=1, table.getn(self.sprayerFillTriggers) do
                  local trigger = self.sprayerFillTriggers[i];
                  if trigger:getIsActivatable(self) then
                      self.sprayerFillTrigger = trigger;
                      break;
                  end;
              end;
          end
      end;
  end;
  
  function SprayerNew:addSprayerFillTrigger(trigger)
      if table.getn(self.sprayerFillTriggers) == 0 then
          g_currentMission:addActivatableObject(self.sprayerFillActivatable);
      end;
      table.insert(self.sprayerFillTriggers, trigger);
  end;
  
  function SprayerNew:removeSprayerFillTrigger(trigger)
      for i=1, table.getn(self.sprayerFillTriggers) do
          if self.sprayerFillTriggers[i] == trigger then
              table.remove(self.sprayerFillTriggers, i);
              break;
          end;
      end;
      if table.getn(self.sprayerFillTriggers) == 0 or trigger == self.sprayerFillTrigger then
          if self.isServer then
              self:setIsSprayerFilling(false);
          end;
          if table.getn(self.sprayerFillTriggers) == 0 then
              g_currentMission:removeActivatableObject(self.sprayerFillActivatable);
          end
      end;
  end;
  
function SprayerNew.findAttachedSprayerTank(currentVehicle, fillType)
      if currentVehicle.isSprayerTank and currentVehicle.getFillLevel ~= nil and currentVehicle.setFillLevel ~= nil and currentVehicle:getFillLevel(fillType) > 0 then
          return currentVehicle;
      end
      for _,implement in pairs(currentVehicle.attachedImplements) do
          if implement.object ~= nil then
              local ret = SprayerNew.findAttachedSprayerTank(implement.object, fillType);
              if ret ~= nil then
                  return ret;
              end
          end
      end
      return nil;
end
  
SprayerFillActivatable = {}
local SprayerFillActivatable_mt = Class(SprayerFillActivatable);
  
function SprayerFillActivatable:new(SprayerNew)
      local self = {};
      setmetatable(self, SprayerFillActivatable_mt);
  
      self.SprayerNew = SprayerNew;
      self.activateText = "unknown";
  
      return self;
end;
  
  
function SprayerFillActivatable:getIsActivatable()
      if self.SprayerNew:getIsActiveForInput() and self.SprayerNew.fillLevel < self.SprayerNew.capacity and self.SprayerNew.isChange then
          -- find the first trigger which is activable
          for i=1, table.getn(self.SprayerNew.sprayerFillTriggers) do
              local trigger = self.SprayerNew.sprayerFillTriggers[i];
              if trigger:getIsActivatable(self.SprayerNew) then
                  self:updateActivateText();
					-- if self.SprayerNew.fillLevel <= 0 then
						-- self.SprayerNew:setIsChange(true);
					-- end;
                  return true;
              end;
          end;
      end
      return false;
end;
  
function SprayerFillActivatable:onActivateObject()
      self.SprayerNew:setIsSprayerFilling(not self.SprayerNew.isSprayerFilling);
      self:updateActivateText();
      g_currentMission:addActivatableObject(self);
end;
  
function SprayerFillActivatable:drawActivate()
      -- TODO draw icon
end;
  
function SprayerFillActivatable:updateActivateText()
      if self.SprayerNew.isSprayerFilling and self.isChange and self.SprayerNew.isChange then
          self.activateText = string.format(g_i18n:getText("stop_refill_OBJECT"), self.SprayerNew.typeDesc);
      else
          self.activateText = string.format(g_i18n:getText("refill_OBJECT"), self.SprayerNew.typeDesc);
      end;
end;