module AR::Game::GaugeBehavior 

  def self.release_changes_for_children_when_changed
    r = AR::Events::Resolution.new event_class: AR::Events::GaugeChanged, as: "gauge" do |e|
      g = e.gauge
      change = e.change
      
      if e.modifier
        m = e.modifier 
        raise "Error: NOT MY MOD!" unless g.modifiers.include? m 
        m.value += change 
      else
        wh = e.which || "current_and_max"
        case wh
        when "current"
          g.change_current change 
        when "max"
          g.change_max change 
        when "current_and_max"
          g.change_current_and_max change 
        end
      end
      ret = g.child_gauge_relations.map(&:child_sync_event)
      ret
    end
  end

  def children_ids
    child_gauge_relations.map(&:child_gauge_id)
  end

  ## ids for gauges should be namespaced with ids of parents,
  #like namespace_perception
  def self.init ar_object 

    #add attr_serializeable properties here... 
    
    ar_object.class.attr_serializeable :value, :max_value, :name
    ar_object.class.attr_serializeable :child_gauge_relations do |relations, kase, parentObj|
      case kase
      when :unload
        relations.map{|rel| {type: rel.class, child_gauge_id: rel.child_gauge_id, data: rel.data}}
      when :load
        relations.map{|rel| Object.const_get(rel[:type]).new parentObj, rel[:child_gauge_id], rel[:data]} 
      end
    end

    ar_object.class.attr_serializeable :modifiers do |mods, kase|
      case kase
      when :unload
        mods.map{|m| {value: m.value, source: m.source} }
      when :load
        mods.map{|m| Modifier.new value: m[:value], source: m[:source]} 
      end
    end

    ar_object.value = 0
    ar_object.max_value = 0
    ar_object.modifiers = []
    ar_object.child_gauge_relations = []
  end

 


  def self.extended ar_object
    self.init ar_object
    ar_object.resolutions.push release_changes_for_children_when_changed
    #add to object's event subscriptions and resolutions here...
  end 

  def add_modifier mod
    raise "cant add another mod from the same source" if find_modifier_by_source(mod.source)
    self.modifiers.push mod
  end

  def remove_modifier mod
    self.modifiers.delete mod
  end

  def rem_modifier mod
    remove_modifier mod
  end


  def find_modifier_by_source s
    self.modifiers.select{|m| m.source == s}.last
  end


  def delete_modifier_by_source s
    mod = find_modifier_by_source s
    return if mod.nil? 
    self.modifiers.delete mod
    mod
  end

  def modified_value
    modified_amount self.value 
  end

  def modified_current
    modified_amount self.value
  end
  
  def modified_max
    modified_amount self.max_value 
  end


  def modified_amount v
    amount = modifier_total 
    unless amount.nil?
      v + amount 
    else
      v
    end
  end

  def modifier_total
    modifiers.map{|m| m.value }.reduce(:+) || 0
  end

  def limit_check!
    if self.modified_current > self.modified_max
      diff = self.modified_max - self.modified_current
      self.value += diff
    elsif self.modified_current < 0
      diff = 0 - self.modified_current
      self.value += diff
    end
  end

  def percent_capacity
    (100 * (self.modified_current.to_f / self.modified_max)).floor
  end


  def to_max!
    diff = self.modified_max - self.modified_current
    change_current diff
  end

  def change_current v
    ret = self.value += v
    limit_check!
    ret
  end 


  def change_value v
    change_current v
  end 

  def change_max v
    ret = self.max_value += v
    limit_check!
    ret
  end


  def change_current_and_max v
    self.change_max v
    self.change_current v
  end

  def change_value_and_max v
    change_current_and_max  v
  end


  def modify value, source
    m = (Modifier.new value: value, source: source)
    self.add_modifier m 
    m
  end

  def parents! c, params
    raise "cant parent the same gauge twice" if already_parents? c
    type_rel = params[:with]
    r = type_rel.new self, c.id, params[:data]
    c.modify r.foreign_mod_value, self.parent_source_name
    child_gauge_relations.push r
  end

  def manual_child_sync
    child_gauge_relations.map do |r| 
      m = r.child_modifier 
      m.value = r.foreign_mod_value
      r.child.manual_child_sync
    end
  end


  def already_parents? child
    child_gauge_relations.map(&:child_gauge_id).include? child.id
  end

  def relation_for child
    child_gauge_relations.select{ |r| r.child_gauge_id == child.id}.last
  end


  def parent_source_name
    "#{self.id}_bonus"
  end






  class Modifier
    attr_accessor :value
    attr_accessor :source

    def initialize params
      self.value = params[:value] || params["value"]
      self.source = params[:source] || params["source"]
    end

  end


  class ChildGaugeRelation
    attr_accessor :this_gauge_parent, :child_gauge_id, :data

    def initialize this_gauge_parent, child_gauge_id, data={}
      self.this_gauge_parent = this_gauge_parent
      self.child_gauge_id = child_gauge_id
      self.data = data
    end

    def child
      self.this_gauge_parent.world.find child_gauge_id
    end

    def child_modifier
      child.find_modifier_by_source this_gauge_parent.parent_source_name
    end

    def serialize
      {type: self.class, child_gauge_id: self.child_gauge_id, data: data}
    end

    def change_from current, new_val
      new_val - current
    end


    #release a modifier changed based on calculation
    def child_sync_event 
      raise "ERR: you need to override child_sync_event"
    end

    #represent the mod value applied to child gauge
    def foreign_mod_value
      raise "ERR: you need to override foreign_mod_value"
    end
  end



  class LinearGaugeRelation < ChildGaugeRelation

    def initialize this_gauge_parent, child_gauge_id, data
      super 
    end

    def factor
      data[:factor] or data["factor"]
    end

    def factor=(f)
      data[:factor] = f
    end

    def foreign_mod_value
      (this_gauge_parent.modified_value * factor).floor
    end

    #release a modifier changed based on calculation
    def child_sync_event
      
      m = child_modifier 
      new_val = foreign_mod_value
      change = change_from m.value, new_val
      unless change == 0
        AR::Events::GaugeChanged.new gauge: child, modifier: m, change: change
      else
        nil 
      end
    end
  end
end


