--
-- road is made up of segments in a chain
--
-- note about scale: 3.62m = 50px (lane width) -> 1m = 13.8 pixels (make it 15)
--

function road_ctor (self)

   self.surface = Surface -- 1 == pavement, 2 == gravel, 3 == dirt, 4 == broken pavement, etc.
   self.roadstream = nil
   self.segs = {}
   self.segs_offset = 0 -- y offset of segments, in pixels
   self.distance_m = 0 -- distance travelled, in meters

   -- how many cars do we *want* for traffic
   self.want_num_cars = 0
end

road = class (road_ctor)

function road:init (Roadstream)

   self.roadstream = Roadstream

   self.segs = {}
   for i = 1, 25, 1 do

      table.insert (self.segs, self.roadstream:next_seg ())
   end

   cur_hud:set_notice (self.roadstream.name)
end

function road:advance (Meters)

   self.distance_m = self.distance_m + Meters

   local Pixels = m2pix (Meters)
   self.segs_offset = self.segs_offset + Pixels

   while (self.segs_offset > 1600) do

      self.segs_offset = self.segs_offset - 400
      table.remove (self.segs, 1)
      table.insert (self.segs, self.roadstream:next_seg ())
   end
end

function road:get_roadseg (DistanceM, SegIDOffset)

   local OffsetM = pix2m (self.segs_offset)
   local DistanceD = m2pix (DistanceM - self.distance_m + OffsetM)
   local SegID = math.floor (DistanceD / 400) + 1

   if (SegID + SegIDOffset >= sizeof (self.segs)) then return nil
   end

   return self.segs[SegID + SegIDOffset]
end

function road:get_cur_roadseg (DistanceM)

   return self:get_roadseg (DistanceM, 0)
end

function road:get_next_roadseg (DistanceM)

   return self:get_roadseg (DistanceM, 1)
end

function road:get_max_lane (DistanceM)

   local NextSeg = self:get_next_roadseg (DistanceM)
   if (not NextSeg) then return nil
   end
   return NextSeg.lanes
end

function road:get_lane_x (DistanceM, LaneID)

   -- first, find the segments they are between based on distance travelled
   local OffsetM = pix2m (self.segs_offset)
   local DistanceD = m2pix (DistanceM - self.distance_m + OffsetM)
   local SegID = math.floor (DistanceD / 400) + 1
   local SegD = (SegID - 1) * 400

   -- return nil if the distance m is off the map
   if (SegID + 1 >= sizeof (self.segs)) then return nil
   end

   -- figure out where we are, and how far along
   local Seg = self.segs[SegID]
   local NextSeg = self.segs[SegID + 1]
   if (not Seg or not NextSeg) then return nil
   end

   local SegAlpha = (DistanceD - ((SegID - 1) * 400)) / 400
   local SegX = self:get_seg_x (Seg, LaneID)
   local NextSegX = self:get_seg_x (NextSeg, LaneID)
   return NextSegX * SegAlpha + SegX * (1 - SegAlpha)
end

function road:get_y (DistanceM)

   local DistanceD = m2pix (self.distance_m - DistanceM) + 400
   return DistanceD
end

function road:get_seg_x (Seg, LaneID)

   return (Seg.median / 2) * 50 + (LaneID - 1) * 50 + 25
end

function road:get_message (DistanceM)

   -- check all segments for messages that appear; messages are in range if the given DistanceM is between
   -- the message start range and the message segment itself; we choose an active message with the latest start
   -- range to handle overlap; returns nil if there is no message
   local OffsetM = pix2m (self.segs_offset)
   local SegLenM = pix2m (400)
   local LatestMessage = nil
   local LatestStartM = nil
   for ID, Seg in pairs (self.segs) do

      if (Seg.message_flag) then

         local SegDistanceM = (ID - 1) * SegLenM + self.distance_m - OffsetM - DistanceM
         local StartM = SegDistanceM - Seg.message_range_m
         if (StartM < 0.0 and (not LatestStartM or StartM > LatestStartM) and SegDistanceM > 0) then

            LatestMessage = Seg.message_text
            LatestStartM = StartM
         end
      end
   end

   return LatestMessage
end

function road:render (R)

   local RList = {}
   local CurY = self.segs_offset + 400
   local TerrainSwatches = { "data/swatch/desert.png", "data/swatch/grass.png", "data/swatch/mountains.png" }
   local TerrainSwatch = TerrainSwatches[self.roadstream.terrain_type]
   for i = 1, sizeof (self.segs) - 1, 1 do

      local NextY = CurY - 400
      table.insert (RList, R:zoffs (0.1))
      table.insert (RList, R:terrain (NextY, 400, 400, TerrainSwatch, self.segs[i].terrain_seed))
      table.insert (RList, R:zoffs (-0.1))
      table.insert (RList, self:render_joint (CurY, self.segs[i], NextY, self.segs[i + 1], R))
      CurY = NextY
   end
   return R:union (RList)
end

function road:render_joint (Y1, Seg1, Y2, Seg2, R)

   -- lane rendering works fairly simply; we have an "outer line" which is a boundary for the lane; it always
   -- goes from source X to dest X, based on how many lanes there are; then we have "dotted lanes", which we
   -- render based on Seg1 only and does not slant at all; then of course, the median

   local RList = {}

   -- if we're an overpass, then draw the crossing highway
   if (Seg1.overpass_flag) then

      local OSeg = roadseg (Seg1.overpass_lanes, Seg1.overpass_rlanes, Seg1.overpass_median)
      table.insert (RList, R:zoffs (0.05))
      table.insert (RList, R:translate (0, Y1, R:rotate (math.pi * 0.5, self:render_joint (-400, OSeg, 400, OSeg, R))))
      table.insert (RList, R:zoffs (-0.05))
   end

   -- draw the reverse lane boundary on the left
   local X1 = (Seg1.rlanes + Seg1.median / 2) * -50
   local X2 = (Seg2.rlanes + Seg2.median / 2) * -50
   local AX1 = (Seg1.lanes + Seg1.median / 2) * 50
   local AX2 = (Seg2.lanes + Seg2.median / 2) * 50
   table.insert (RList, R:rgba (0, 0, 0, 0.5))
   table.insert (RList, R:vwedge (X1 - 75, AX1 + 75, X2 - 75, AX2 + 75, Y1, Y2))
   table.insert (RList, R:rgba (0, 0, 0, 1))
   table.insert (RList, R:vwedge (X1, AX1, X2, AX2, Y1, Y2))
   table.insert (RList, R:rgba (1, 1, 1, 1))
   table.insert (RList, R:line (X1, Y1, X2, Y2, 5))

   -- draw each additional reverse lane
   local MedianDiff = (Seg2.median - Seg1.median) * 50 / 2
   local CurX = X1
   for CurRLane = 1, Seg1.rlanes - 1, 1 do

      CurX = CurX + 50
      table.insert (RList, R:dashline (CurX, Y1, CurX - MedianDiff, Y2, 2))
   end

   -- draw the median; this is done with lines that are a little "in" from center
   if (Seg1.median > 0 or Seg2.median > 0) then

      -- compute the boundary of this median section
      local MX1_1 = (Seg1.median / 2) * -50
      local MX1_2 = (Seg1.median / 2) * 50
      local MX2_1 = (Seg2.median / 2) * -50
      local MX2_2 = (Seg2.median / 2) * 50
      table.insert (RList, R:rgba (0.25, 0.25, 0.25, 1))
      table.insert (RList, R:vwedge (MX1_1, MX1_2, MX2_1, MX2_2, Y1, Y2))
      table.insert (RList, R:rgba (1, 1, 1, 1))
      table.insert (RList, R:vwedge_border (MX1_1, MX1_2, MX2_1, MX2_2, Y1, Y2, 10, 5))
   else

      -- centerline
      table.insert (RList, R:rgba (0.5, 0.5, 0.25, 1))
      table.insert (RList, R:line (-3, Y1, -3, Y2, 4))
      table.insert (RList, R:line (3, Y1, 3, Y2, 4))
      table.insert (RList, R:rgba (1, 1, 1, 1))
   end

   -- draw the forward lane boundary on the right
   X0 = (Seg1.lanes - 1 + Seg1.median / 2) * 50
   X1 = (Seg1.lanes + Seg1.median / 2) * 50
   X2 = (Seg2.lanes + Seg2.median / 2) * 50
   if (Seg1.exit_flag) then

      -- draw an exit; this consists of a circular arc going off the road (angle for now), and a
      -- boundary for the new "inner" lane (exit is supposed to peel off a lane)
      local ExitR = Seg1.exit_radius
      table.insert (RList, R:line (X2, Y1, X2, Y2, 5)) -- inside lane continuation
      table.insert (RList, R:circarc (X1 + ExitR, Y1, ExitR + 50, math.pi * 1.0, math.pi * 1.5, 5)) -- inner angle line
      table.insert (RList, R:circarc (X1 + ExitR, Y1, ExitR, math.pi * 1.0, math.pi * 1.5, 5)) -- outer angle line

   elseif (Seg1.entrance_flag) then

      local EntranceR = Seg1.entrance_radius
      table.insert (RList, R:line (X0, Y1, X0, Y2, 5)) -- inside continuation
      table.insert (RList, R:circarc (X1 + EntranceR, Y2, EntranceR + 50, math.pi * 0.5, math.pi * 1.0, 5)) -- inner angle line
      table.insert (RList, R:circarc (X1 + EntranceR, Y2, EntranceR, math.pi * 0.5, math.pi * 1.0, 5)) -- outer angle line

   elseif (Seg2.entrance_flag) then

      table.insert (RList, R:line (X1, Y1, X1, Y2, 5)) -- an inside line

   else

      table.insert (RList, R:line (X1, Y1, X2, Y2, 5))
   end

   -- draw additional lanes
   CurX = X1
   for CurLane = Seg1.lanes, 1, -1 do

      CurX = CurX - 50
      if (CurLane ~= 1) then table.insert (RList, R:dashline (CurX, Y1, CurX + MedianDiff, Y2, 2))
      end
   end

   return R:union (RList)
end

