


-------------------------------------------------------------------------------
-- World map nodes. Level markers that link to each other.
-------------------------------------------------------------------------------

-- TODO: Move these to their own file.

-- TODO: Need a type of these that allows transitioning between world
--   maps. (Or we could do it the SMB3 way, I guess, and just have the
--   transitions inside small levels..)

WorldMapNode = Entity:createSubClass();
function WorldMapNode:init()

   self.scale = 1;
   self:setGravityInfluence(0);
   self:setSpriteFrame(336, 0, 352, 16);
   self:setSize(16 * self.scale, 16 * self.scale);
   self:setSpriteScale(self.scale);
   self:setBlockable(false);
   self:setForceUpdate(false);

   self:setTexture("lilymapicons.tga");
   setSpriteFromDef(self, 1, 2);

   self.unlockedCheckpoints = { };

   self.editorProperties = {
      levelFinished = false,
      linkOnly = false,
      defaultLevelName = "",
   };

end

-- Assuming there are only two linked nodes, get the linked node that
-- ISN'T the one that's being passed-in.
function WorldMapNode:getOtherLinkedNode(myNode)

   if self.editorProperties.linkOnly then

      local nodes = self:getAllLinkTargets();

      for k, v in pairs(nodes) do
         if not (v == myNode) then
            return v;
         end
      end

   end

   return nil;

end

-- For any node that this node links to, make sure it has a link back
-- to this node.
function WorldMapNode:addDoubleLinks()

   local allNodes = self:getAllLinkTargets();

   for k, v in pairs(allNodes) do
      
      local ent = entitiesByName[v];

      if ent then
         
         ent:addLinkTarget(self:getName());

      end

   end

end

-- Determine if this node is "finished". For normal levels this means
-- the level has been completed. For links, this just recurses to the
-- next link.
function WorldMapNode:isFinishedFrom(startNode)

   if self.editorProperties.linkOnly then

      -- Recurse to the next node in the chain.
      writeConsole("starting at "..startNode.."\n");

      local otherNodeName = self:getOtherLinkedNode(startNode);
      local otherNode = entitiesByName[otherNodeName];
      if otherNode then
         writeConsole("Chaining from "..self:getName().." to "..otherNodeName.."\n");
         return otherNode:isFinishedFrom(self:getName());
      else
         writeConsole("Chain ended abruptly at "..self:getName().."\n");
         return false;
      end
   end

   writeConsole("returning my own value at "..self:getName().."\n");
   return self.editorProperties.levelFinished;

end

function WorldMapNode:save()

   return { };

end

function WorldMapNode:initFromSave(saveData)

end

function WorldMapNode:update()
   
   -- FIXME: We probably don't actually have to do this every
   --   frame. Take it out?
   self:addDoubleLinks();

   -- Apply some SMBW-style coloring for levels. Red = incomplete, Yellow = complete.

   local lightness = math.sin(self:getAge() / 16) / 2 + 0.5;
   lightness = 0.5 + lightness / 2;
      
   if self.editorProperties.linkOnly then

      self:setColor(0, 1, 0);
      self:setEditorVisibleOnly(true);

   else

      if self.editorProperties.levelFinished then

         self:setColor(1, lightness, 0);

      else

         self:setColor(lightness, 0, 0);

      end

   end

end

registerSpawn("WorldMapNode");


-------------------------------------------------------------------------------
-- UI for checkpoint selection.
-------------------------------------------------------------------------------

-- TODO: Switch this over to use new saveAndSwitchLevel() function.
function finishLevelTransition(checkpointName, levelName)

   writeConsole("Going to level "..levelName.." and checkpoint "..checkpointName.."\n");

   fade_fadeBlack = false;
   fade_alphaBlack = 0.99;

   popAndDeleteUIScreen();

   -- Begin (actual) level transition!

   local currentNode = entitiesByName[globalData.worldNodeName];

   saveEntities(globalData.currentLevel, "player", "worldPlayer");

   globalData.currentLevel = levelName;

   loadTiles(levelName..".tiles.rle");
   for i = 0, 6, 1 do
      clearLayer(i, "");
   end
   unnameDeadEntities();
   loadEntities(levelName);

   -- Spawn the player.

   local player = Shooter:create();
   player:setLayer(3);
   local startNode = entitiesByName[checkpointName];
   if startNode then
      player:setPos(startNode:getPos());
   end

   -- Save in the new level.
   
   saveEntities(globalData.currentLevel);
   saveTable("globaldata.xml", globalData);

end

-- Pops up a list of checkpoints.
function popupCheckpointMenu(checkpointList)

	local alignedBox = WidgetAlignedBox:new();
	
	local paddedBox = WidgetPaddedBox:new();
	paddedBox:setBackgroundImage("editordata/np_ff2.ninepatch");
	paddedBox:setPadding(16);
	paddedBox:setMinWidth(384);

    alignedBox:setChild(paddedBox);

    local mainContainer = WidgetContainer:new();
    paddedBox:setChild(mainContainer);
    mainContainer:setNumRows(2);
    mainContainer:setStretchableRow(1);
    
    local infoText = WidgetText:new();
    mainContainer:addChild(infoText);
    infoText:setText("Select a checkpoint");
    infoText:setFontName("games/lily/fonts/arcadeFont.tga");

    local selectionContainer = WidgetContainer:new();
    mainContainer:addChild(selectionContainer);

    -- Sort the list of checkpoints, except for the start point.
    local sortedPairs = {};
    for k, v in pairs(checkpointList) do
       if not (k == "Start") then
          sortedPairs[#sortedPairs + 1] = k;
       end
    end

    table.sort(sortedPairs);

    local i;
    for i = 0, #sortedPairs do

       local k;

       -- Index 0 is where the start goes, always.
       if i == 0 then
          k = "Start";
       else
          k = sortedPairs[i];
       end

       local v = checkpointList[k];

       local button = WidgetButton:new();
       setButtonDefaults(button);
       button:setLabel(v[2]);
       button:setLuaEventHandler("onClick",
                                 "finishLevelTransition(\""..k.."\", \""..(v[1]).."\");");
       selectionContainer:addChild(button);
    end

    selectionContainer:setNumRows(selectionContainer:getNumChildren());
    selectionContainer:setStretchableRow(-1);
    
    pushUIScreen(alignedBox);

    paused = true;

end

-------------------------------------------------------------------------------
-- Player representation on the world map.
-------------------------------------------------------------------------------

WorldMapPlayer = Entity:createSubClass();
function WorldMapPlayer:init()
   --self.scale = 2;
   self.facing = 0;

   self:setTexture("lilymapicons.tga");
   self:setGravityInfluence(0);
   --self:setSpriteFrame(336, 0, 352, 16);
   --self:setSize(16 * self.scale, 16 * self.scale);
   --self:setSpriteScale(self.scale);
   --self:setBlockable(true);

   setSpriteFromDef(self, 1, 1);
   --self:setSpriteOffset(0, -32);

   self:setForceUpdate(true);

   self.currentNodeName = "startNode";
   self.nextNodeName = nil;
   self.walkTime = 0;

   self:setName("worldPlayer");

   self.fadingToLevel = false;

   self:setDepth(1);

   if globalData and globalData.worldNodeName then
      self.currentNodeName = globalData.worldNodeName;
   end

end

function WorldMapPlayer:save()
   return {
      currentNodeName = self.currentNodeName
   };
end

function WorldMapPlayer:initFromSave(saveData)

   self.fadingToLevel = false;

   self.currentNodeName = saveData.currentNodeName;
   self.nextNodeName = nil;
   self.walkTime = 0;

end

function WorldMapPlayer:update()

   if fade_alphaBlack > 0 then

      local currentNode = entitiesByName[self.currentNodeName];

      if fade_alphaBlack >= 1 and fade_fadeBlack then

         --    -- Fully faded out. Begin level transition!

         --    globalData.currentLevel = currentNode.editorProperties.defaultLevelName;
         --    globalData.worldNodeName = currentNode:getName();

         --    loadTiles(currentNode.editorProperties.defaultLevelName..".tiles.gz");
         --    for i = 0, 6, 1 do
         --       clearLayer(i, "");
         --    end
         --    unnameDeadEntities();
         --    loadEntities(currentNode.editorProperties.defaultLevelName);

         --    -- Spawn the player.

         --    local player = Shooter:create();
         --    local startNode = entitiesByName["Start"];
         --    if startNode then
         --       player:setPos(startNode:getPos());
         --    end

         --    -- Save in the new level.
         
         --    saveEntities(globalData.currentLevel);
         --    saveTable("globaldata.xml", globalData);

         --    -- Remove the fade.
         --    fade_fadeBlack = false;

         globalData.worldNodeName = currentNode:getName();
         globalData.worldMapLevel = globalData.currentLevel;
         
         local checkpoints = globalData.worldNodeCheckpoints[self.currentNodeName];
         local displayedCheckpoints = {
            Start = {
               currentNode.editorProperties.defaultLevelName,
               "Start"
            };
         };

         -- Add to or overwrite the default list with unlocked
         -- checkpoints.
         if checkpoints then
            for k, v in pairs(checkpoints) do
               displayedCheckpoints[k] = v;
            end
         end

         writeConsole("Available checkpoints:\n");
         for k, v in pairs(displayedCheckpoints) do
            writeConsole("  "..k.." ("..v[1].." - "..v[2]..")\n");
         end

         popupCheckpointMenu(displayedCheckpoints);

      end

      return;
   end

   -- Get a bunch of information
   local dx, dy = self:getVel();
   local x, y = self:getPos();
   
   if dx < 0 then 
      self.facing = 0;
   elseif dx > 0 then
      self.facing = 1;
   end

   -- Get some input.

   local axis1 = getControlValue(1) - getControlValue(0);
   local axis0 = getControlValue(3) - getControlValue(2);

   local button0 = getControlBoolValue(9);

   -- Figure out a movement direction.

   local moveLeft;
   local moveRight;
   local moveUp;
   local moveDown;
   
   if math.abs(axis0) > 32 then
      if axis0 > 0 then
         moveRight = true;
      else
         moveLeft = true;
      end
   end
   
   if math.abs(axis1) > 32 then
      if axis1 < 0 then
         moveDown = true;
      else
         moveUp = true;
      end
   end
   
   -- Update location.
   
   local currentNode = entitiesByName[self.currentNodeName];
   local dontMoveAgain = false;
   local walkSpeed = 16;
   local nextNode = nil;

   if self.nextNodeName then
      nextNode = entitiesByName[self.nextNodeName];
   end

   if nextNode then

      local dist;
      local nx, ny = nextNode:getPos();
      local cx, cy = currentNode:getPos();

      -- Get a vector difference.

      local diffx, diffy;
      diffx = nx - cx;
      diffy = ny - cy;

      dist = math.sqrt(diffx * diffx + diffy * diffy);
      
      -- Have we made it yet?

      if self.walkTime >= dist then

         local lastNode = self.currentNodeName;

         self.currentNodeName = self.nextNodeName;

         -- In case this is just a linking node, follow it to the
         -- next one.
         self.nextNodeName = nextNode:getOtherLinkedNode(lastNode);

         self.walkTime = 0;

         self:setPos(nx, ny);

         dontMoveAgain = false;

      else

         -- Normalize it.

         diffx = diffx / dist;
         diffy = diffy / dist;

         -- Get our final coordinates. (Current node's position +
         -- difference vector * walktime.)
         
         local fx, fy;

         fx = diffx * self.walkTime + cx;
         fy = diffy * self.walkTime + cy;
         
         self:setPos(fx, fy);

         -- Walk a little bit.

         self.walkTime = self.walkTime + walkSpeed;

      end

   else
      
      -- Just stand at the current node.

      self:setPos(currentNode:getPos());

   end


   currentNode = entitiesByName[self.currentNodeName];

   if ( moveUp or moveDown or moveLeft or moveRight ) and ( not self.nextNodeName ) and ( not dontMoveAgain ) then

      -- Handle movement to another node.

      -- Start by getting the linked nodes that are furthest in each
      -- direction.
      
      local allNodes = currentNode:getAllLinkTargets();

      local levelFinished = currentNode.editorProperties.levelFinished;

      local mostLeft  = { nil, 0 };
      local mostRight = { nil, 0 };
      local mostUp    = { nil, 0 };
      local mostDown  = { nil, 0 };

      local myx, myy = currentNode:getPos();

      for k, v in pairs(allNodes) do
         
         local node = entitiesByName[v];
         local x, y = node:getPos();
         x = x - myx;
         y = y - myy;

         if x > mostRight[2] then
            mostRight[1] = node;
            mostRight[2] = x;
         end

         if x < mostLeft[2] then
            mostLeft[1] = node;
            mostLeft[2] = x;
         end

         if y < mostUp[2] then
            mostUp[1] = node;
            mostUp[2] = y;
         end

         if y > mostDown[2] then
            mostDown[1] = node;
            mostDown[2] = y;
         end

      end

      -- Pick one as the next node depending on the direction the
      -- player pushed.

      if moveDown and mostDown[1] then
         self.nextNodeName = mostDown[1]:getName();
         --writeConsole("Moving down. "..self.currentNodeName.." to "..self.nextNodeName.."\n");
      end

      if moveUp and mostUp[1] then
         self.nextNodeName = mostUp[1]:getName();
         --writeConsole("Moving up. "..self.currentNodeName.." to "..self.nextNodeName.."\n");
      end

      if moveLeft and mostLeft[1] then
         self.nextNodeName = mostLeft[1]:getName();
         --writeConsole("Moving left. "..self.currentNodeName.." to "..self.nextNodeName.."\n");
      end

      if moveRight and mostRight[1] then
         self.nextNodeName = mostRight[1]:getName();
         --writeConsole("Moving right. "..self.currentNodeName.." to "..self.nextNodeName.."\n");
      end

      -- Invalidate whatever choice was made if this level hasn't
      -- been finished and that level has not been finished.

      local nextNode = entitiesByName[self.nextNodeName];
      local levelPass = false;
      if nextNode then

         if nextNode.editorProperties then

            if nextNode.isFinishedFrom then
               -- Have no idea what we're headed for. Better just let
               -- the player go. Teleporter, maybe.
               levelPass = true;
            else
            
               --if nextNode.editorProperties.levelFinished then
               if nextNode:isFinishedFrom(self.currentNodeName) then
                  -- Destination level has been finished. It's okay to
                  -- go to it.
                  levelPass = true;
               end

            end
         end
      end

      if levelFinished then
         levelPass = true;
      end

      if not levelPass then
         writeConsole("Can't go to that level.\n");
         self.nextNodeName = nil;
      end

   end


   if button0 and self.currentNodeName and not self.nextNodeName then
      
      -- Start the level!

      if currentNode.editorProperties.defaultLevelName then
         fade_fadeBlack = true;
      end

   end

end

registerSpawn("WorldMapPlayer");



