------------------------------------------------------------
-- Copyright (c) 2016 tacigar
-- https://github.com/tacigar/maidroid
------------------------------------------------------------

local _aux = maidroid.modules._aux

local state = {
  walk = 0,
  punch = 1,
  plant = 2,
  walk_to_plant = 3,
  walk_to_soil = 4,
  walk_avoid = 5,
}
local max_punch_time = 20
local max_plant_time = 15
local max_avoid_time = 15
local search_lenvec = {x = 3, y = 0, z = 3}


-- find max size of each plants
local target_plants_list = {}
minetest.after(0, function()
  local max = {}
  for name, node in pairs(minetest.registered_nodes) do
    if minetest.get_item_group(name, "plant") > 0 then
      local s, i = string.match(name, "(.+)_(%d+)")
      if (s and i) and (max[s] == nil or max[s] < i) then max[s] = i end
    end
  end
  for s, i in pairs(max) do
    table.insert(target_plants_list, s.."_"..i)
  end
end)


-- check the maidroid has seed items
local function has_seed_item(self)
  local inv = maidroid._aux.get_maidroid_inventory(self)
  local stacks = inv:get_list("main")
  for _, stack in ipairs(stacks) do
    local item_name = stack:get_name()
    if minetest.get_item_group(item_name, "seed") > 0 then
      return true
    end
  end
  return false
end


-- check can plant plants.
local function can_plant(self, pos)
  local node = minetest.get_node(pos)
  local upos = _aux.get_under_pos(pos)
  local unode = minetest.get_node(upos)
  return node.name == "air"
     and minetest.get_item_group(unode.name, "wet") > 0
     and has_seed_item(self)
end


-- check can punch plant
local function can_punch(self, pos)
  local node = minetest.get_node(pos)
  return maidroid.util.table_find_value(target_plants_list, node.name)
end


-- change state to walk
local function to_walk(self)
  self.state = state.walk
  self.destination = nil
  self.object:set_animation(maidroid.animations.walk, 15, 0)
  self.time_count = 0
  _aux.change_dir(self)
end


local function to_walk_avoid(self)
  to_walk(self)
  self.state = state.walk_avoid
end


maidroid.register_module("maidroid:farming_module", {
  description = "Maidroid Module : Farming",
  inventory_image = "maidroid_farming_module.png",

  initialize = function(self)
    self.object:set_animation(maidroid.animations.walk, 15, 0)
    self.object:setacceleration{x = 0, y = -10, z = 0}
    self.state = state.walk
    self.preposition = self.object:getpos()
    self.time_count = 0
    self.destination = nil -- for walk_to_*
    _aux.change_dir(self)
  end,

  finalize = function(self)
    self.state = nil
    self.preposition = nil
    self.time_count = nil
    self.destination = nil
    self.object:setvelocity{x = 0, y = 0, z = 0}
  end,

  on_step = function(self, dtime)
    local pos = self.object:getpos()
    local rpos = vector.round(pos)
    local upos = _aux.get_under_pos(pos)
    local yaw = self.object:getyaw()

    _aux.pickup_item(self, 1.5, function(itemstring) -- pickup droped seed items
      return minetest.get_item_group(itemstring, "seed") > 0
    end)
    if self.state == state.walk then -- searching plants or spaces
      local b1, dest1 = _aux.search_surrounding(self, search_lenvec, can_plant)
      local b2, dest2 = _aux.search_surrounding(self, search_lenvec, can_punch)
      -- search soil node near
      if b1 then -- to soil
        self.state = state.walk_to_soil
        self.destination = dest1
        _aux.change_dir_to(self, dest1)
      elseif b2 then
        self.state = state.walk_to_plant
        self.destination = dest2
        _aux.change_dir_to(self, dest2)
      elseif pos.x == self.preposition.x or pos.z == self.preposition.z then
        _aux.change_dir(self)
      end

    elseif self.state == state.punch then
      if self.time_count >= max_punch_time then
        if can_punch(self, self.destination) then
          local destnode = minetest.get_node(self.destination)
          minetest.remove_node(self.destination)
          local inv = minetest.get_inventory{type = "detached", name = self.invname}
          local stacks = minetest.get_node_drops(destnode.name)
          for _, stack in ipairs(stacks) do
            local leftover = inv:add_item("main", stack)
            minetest.add_item(self.destination, leftover)
          end
        end
        to_walk(self)
      else
        self.time_count = self.time_count + 1
      end

    elseif self.state == state.plant then
      if self.time_count >= max_plant_time then
        if can_plant(self, self.destination) then
          local inv = minetest.get_inventory{type = "detached", name = self.invname}
          local stacks = inv:get_list("main")
          for idx, stack in ipairs(stacks) do
            local item_name = stack:get_name()
            if minetest.get_item_group(item_name, "seed") > 0 then
              minetest.add_node(self.destination, {name = item_name, param2 = 1})
              stack:take_item(1)
              inv:set_stack("main", idx, stack)
              break
            end
          end
        end
        to_walk(self)
      else
        self.time_count = self.time_count + 1
      end

    elseif self.state == state.walk_to_soil then
      if vector.distance(pos, self.destination) < 1.5 then -- to plant state
        local destnode = minetest.get_node(self.destination)
        if (can_plant(self, self.destination)) then
          self.state = state.plant
          self.object:set_animation(maidroid.animations.mine, 15, 0)
          self.object:setvelocity{x = 0, y = 0, z = 0}
        else to_walk(self) end
      else
        if pos.x == self.preposition.x or pos.z == self.preposition.z then
          to_walk_avoid(self)
        end
      end

    elseif self.state == state.walk_to_plant then
      if vector.distance(pos, self.destination) < 1.5 then
        local destnode = minetest.get_node(self.destination)
        if maidroid.util.table_find_value(target_plants_list, destnode.name) then
          self.state = state.punch
          self.object:set_animation(maidroid.animations.mine, 15, 0)
          self.object:setvelocity{x = 0, y = 0, z = 0}
        else to_walk(self) end
      else
        if pos.x == self.preposition.x or pos.z == self.preposition.z then
          to_walk_avoid(self)
        end
      end

    elseif self.state == state.walk_avoid then
      if self.time_count > max_avoid_time then
        self.state = state.walk
        self.time_count = 0
      else
        self.time_count = self.time_count + 1
      end
    end
    self.preposition = pos
    return
  end
})