
Chaser = Actor:createSubClass();
registerSpawn("Chaser");

-- Chaser AI states:

local chaserAI_wander = 0;
local chaserAI_chase = 1;

function Chaser:init()

   Actor.init(self);

   self.nextThink = 0;

   self.aiState = chaserAI_wander;

   self:setBlockable(true);

end

function doLine(x1, y1, x2, y2, cb)

   -- Basically copied from Wikipedia.
   -- http://en.wikipedia.org/wiki/Bresenham's_line_algorithm

   local tmp;

   local steep = math.abs(y2 - y1) > math.abs(x2 - x1);

   if steep then

      tmp = x1;
      x1 = y1;
      y1 = tmp;

      tmp = x2;
      x2 = y2;
      y2 = tmp;

   end

   if x1 > x2 then

      tmp = x1;
      x1 = x2;
      x2 = tmp;

      tmp = y1;
      y1 = y2;
      y2 = tmp;      

   end

   local dx = math.floor(x2 - x1);
   local dy = math.floor(math.abs(y2 - y1));

   local err = 0;
   local deltaErr = dy / dx;

   local ystep;

   local y = y1;
   local x;

   if y1 < y2 then
      ystep = 1;
   else
      ystep = -1;
   end

   for x = x1, x2 do
      
      if steep then
         if cb(y, x) then
            return true;
         end
      else
         if cb(x, y) then
            return true;
         end
      end

      err = err + deltaErr;
      if err > 0.5 then
         y = y + ystep;
         err = err - 1;
      end

   end

   return false;

end

function checkForBlock(x, y)
   
   local tile = getTile(x, y, 3);

   -- TODO: Add specific block numbers here for exceptions?
   if tile > -1 then
      return true;
   end

   return false;

end

-- Check line of site between two Entities
function entsLos(ent1, ent2) 

   local x1, y1 = ent1:getPos();
   local x2, y2 = ent2:getPos();

   local tx1, ty1 = worldSpaceToTileSpace(x1, y1, 3);
   local tx2, ty2 = worldSpaceToTileSpace(x2, y2, 3);

   local notGotLos = doLine(tx1, ty1, tx2, ty2, checkForBlock);

   return not notGotLos;

end


function Chaser:update()

   Actor.update(self);

   self.nextThink = self.nextThink - 1;

   if self.nextThink <= 0 then

      local x, y = self:getPos();
      local visRange = 512;
      local maxSpeed = 8;

      if self.aiState == chaserAI_wander then

         -- Wandering aimlessly state.
         
         self.nextThink = math.random() * 60;

         self:setVel(
            math.random() * maxSpeed * 2 - maxSpeed,
            math.random() * maxSpeed * 2 - maxSpeed);

         -- Check for LOS to something of another faction, if visible,
         -- switch to chase state.
         
         local nearEnts = getEntsTouchingBox(
            self:getLayer(),
            x - visRange, y + visRange,
            x + visRange, y - visRange);

         local n, entName;
         for n,entName in pairs(nearEnts) do
            local ent = entitiesByName[entName];
            if ent and ent.isActor then
               if ent.editorProperties and not (ent.editorProperties.faction == self.editorProperties.faction) then

                  -- Found an enemy!
                  if entsLos(self, ent) then
                     local ex, ey = ent:getPos();
                     self.gotoPoint = { x = ex, y = ey };
                     self.gotoEnt = entName;
                     self.aiState = chaserAI_chase;
                     writeConsole("Found an enemy!\n");
                  end

               end
            end
         end

         -- TODO: Check for LOS to an a same-faction. If visible and
         -- they're in chaserAI_chase, switch self to chaserAI_chase
         -- going after the ally.

      end

      if self.aiState == chaserAI_chase then
         
         -- Chasing after something.

         -- Go to our goal.
         local dx, dy;
         dx = self.gotoPoint.x - x;
         dy = self.gotoPoint.y - y;

         local dist = math.sqrt(dx * dx + dy * dy);
         dx = (dx / dist) * maxSpeed;
         dy = (dy / dist) * maxSpeed;
         self:setVel(dx, dy);

         local giveUpThreshold = 10;

         if not self.giveUp then
            self.giveUp = 0;
         else
            self.giveUp = self.giveUp + 1;
         end

         -- Check our target. If it's visible, change goal to
         -- its current position.
         if self.gotoEnt then
            local targEnt = entitiesByName[self.gotoEnt];
            if targEnt then

               if entsLos(self, targEnt) then

                  local tx, ty = targEnt:getPos();
                  self.gotoPoint = { x = tx, y = ty };
                  self.giveUp = 0;

                  -- Take a shot at the enemy.
                  shootBullet(self, tx, ty);

               end
            end
         end

         if self.giveUp > giveUpThreshold then
            self.giveUp = nil;
            self.gotoPoint = nil;
            self.gotoEnt = nil;
            self.aiState = chaserAI_wander;
         end

         -- TODO: If target is ally, keep an eye out for actual valid
         -- targets.

         -- TODO: If enough time has passed and we can't see the
         -- target, or the target is an ally, return to
         -- chaserAI_wander.

         self.nextThink = math.random() * 10;

      end

   end

end

function Chaser:collidedWithWorld()
   
   --Actor.collidedWithWorld(self);

   self.nextThink = 0;

end

