-- common_click.inc
--
-- Common utility functions for clicking or other mouse actions
--
-- DO NOT INCLUDE THIS FILE! Include common.inc instead
--

-------------------------------------------------------------------------------
-- drag(sourceX, sourceY, destX, destY, timeout)
--
-- Drag the mouse from (sourceX, sourceY) to (destX, destY)
-------------------------------------------------------------------------------

function drag(sourceX, sourceY, destX, destY)
  if not sourceX or not sourceY or not destX or not destY then
    error("Incorrect number of arguments for drag()");
  end
  srSetMousePos(sourceX, sourceY);  
  srMouseDown(sourceX, sourceY, 0);
  srSetMousePos(destX, destY);
  lsSleep(60);
  srMouseUp(destX, destY, 0);
end

-------------------------------------------------------------------------------
-- safeBegin()
--
-- Call this just before a click or a drag to make sure the user isn't
-- moving the mouse or clicking it. Reduces the chances of interference.
-------------------------------------------------------------------------------

function safeBegin()
  local oldX = 0;
  local oldY = 0;
  oldX, oldY = srMousePos();
  local at_rest = false;
  local loopCount = 0;
  while not at_rest do
    lsSleep(tick_time);
    local currentX = 0;
    local currentY = 0;
    currentX, currentY = srMousePos();
    at_rest = (currentX == oldX and currentY == oldY);
    oldX = currentX;
    oldY = currentY;
    loopCount = loopCount + 1;
    if loopCount > 200 then
      error("Error: The mouse keeps moving");
    end
  end
  srMouseUp(oldX, oldY);
end

-------------------------------------------------------------------------------
-- safeClick(x, y, rightClick)
--
-- Click the mouse without moving it.
-------------------------------------------------------------------------------

function safeClick(x, y, rightClick)
  if not x or not y then
    error("Incorrect number of arguments for safeClick()");
  end
  safeBegin();
  srClickMouseNoMove(x, y, rightClick);
end

-------------------------------------------------------------------------------
-- safeDrag(sourceX, sourceY, destX, destY, timeout)
-------------------------------------------------------------------------------

function safeDrag(sourceX, sourceY, destX, destY)
  if not sourceX or not sourceY or not destX or not destY then
    error("Incorrect number of arguments for safeDrag()");
  end
  safeBegin();
  drag(sourceX, sourceY, destX, destY, timeout);
end

-------------------------------------------------------------------------------
-- safeScroll(x, y, steps)
--
-- Move the mouse to x,y and scroll the wheel
--
-- x, y -- Point to click
-- steps -- positive value rolls the wheel forward (away from the user)
--       -- negative value rolls the wheel backward (toward the user)
-- safeScroll(100,100,-12)
-------------------------------------------------------------------------------

function safeScroll(x, y, steps)
  --This version includes required changes to the srMouseWheel
  lsRequireVersion(2,73);

  if not x or not y or not steps then
    error("Incorrect number of arguments for safeScroll()");
  end
  safeBegin();
  srMouseWheel(x, y, steps);
end

-------------------------------------------------------------------------------
-- clickPoint(point, offsetX, offsetY, rightClick)
--
-- Click one point with an offset
--
-- offsetX, offsetY (optional) -- distance from each point to click (default 5)
-- rightClick (optional) -- make each click a right click (default false)
-------------------------------------------------------------------------------

function clickPoint(point, offsetX, offsetY, rightClick)
  if not point then
    error("Incorrect number of arguments for clickPoint()");
  end
  if not offsetX then
    offsetX = 5;
  end
  if not offsetY then
    offsetY = 5;
  end

  safeClick(point[0]+offsetX, point[1]+offsetY, rightClick);
  lsSleep(click_delay);
end

-------------------------------------------------------------------------------
-- clickXY(x, y, offsetX, offsetY, rightClick)
--
-- Click one point with an offset
--
-- x, y -- Point to click
-- offsetX, offsetY (optional) -- distance from each point to click (default 5)
-- rightClick (optional) -- make each click a right click (default false)
-------------------------------------------------------------------------------

function clickXY(x, y, offsetX, offsetY, rightClick)
  if not x then
    error("Incorrect number of arguments for clickPoint()");
  end
  if not y then
    error("Incorrect number of arguments for clickPoint()");
  end
  if not offsetX then
    offsetX = 5;
  end
  if not offsetY then
    offsetY = 5;
  end
  clickPoint(makePoint(x,y),offsetX,offsetY,rightClick);
end

-------------------------------------------------------------------------------
-- clickAllPoints(points, offsetX, offsetY, rightClick)
--
-- Click all points in a list.
--
-- offsetX, offsetY (optional) -- distance from each point to click (default 5)
-- rightClick (optional) -- make each click a right click (default false)
-------------------------------------------------------------------------------

function clickAllPoints(points, offsetX, offsetY, rightClick)
  if not points then
    error("Incorrect number of arguments for clickAllPoints()");
  end
  if not offsetX then
    offsetX = 5;
  end
  if not offsetY then
    offsetY = 5;
  end

  for i=1, #points  do
    if click_delay > 0 and #points > 1 then
      statusScreen("Clicking " .. #points .. " button(s)...");
    end
    safeClick(points[i][0]+offsetX, points[i][1]+offsetY, rightClick);
    lsSleep(click_delay);
  end
  if click_delay > 0 and #points > 1 then
    statusScreen("Done clicking (" .. #points .. " clicks).");
  end
  lsSleep(click_delay);
end

-------------------------------------------------------------------------------
-- clickAllImages(image_name, offsetX, offsetY, rightClick, tol)
--
-- Find all positions matching a given image and click them.
--
-- image_name -- path and filename used to fetch the image
-- offsetX, offsetY (optional) -- distance from found position to
--                                click (default 5)
-- rightClick (optional) -- click the right mouse button (default false)
-- tol (optional) -- tolerance value used for comparison
--
-- returns the number of buttons clicked
-------------------------------------------------------------------------------

function clickAllImages(image_name, offsetX, offsetY, rightClick, tol)
  if not image_name then
    error("Incorrect number of arguments for clickAllImages()");
  end

  srReadScreen();
  local buttons = findAllImages(image_name, nil, tol);
  clickAllPoints(buttons, offsetX, offsetY, rightClick);
  return #buttons;
end

function clickAllText(text, offsetX, offsetY, rightClick)
  if not text then
    error("Incorrect number of arguments for clickAllText()");
  end

  if not offsetX then
    offsetX = 10;
  end

  srReadScreen();
  local buttons = findAllText(text);
  clickAllPoints(buttons, offsetX, offsetY, rightClick);
  return #buttons;
end

-------------------------------------------------------------------------------
-- drawWater(skipTimer)
--
-- Attempts to draw water from an aqueduct, storage barrel, or pond.
--
-- Use drawWater(1) to avoid the 3500 sleepWithStatus message 'Waiting for water pickup animation...'
-- Use drawWater() to wait for animation to finish, before continuing
-- Returns true on success
-------------------------------------------------------------------------------

function drawWater(skipTimer)
  local result = false;
  srReadScreen();

  -- First, check for storage barrel
  local barrel = findText("Storage Barrel")
  if barrel then
    sleepWithStatus(200, "Refreshing barrel...", nil, 0.7);
    safeClick(barrel[0], barrel[1]);
    srReadScreen();
    local fill = findText("Draw Water");
      if fill then
        sleepWithStatus(200, "Refilling water...", nil, 0.7);
        safeClick(fill[0] + 5, fill[1]);
        local max = waitForImage("max.png", 3000, "Waiting for Max button");
          if max then
            safeClick(max[0]+5, max[1]+5);
            result = true;
          end
      end
  end

  -- Second, check for water well
  srReadScreen();
  if not result then
    local waterWell = findText("Water Well")
    if waterWell then
      sleepWithStatus(200, "Refreshing Water Well...", nil, 0.7);
      safeClick(waterWell[0], waterWell[1]);
      srReadScreen();
      local fill = findText("Fill Containers");
        if fill then
          sleepWithStatus(200, "Refilling water...", nil, 0.7);
          safeClick(fill[0] + 5, fill[1]);
          local max = waitForImage("max.png", 3000, "Waiting for Max button");
            if max then
              safeClick(max[0]+5, max[1]+5);
              result = true;
            end
        end
    end
  end  

  -- Third, check for aqueduct
  srReadScreen();
  if not result then
    local aqueduct = findText("This is [A-Za-z]+ Aqueduct", nil, REGEX)
    if aqueduct then
      sleepWithStatus(200, "Refreshing aqueduct...", nil, 0.7);
      safeClick(aqueduct[0], aqueduct[1]);
      srReadScreen();
      local fill = findText("Fill all");
        if fill then
          sleepWithStatus(200, "Refilling water...", nil, 0.7);
          safeClick(fill[0] + 5, fill[1]);
          local max = waitForImage("max.png", 3000, "Waiting for Max button");
            if max then
              safeClick(max[0]+5, max[1]+5);
              result = true;
            end
        end
    end
  end

  -- Last, check for nearby pond
  srReadScreen();
  if not result then
    local water = srFindImage("water.png", 5000);
    --local watersmall = srFindImage("iconWaterJugSmallIcon.png", 1);

    --if (water or watersmall) then
    if (water) then
    sleepWithStatus(200, "Refilling water...", nil, 0.7);
	if water then
        safeClick(water[0]+3, water[1]-5);
	else
        safeClick(watersmall[0]+3, watersmall[1]-5);
	end
      local max = waitForImage("max.png", 3000, "Waiting for Max button");
      if max then
        safeClick(max[0]+5, max[1]+5);
      if not skipTimer then
        sleepWithStatus(3500, "Waiting for water pickup animation...", nil, 0.7, "Please standby");
      end
        result = true;
      end
    end
  end
  if result == true then
    lsSleep(60)
    srReadScreen();
    local OK = srFindImage("OK.png")
    if OK then
      srClickMouseNoMove(ok[0]+5,ok[1],1);
    end
  end
  return result;
end

------------------------------------------------------------------------------
-- clickText(parse, safe, offsetX, offsetY)
--
-- convenient wrapper to click on text 20, 7 pixels in from where the top
-- left of the parse object
-- parse - parse object which we're clicking
-- safe - Set to false if you want to do an unsafe click
-- always returns nil
------------------------------------------------------------------------------

function clickText(parse, safe, offsetX, offsetY)
  if not offsetX then
    offsetX = 20;
  end
  if not offsetY then
    offsetY = 7;
  end
  if parse then
    if safe and safe ~= false then
      safeClick(parse[0] + offsetX, parse[1] + offsetY, 0);
    else
      srClickMouseNoMove(parse[0] + offsetX, parse[1] + offsetY);
      lsSleep(per_click_delay);
    end
  end
end

-------------------------------------------------------------------------------
-- stash()
--
-- Attempts to stash an item in a storage container.  If a storage container's
-- menu is pinned that will be used, otherwise a feeble attempt will be made to
-- find a chest, warehouse, or tent by color
--
-- types - An array of strings to search for in the stash menu
-- x, y (optional) - The location on the screen of a known storage container
--
--
-- Returns true on success
-------------------------------------------------------------------------------

warehouse_color = 1412899327;
chest_color = 2036219647;
tent_color = 1399546879;

function stash(types, x, y)
  if(types == nil or #types < 1) then
    return false;
  end
  srReadScreen();
  local leaveMenuPinned = false;
  local pos;
  if((not x == nil) and (not y == nil)) then
    pos = pinStorageMenu(x,y);
  end
  if(not pos) then
    pos = findText("Stash");
    if(pos) then
      leaveMenuPinned = true;
    end
  end
  if(not pos) then
    pos = findAndPinContainer();
  end
  if(not pos) then
    lsPrintln("Unable to find the storage container or its menu");
    return false;
  end

  local stashPos = findText("Stash");
  if(not stashPos) then
    lsPrintln("stash() could not find the Stash menu");
    if(not leaveMenuPinned) then
      unpinStorageMenu();
    end
    return false;
  end
  stashPos[0] = stashPos[0] + 15;
  stashPos[1] = stashPos[1] + 3;
  local stashMenuOnScreen = false;
  local i;
  for i=1,#types do
    checkBreak();
    if(not stashMenuOnScreen) then
      safeClick(stashPos[0],stashPos[1]);
      lsSleep(click_delay*3);
      srReadScreen();
      stashMenuOnScreen = true;
    end
    local pos = findText(types[i]);
    if(pos) then
      safeClick(pos[0]+15,pos[1]+3);
      stashMenuOnScreen = false;
      lsSleep(click_delay*3);
      srReadScreen();
      local pos = findText("Stash All");
      if(pos) then
        safeClick(pos[0]+15,pos[1]+3);
      else
        if(not clickMax()) then
          lsSleep(500);
          srReadScreen();
          clickMax();
        end
      end
      lsPrintln(types[i] .. " stashed");
      lsSleep(click_delay*3);
      srReadScreen();
    end
  end
  if(stashMenuOnScreen) then
    safeClick(stashPos[0]-30,stashPos[1]);
  end
  if(leaveMenuPinned) then
    -- Click the menu to update the numbers in the title so that
    -- anyone viewing the screen can see how much space is left
    -- in the storage container
    pos = findText("full");
    if(pos) then
      safeClick(pos[0]+15,pos[1]+3);
    end
  else
    unpinStorageMenu();
  end
  return true;
end

function pinStorageMenu(x,y)
  centerMouse();
  safeClick(x,y,false);
  lsSleep(click_delay*3);
  srReadScreen();
  local pos = findText("Stash");
  if(pos) then
    local pos2 = findText("full");
    if(pos2) then
      safeClick(pos2[0],pos2[1],true);
      return pos;
    end
  end
  return nil;
end

function unpinStorageMenu()
  local pos = findText("full");
  if(pos) then
    safeClick(pos[0]+15,pos[1]+3,true);
  end
end

function findAndPinContainer()
  centerMouse();
  local xyWindowSize = srGetWindowSize();
  local mid = {};
  mid[0] = xyWindowSize[0] / 2;
  mid[1] = xyWindowSize[1] / 2;
  local storagePos = nil;
  local maxDelta = math.max(xyWindowSize[0] / 2, xyWindowSize[1] / 2);
  local delta;
  for delta = 1, maxDelta, 2 do
    checkBreak();
    local dx;
    for dx = (delta * -1), delta, 2 do
      if(mid[0]+dx >= 0 and mid[0]+dx < xyWindowSize[0] and mid[1] > delta) then
        if(checkStoragePixel(mid[0]+dx,mid[1]+delta)) then
          local pos = pinStorageMenu(mid[0]+dx,mid[1]+delta);
          if(pos) then
            return pos;
          end
        end
        if(checkStoragePixel(mid[0]+dx,mid[1]-delta)) then
          local pos = pinStorageMenu(mid[0]+dx,mid[1]-delta);
          if(pos) then
            return pos;
          end
        end
      end
    end
    local dy;
    for dy = (delta * -1), delta, 3 do
      if(mid[1]+dy >= 0 and mid[1]+dy < xyWindowSize[1] and mid[0] > delta) then
        if(checkStoragePixel(mid[0]+delta,mid[1]+dy)) then
          local pos = pinStorageMenu(mid[0]+delta,mid[1]+dy);
          if(pos) then
            return pos;
          end
        end
        if(checkStoragePixel(mid[0]-delta,mid[1]+dy)) then
          local pos = pinStorageMenu(mid[0]+delta,mid[1]+dy);
          if(pos) then
            return pos;
          end
        end
      end
    end
  end
  lsPrintln("Unable to find the storage container");
  return false;
end

function checkStoragePixel(x, y)
  if(pixelBlockCheck(x, y, tent_color, 10, 8, 2)) then
    return true;
  end
  if(pixelBlockCheck(x, y, warehouse_color, 10, 8, 2)) then
    return true;
  end
  if(pixelBlockCheck(x, y, chest_color, 10, 8, 2)) then
    return true;
  end
end



-------------------------------------------------------------------------------
-- clickMax()
--
-- Clicks the Max button
-------------------------------------------------------------------------------

function clickMax()
  local count;
  for count=0, 10 do
    local pos = srFindImage("max.png", 5000);
    if pos then
      safeClick(pos[0]+5, pos[1]+5);
      return true;
    end
    lsSleep(click_delay);
    srReadScreen();
  end
  return false;
end


-------------------------------------------------------------------------------
-- centerMouse()
--
-- Moves the mouse to the center of the screen
-------------------------------------------------------------------------------

function centerMouse()
  local xyWindowSize = srGetWindowSize();
  local mid = {};
  mid[0] = xyWindowSize[0] / 2;
  mid[1] = xyWindowSize[1] / 2;
  srSetMousePos(mid[0],mid[1]);
end

-------------------------------------------------------------------------------
-- loadSimpleResource()
--
-- Load a single resource into a building via the window provided
--
-- window - window for the building we wish to load the resource into.
-- clickString - String to click, within the window provided
-- loadVolume - Volume of the material to load
--
-------------------------------------------------------------------------------

function loadSimpleResource(window, clickString, loadVolume)
  clickText(findText(clickString,window));
  waitForImage("max.png");
  srKeyEvent(loadVolume);
  
  srReadScreen();
  local addOK = srFindImage("ok2.png")
  if addOK then
    srClickMouseNoMove(addOK[0] + 5, addOK[1]);
  end

  waitForNoImage("max.png");
end

-------------------------------------------------------------------------------
-- pixelBlockCheck(x, y, color, rgbTol, hueTol, size)
--
-- Checks for a block of pixels centered on (x, y), within radius size
-- matching color within the tolerances rgbTol and hueTol
-------------------------------------------------------------------------------

function pixelBlockCheck(x, y, color, rgbTol, hueTol, size)
  local startX = x - size;
  local startY = y - size;
  local endX = x + size;
  local endY = y + size;
  local i;
  for i = startX, endX do
    local j;
    for j = startY, endY do
      local currColor = srReadPixelFromBuffer(x, y);
      if(not compareColorEx(color,currColor,rgbTol,hueTol)) then
        return false;
      end
    end
  end
  return true;
end

function waitAndClickText(text, range, flags)
  local found = waitForText(
    text,
    60000,
    "Waiting for '" .. text .. "' text on the screen.",
    range,
    flags
  );
  lsDoFrame();

  if not found then
    error("Unable to find '" .. text .. "' on the screen");
  end

  clickText(found);
end

function waitAndClickImage(image, range, tol)
  local found = waitForImage(
    image,
    60000,
    "Waiting for image ''" .. image .. "' on the screen.",
    range,
    tol
  );
  lsDoFrame();

  if not found then
    error("Unable to find the image '" .. image .. "' on the screen");
  end

  clickText(found); --Works for images too!
end
