classdef Packet < handle
  properties
    currentPosition %current position on the board
    route %list of positions to visit
    where %index of next destination in `route`
    sent %is this packet being sent by a client?
    velocity
    processingSteps %how long a packet remains at a server
    color
  end

  methods
    %construct a new packet given a list of servers to visit
    function obj = Packet(path, sent);
      obj.where = 1;
      obj.velocity = 7; %hacky constant velocity; TODO: randomize based on dim
      obj.processingSteps = 0;
      obj.sent = sent;

      obj.currentPosition = path(1,:);
      obj.route = path;
      obj.color = rand(1,3);
    end

    %advance the position of the packet by 1 time step
    %
    %The packet moves from its currentPosition to the front elements on its
    %route. If it reaches this position, update the route.
    function new = advance(obj);
      new = {};
      %process the packet- it doesnt move this step
      if obj.processingSteps > 0;
        obj.processingSteps = obj.processingSteps - 1;
        %return this packet
        new{1} = obj;
        return;
      end

      dest = obj.route(obj.where,:);
      dist = dest - obj.currentPosition; %the total distance vector

      %calculate a `velocity' sized initial bearing towards the destination
      delta = obj.velocity * [ ...
        (cosd(obj.currentPosition(1)) * sind(dest(1))) - ...
          (sind(obj.currentPosition(1)) * ...
          cosd(dest(1)) * ...
          cosd(dest(2) - obj.currentPosition(2))), ...
        sind(dest(2) - obj.currentPosition(2)) * cosd(dest(1))];

      %dont overshoot: take the min of the update amount and the total dist
      if obj.velocity > norm(dist);
        obj.currentPosition = obj.currentPosition + dist;
      else;
        obj.currentPosition = obj.currentPosition + delta;
      end

      %packet not at destination; nothing else to do
      if ~Packet.close(obj.currentPosition, dest);
        %return this packet
        new{1} = obj;
        return;
      end

      %arrived at tor router: assume constant processing latency
      obj.processingSteps = 3;
      obj.where = obj.where + 1; %move to the next destination

      %tor routers left to visit
      if obj.where <= size(obj.route, 1);
        %return this packet
        new{1} = obj;
        return;
      end

      %XXX: Number the packets created to avoid going instable
      %forward route complete; go backwards now
      if obj.sent; %the packet was sent by a client
        r = randi([1 3], 1, 1);
        route = flipud(obj.route);
        %server creates 1-3 response Packets
        for i = 1:r;
          p = Packet(route, 0);
          p.processingSteps = 6+i; %webservers are slower
          p.color = obj.color;
          p.velocity = obj.velocity + i;
          new{i} = p;
        end
      else %client responds
        r = randi([0 1], 1, 1);
        route = flipud(obj.route);
        %server creates 0-1 response Packets
        for i = 1:r;
          p = Packet(route, 1);
          p.processingSteps = 3;
          p.color = obj.color;
          p.velocity = obj.velocity + i;
          new{i} = p;
        end
      end
    end
  end

  methods(Static)
    %Given two points, determine if they are sufficiently close to each other
    %in Cartesian space.
    %
    function y = close(a, b);
      y = (norm(a - b) < .01);
    end
  end
end
