# Integrated unit tests

test_that("models can add agents each day", {
  # Create a model that creates a new agent each day
  population_model <- function(current_state, previous_state, model_data, agent_mgr, resource_mgr) {
    new_agent <- agent$new()
    agent_mgr$add_agent(new_agent)
  }

  initial_condition <- function(current_state, model_data, agent_mgr, resource_mgr) {
      # Do nothing
    }
  # Create a default village
  plains_village  <- village$new("Test_Village", initial_condition, models = population_model)
  # Run for 5 days
  new_siumulator <- simulation$new(6, villages = list(plains_village))
  new_siumulator$run_model()
  testthat::expect_length(new_siumulator$villages[[1]]$agent_mgr$agents, 6)
  ending_population <- new_siumulator$villages[[1]]$agent_mgr$get_living_population()
  testthat::expect_equal(ending_population, 6)
})

test_that("models can add and change resource quantities", {
  # Create a model that creates a stock of corn
  # At the end of three days, make sure that there are 6 corn stocks
  initial_condition <- function(current_state, model_data, agent_mgr, resource_mgr) {
    crop_resource <- resource$new(name = "corn", quantity = 0)
    resource_mgr$add_resource(crop_resource)
  }

  deterministic_crop_stock_model <- function(current_state, previous_state, model_data, agent_mgr, resource_mgr) {
    corn <- resource_mgr$get_resource("corn")
    corn$quantity <- corn$quantity + 3
  }

  # Create a default village
  plains_village  <- village$new("Test_Village", initial_condition, models = deterministic_crop_stock_model)
  new_siumulator <- simulation$new(3, villages = list(plains_village))
  new_siumulator$run_model()
  last_record <- new_siumulator$villages[[1]]$current_state
  corn <- last_record$resource_states %>% dplyr::filter(name == "corn")
  testthat::expect_equal(corn$quantity, 9)
})

test_that("models can change resoources based on information from the agent_manager", {
  # The village starts with 20 crops and is decreased each day by 2*population
  # For this unit test, the population is constant.
  # If things are working properly, there should be 8 crops left after 3 days

  initial_condition <- function(current_state, model_data, agent_mgr, resource_mgr) {
    # Create an initial stock of crops and add 2 agents
    crop_resource <- resource$new(name = "crops", quantity = 20)
    resource_mgr$add_resource(crop_resource)
    agent_mgr$add_agent(agent$new())
    agent_mgr$add_agent(agent$new())
  }

  crop_stock_model <- function(current_state, previous_state, model_data, agent_mgr, resource_mgr) {
    crops <- resource_mgr$get_resource("crops")
    # Each villager eats 2 crops each day
    crops$quantity <- crops$quantity - 2 * agent_mgr$get_living_population()
  }

  # Create a default village
  plains_village  <- village$new("Test_Village", initial_condition, models = crop_stock_model)
  new_siumulator <- simulation$new(3, villages = list(plains_village))
  new_siumulator$run_model()

  # Check to see if the correct number are left
  record_length <- length(new_siumulator$villages[[1]]$StateRecords)
  last_record <- new_siumulator$villages[[1]]$current_state
  crops <- last_record$resource_states %>% dplyr::filter(name == "crops")
  testthat::expect_equal(crops$quantity, 8)
})

test_that("models can have dynamics based on agent behavior", {
  initial_condition <- function(current_state, model_data, agent_mgr, resource_mgr) {
    # Create an initial stock of crops and add 2 agents
    crop_resource <- resource$new(name = "crops", quantity = 20)
    resource_mgr$add_resource(crop_resource)

    agent_mgr$add_agent(agent$new())
    agent_mgr$add_agent(agent$new())
  }

  # Create a model where agents are added if there is extra food available
  crop_stock_model <- function(current_state, previous_state, model_data, agent_mgr, resource_mgr) {
    crops <- resource_mgr$get_resource("crops")
    crops$quantity <- crops$quantity + 1
    if (crops$quantity - agent_mgr$get_living_population() > 0) {
      agent_mgr$add_agent(agent$new())
    }
  }

  # Create a default village
  plains_village  <- village$new("Test_Village", initial_condition, models = crop_stock_model)
  new_siumulator <- simulation$new(3, villages = list(plains_village))
  new_siumulator$run_model()

  # Check to see if the correct number are left
  record_length <- length(new_siumulator$villages[[1]]$StateRecords)
  last_record <- new_siumulator$villages[[1]]$StateRecords[[record_length]]
  testthat::expect_equal(plains_village$agent_mgr$get_living_population(), 5)
})

test_that("agents and resources can have properties changed in models", {
  initial_condition <- function(current_state, model_data, agent_mgr, resource_mgr) {
    # Create an initial state of 4 agents, all alive and marine resources
    resource_mgr$add_resource(resource$new(name = "marine", quantity = 100))
    dead_agent_id <- "dead_agent_1"
    dead_agent2_id <- "dead_agent_2"

    agent_mgr$add_agent(agent$new(identifier = dead_agent_id, alive = FALSE))
    agent_mgr$add_agent(agent$new(identifier = dead_agent2_id, alive = FALSE))
    agent_mgr$add_agent(agent$new(alive = TRUE))
    agent_mgr$add_agent(agent$new(alive = TRUE))
  }

  # Create a model where agents are set to alive/dead
  crop_stock_model <- function(current_state, previous_state, model_data, agent_mgr, resource_mgr) {
    # If it's not the first year, then set two agents to the dead state
    agent_1 <- agent_mgr$get_agent("dead_agent_1")
    agent_2 <- agent_mgr$get_agent("dead_agent_2")
    agent_1$alive <- FALSE
    agent_2$alive <- FALSE
    marine_resource <- resource_mgr$get_resource("marine")
    marine_resource$quantity <- 50
  }

  # Create a default village
  plains_village  <- village$new("Test_Village", initial_condition, models = crop_stock_model)
  new_siumulator <- simulation$new(4, villages = list(plains_village))
  new_siumulator$run_model()

  # Check to see if the correct number are left
  last_record <- new_siumulator$villages[[1]]$current_state
  testthat::expect_equal(plains_village$resource_mgr$get_resource("marine")$quantity,
                         50)
  testthat::expect_equal(plains_village$agent_mgr$get_living_population(), 2)
  testthat::expect_equal(plains_village$agent_mgr$get_living_population(), 2)
})


test_that("agents profession can change based on age", {
  initial_condition <- function(current_state, model_data, agent_manager, resource_mgr) {
    # 40 years old male
    agent_manager$add_agent(agent$new(identifier = "male1", age = 14610, alive = TRUE, gender = "Male"))
    # 20 year old male
    agent_manager$add_agent(agent$new(identifier = "male2", age = 7305, alive = TRUE, gender = "Male"))
    # 13 year old female
    agent_manager$add_agent(agent$new(identifier = "female1", age = 4748, alive = TRUE, gender = "Female"))
    # 8 year old female
    agent_manager$add_agent(agent$new(identifier = "female2", age = 2292, alive = TRUE, gender = "Female"))
  }

  agent_model <- function(current_state, previous_state, model_data, agent_mgr, resource_mgr) {
    # Get the new list of living agents and assign professions
    for (living_agent in agent_mgr$get_living_agents()) {
      if (living_agent$age >= 14610) {
        living_agent$profession <- "Forager"
      } else if (living_agent$age >= 3287 && living_agent$age < 14610 && living_agent$gender == "Male") {
        living_agent$profession <- "Fisher"
      } else if (living_agent$age >= 5113 && living_agent$age <= 14610 && living_agent$gender == "Female") {
        living_agent$profession <- "Farmer"
        } else if (living_agent$age >= 5113 && living_agent$age <= 5113 && living_agent$gender == "Female") {
          living_agent$profession <- "Fisher"
        } else if (living_agent$age < 5000 && living_agent$age > 3288) {
          living_agent$profession <- "Farmer"
        } else if (living_agent$age < 3287) {
          living_agent$profession <- "Child"
        }
      }
    }

  # Create a default village
  plains_village  <- village$new("Test_Village", initial_condition, models = agent_model)
  # Run the simulationn for a year so that the agents get assigned new professions
  new_siumulator <- simulation$new(364, villages = list(plains_village))
  new_siumulator$run_model()

  # Check to see that the professions are correct
  village_agent_mgr <- new_siumulator$villages[[1]]$agent_mgr
  testthat::expect_equal(village_agent_mgr$get_agent("male1")$profession, "Forager")
  testthat::expect_equal(village_agent_mgr$get_agent("male2")$profession, "Fisher")
  testthat::expect_equal(village_agent_mgr$get_agent("female1")$profession, "Farmer")
  testthat::expect_equal(village_agent_mgr$get_agent("female2")$profession, "Child")
})
