#===============================================================================
# Represents a planted berry. Stored in $PokemonGlobal.eventvars.
#===============================================================================
class BerryPlantData
  attr_accessor :new_mechanics        # false for Gen 3, true for Gen 4
  attr_accessor :berry_id
  attr_accessor :mulch_id             # Gen 4 mechanics
  attr_accessor :time_alive
  attr_accessor :time_last_updated
  attr_accessor :growth_stage
  attr_accessor :replant_count
  attr_accessor :watered_this_stage   # Gen 3 mechanics
  attr_accessor :watering_count       # Gen 3 mechanics
  attr_accessor :moisture_level       # Gen 4 mechanics
  attr_accessor :yield_penalty        # Gen 4 mechanics

  def initialize
    reset
  end

  def reset(planting = false)
    @new_mechanics      = Settings::NEW_BERRY_PLANTS
    @berry_id           = nil
    @mulch_id           = nil if !planting
    @time_alive         = 0
    @time_last_updated  = 0
    @growth_stage       = 0
    @replant_count      = 0
    @watered_this_stage = false
    @watering_count     = 0
    @moisture_level     = 100
    @yield_penalty      = 0
  end

  def plant(berry_id)
    reset(true)
    @berry_id          = berry_id
    @growth_stage      = 1
    @time_last_updated = pbGetTimeNow.to_i
  end

  def replant
    @time_alive         = 0
    @growth_stage       = 2
    @replant_count      += 1
    @watered_this_stage = false
    @watering_count     = 0
    @moisture_level     = 100
    @yield_penalty      = 0
  end

  def planted?
    return @growth_stage > 0
  end

  def growing?
    return @growth_stage > 0 && @growth_stage < 5
  end

  def grown?
    return @growth_stage >= 5
  end

  def replanted?
    return @replant_count > 0
  end

  def moisture_stage
    return 0 if !@new_mechanics
    return 2 if @moisture_level > 50
    return 1 if @moisture_level > 0
    return 0
  end

  def water
    @moisture_level = 100
    if !@watered_this_stage
      @watered_this_stage = true
      @watering_count += 1
    end
  end

  def berry_yield
    data = GameData::BerryPlant.get(@berry_id)
    if @new_mechanics
      return [data.maximum_yield * (5 - @yield_penalty) / 5, data.minimum_yield].max
    elsif @watering_count > 0
      ret = (data.maximum_yield - data.minimum_yield) * (@watering_count - 1)
      ret += rand(1 + data.maximum_yield - data.minimum_yield)
      return (ret / 4) + data.minimum_yield
    end
    return data.minimum_yield
  end

  # Old mechanics only update a plant when its map is loaded. New mechanics
  # update it every frame while its map is loaded.
  def update
    return if !planted?
    time_now = pbGetTimeNow
    time_delta = time_now.to_i - @time_last_updated
    return if time_delta <= 0
    new_time_alive = @time_alive + time_delta
    # Get all growth data
    plant_data = GameData::BerryPlant.get(@berry_id)
    time_per_stage = plant_data.hours_per_stage * 3600   # In seconds
    drying_per_hour = plant_data.drying_per_hour
    max_replants = GameData::BerryPlant::NUMBER_OF_REPLANTS
    stages_growing = GameData::BerryPlant::NUMBER_OF_GROWTH_STAGES
    stages_fully_grown = GameData::BerryPlant::NUMBER_OF_FULLY_GROWN_STAGES
    case @mulch_id
    when :GROWTHMULCH
      time_per_stage = (time_per_stage * 0.75).to_i
      drying_per_hour = (drying_per_hour * 1.5).ceil
    when :DAMPMULCH
      time_per_stage = (time_per_stage * 1.25).to_i
      drying_per_hour /= 2
    when :GOOEYMULCH
      max_replants = (max_replants * 1.5).ceil
    when :STABLEMULCH
      stages_fully_grown = (stages_fully_grown * 1.5).ceil
    end
    # Do replants
    done_replant = false
    loop do
      stages_this_life = stages_growing + stages_fully_grown - (replanted? ? 1 : 0)
      break if new_time_alive < stages_this_life * time_per_stage
      if @replant_count >= max_replants
        reset
        return
      end
      replant
      done_replant = true
      new_time_alive -= stages_this_life * time_per_stage
    end
    # Update how long plant has been alive for
    old_growth_stage = @growth_stage
    @time_alive = new_time_alive
    @growth_stage = 1 + (@time_alive / time_per_stage)
    @growth_stage += 1 if replanted?   # Replants start at stage 2
    @time_last_updated = time_now.to_i
    # Record watering (old mechanics), and apply drying out per hour (new mechanics)
    if @new_mechanics
      old_growth_hour = (done_replant) ? 0 : (@time_alive - time_delta) / 3600
      new_growth_hour = @time_alive / 3600
      if new_growth_hour > old_growth_hour
        (new_growth_hour - old_growth_hour).times do
          if @moisture_level > 0
            @moisture_level -= drying_per_hour
          else
            @yield_penalty += 1
          end
        end
      end
    else
      old_growth_stage = 0 if done_replant
      new_growth_stage = [@growth_stage, stages_growing + 1].min
      @watered_this_stage = false if new_growth_stage > old_growth_stage
      water if $game_screen && GameData::Weather.get($game_screen.weather_type).category == :Rain
    end
  end
end

#===============================================================================
#
#===============================================================================
class BerryPlantMoistureSprite
  def initialize(event, map, viewport = nil)
    @event          = event
    @map            = map
    @sprite         = IconSprite.new(0, 0, viewport)
    @sprite.ox      = 16
    @sprite.oy      = 24
    @moisture_stage = -1   # -1 = none, 0 = dry, 1 = damp, 2 = wet
    @disposed       = false
    update_graphic
  end

  def dispose
    @sprite.dispose
    @map      = nil
    @event    = nil
    @disposed = true
  end

  def disposed?
    return @disposed
  end

  def update_graphic
    case @moisture_stage
    when -1 then @sprite.setBitmap("")
    when 0  then @sprite.setBitmap("Graphics/Characters/berrytreedry")
    when 1  then @sprite.setBitmap("Graphics/Characters/berrytreedamp")
    when 2  then @sprite.setBitmap("Graphics/Characters/berrytreewet")
    end
  end

  def update
    return if !@sprite || !@event
    new_moisture = -1
    berry_plant = @event.variable
    if berry_plant.is_a?(BerryPlantData) && berry_plant.planted?
      new_moisture = berry_plant.moisture_stage
    end
    if new_moisture != @moisture_stage
      @moisture_stage = new_moisture
      update_graphic
    end
    @sprite.update
    @sprite.x      = ScreenPosHelper.pbScreenX(@event)
    @sprite.y      = ScreenPosHelper.pbScreenY(@event)
    @sprite.zoom_x = ScreenPosHelper.pbScreenZoomX(@event)
    @sprite.zoom_y = @sprite.zoom_x
    pbDayNightTint(@sprite)
  end
end

#===============================================================================
#
#===============================================================================
class BerryPlantSprite
  def initialize(event, map, _viewport)
    @event     = event
    @map       = map
    @old_stage = 0
    @disposed  = false
    berry_plant = event.variable
    return if !berry_plant
    @old_stage = berry_plant.growth_stage
    @event.character_name = ""
    update_plant(berry_plant)
    set_event_graphic(berry_plant, true)   # Set the event's graphic
  end

  def dispose
    @event    = nil
    @map      = nil
    @disposed = true
  end

  def disposed?
    @disposed
  end

  def set_event_graphic(berry_plant, full_check = false)
    return if !berry_plant || (berry_plant.growth_stage == @old_stage && !full_check)
    case berry_plant.growth_stage
    when 0
      @event.character_name = ""
    else
      if berry_plant.growth_stage == 1
        @event.character_name = "berrytreeplanted"   # Common to all berries
        @event.turn_down
      else
        filename = sprintf("berrytree_%s", GameData::Item.get(berry_plant.berry_id).id.to_s)
        if pbResolveBitmap("Graphics/Characters/" + filename)
          @event.character_name = filename
          case berry_plant.growth_stage
          when 2 then @event.turn_down    # X sprouted
          when 3 then @event.turn_left    # X taller
          when 4 then @event.turn_right   # X flowering
          else
            @event.turn_up if berry_plant.growth_stage >= 5   # X berries
          end
        else
          @event.character_name = "Object ball"
        end
      end
      if berry_plant.new_mechanics && @old_stage != berry_plant.growth_stage &&
         @old_stage > 0 && berry_plant.growth_stage <= GameData::BerryPlant::NUMBER_OF_GROWTH_STAGES + 1
        spriteset = $scene.spriteset(@map.map_id)
        spriteset&.addUserAnimation(Settings::PLANT_SPARKLE_ANIMATION_ID,
                                    @event.x, @event.y, false, 1)
      end
    end
    @old_stage = berry_plant.growth_stage
  end

  def update_plant(berry_plant, initial = false)
    berry_plant.update if berry_plant.planted? && (initial || berry_plant.new_mechanics)
  end

  def update
    berry_plant = @event.variable
    return if !berry_plant
    update_plant(berry_plant)
    set_event_graphic(berry_plant)
  end
end

#===============================================================================
#
#===============================================================================
EventHandlers.add(:on_new_spriteset_map, :add_berry_plant_graphics,
  proc { |spriteset, viewport|
    map = spriteset.map
    map.events.each do |event|
      next if !event[1].name[/berryplant/i]
      spriteset.addUserSprite(BerryPlantMoistureSprite.new(event[1], map, viewport))
      spriteset.addUserSprite(BerryPlantSprite.new(event[1], map, viewport))
    end
  }
)

#===============================================================================
#
#===============================================================================
def pbBerryPlant
  interp = pbMapInterpreter
  this_event = interp.get_self
  berry_plant = interp.getVariable
  if !berry_plant
    berry_plant = BerryPlantData.new
    interp.setVariable(berry_plant)
  end
  berry = berry_plant.berry_id
  # Interact with the event based on its growth
  if berry_plant.grown?
    this_event.turn_up   # Stop the event turning towards the player
    berry_plant.reset if pbPickBerry(berry, berry_plant.berry_yield)
    return
  elsif berry_plant.growing?
    berry_name = GameData::Item.get(berry).name
    case berry_plant.growth_stage
    when 1   # X planted
      this_event.turn_down   # Stop the event turning towards the player
      if berry_name.starts_with_vowel?
        pbMessage(_INTL("An {1} was planted here.", berry_name))
      else
        pbMessage(_INTL("A {1} was planted here.", berry_name))
      end
    when 2   # X sprouted
      this_event.turn_down   # Stop the event turning towards the player
      pbMessage(_INTL("The {1} has sprouted.", berry_name))
    when 3   # X taller
      this_event.turn_left   # Stop the event turning towards the player
      pbMessage(_INTL("The {1} plant is growing bigger.", berry_name))
    else     # X flowering
      this_event.turn_right   # Stop the event turning towards the player
      if Settings::NEW_BERRY_PLANTS
        pbMessage(_INTL("This {1} plant is in bloom!", berry_name))
      else
        case berry_plant.watering_count
        when 4
          pbMessage(_INTL("This {1} plant is in fabulous bloom!", berry_name))
        when 3
          pbMessage(_INTL("This {1} plant is blooming very beautifully!", berry_name))
        when 2
          pbMessage(_INTL("This {1} plant is blooming prettily!", berry_name))
        when 1
          pbMessage(_INTL("This {1} plant is blooming cutely!", berry_name))
        else
          pbMessage(_INTL("This {1} plant is in bloom!", berry_name))
        end
      end
    end
    # Water the growing plant
    GameData::BerryPlant::WATERING_CANS.each do |item|
      next if !$bag.has?(item)
      break if !pbConfirmMessage(_INTL("Want to sprinkle some water with the {1}?",
                                       GameData::Item.get(item).name))
      berry_plant.water
      pbMessage("\\se[Water berry plant]" + _INTL("{1} watered the plant.", $player.name) + "\\wtnp[40]")
      if Settings::NEW_BERRY_PLANTS
        pbMessage(_INTL("There! All happy!"))
      else
        pbMessage(_INTL("The plant seemed to be delighted."))
      end
      break
    end
    return
  end
  # Nothing planted yet
  ask_to_plant = true
  if Settings::NEW_BERRY_PLANTS
    # New mechanics
    if berry_plant.mulch_id
      pbMessage(_INTL("{1} has been laid down.", GameData::Item.get(berry_plant.mulch_id).name))
    else
      case pbMessage(_INTL("It's soft, earthy soil."),
                     [_INTL("Fertilize"), _INTL("Plant Berry"), _INTL("Exit")], -1)
      when 0   # Fertilize
        mulch = nil
        pbFadeOutIn do
          scene = PokemonBag_Scene.new
          screen = PokemonBagScreen.new(scene, $bag)
          mulch = screen.pbChooseItemScreen(proc { |item| GameData::Item.get(item).is_mulch? })
        end
        return if !mulch
        mulch_data = GameData::Item.get(mulch)
        if mulch_data.is_mulch?
          berry_plant.mulch_id = mulch
          $bag.remove(mulch)
          pbMessage(_INTL("The {1} was scattered on the soil.", mulch_data.name))
        else
          pbMessage(_INTL("That won't fertilize the soil!"))
          return
        end
      when 1   # Plant Berry
        ask_to_plant = false
      else   # Exit/cancel
        return
      end
    end
  else
    # Old mechanics
    return if !pbConfirmMessage(_INTL("It's soft, loamy soil. Want to plant a berry?"))
    ask_to_plant = false
  end
  if !ask_to_plant || pbConfirmMessage(_INTL("Want to plant a Berry?"))
    pbFadeOutIn do
      scene = PokemonBag_Scene.new
      screen = PokemonBagScreen.new(scene, $bag)
      berry = screen.pbChooseItemScreen(proc { |item| GameData::Item.get(item).is_berry? })
    end
    if berry
      $stats.berries_planted += 1
      berry_plant.plant(berry)
      $bag.remove(berry)
      if Settings::NEW_BERRY_PLANTS
        pbMessage(_INTL("The {1} was planted in the soft, earthy soil.",
                        GameData::Item.get(berry).name))
      elsif GameData::Item.get(berry).name.starts_with_vowel?
        pbMessage(_INTL("{1} planted an {2} in the soft loamy soil.",
                        $player.name, GameData::Item.get(berry).name))
      else
        pbMessage(_INTL("{1} planted a {2} in the soft loamy soil.",
                        $player.name, GameData::Item.get(berry).name))
      end
    end
  end
end

#===============================================================================
#
#===============================================================================
def pbPickBerry(berry, qty = 1)
  berry = GameData::Item.get(berry)
  berry_name = (qty > 1) ? berry.portion_name_plural : berry.portion_name
  if qty > 1
    message = _INTL("There are {1} \\c[1]{2}\\c[0]!\nWant to pick them?", qty, berry_name)
  else
    message = _INTL("There is 1 \\c[1]{1}\\c[0]!\nWant to pick it?", berry_name)
  end
  return false if !pbConfirmMessage(message)
  if !$bag.can_add?(berry, qty)
    pbMessage(_INTL("Too bad...\nThe Bag is full..."))
    return false
  end
  $stats.berry_plants_picked += 1
  if qty >= GameData::BerryPlant.get(berry.id).maximum_yield
    $stats.max_yield_berry_plants += 1
  end
  $bag.add(berry, qty)
  if qty > 1
    pbMessage("\\me[Berry get]" + _INTL("You picked the {1} \\c[1]{2}\\c[0].", qty, berry_name) + "\\wtnp[30]")
  else
    pbMessage("\\me[Berry get]" + _INTL("You picked the \\c[1]{1}\\c[0].", berry_name) + "\\wtnp[30]")
  end
  pocket = berry.pocket
  pbMessage(_INTL("You put the {1} in\\nyour Bag's <icon=bagPocket{2}>\\c[1]{3}\\c[0] pocket.",
                  berry_name, pocket, PokemonBag.pocket_names[pocket - 1]) + "\1")
  if Settings::NEW_BERRY_PLANTS
    pbMessage(_INTL("The soil returned to its soft and earthy state."))
  else
    pbMessage(_INTL("The soil returned to its soft and loamy state."))
  end
  this_event = pbMapInterpreter.get_self
  pbSetSelfSwitch(this_event.id, "A", true)
  return true
end
