# An OrderArticle represents a single Article that is part of an Order.
class OrderArticle < ActiveRecord::Base

  attr_reader :update_current_price

  belongs_to :order
  belongs_to :article
  belongs_to :article_price
  has_many :group_order_articles, :dependent => :destroy

  validates_presence_of :order_id, :article_id
  validate :article_and_price_exist
  validates_uniqueness_of :article_id, scope: :order_id

  scope :ordered, :conditions => "units_to_order >= 1"

  before_create :init_from_balancing
  after_destroy :update_ordergroup_prices

  # This method returns either the ArticlePrice or the Article
  # The first will be set, when the the order is finished
  def price
    article_price || article
  end
  
  # Count quantities of belonging group_orders. 
  # In balancing this can differ from ordered (by supplier) quantity for this article.
  def group_orders_sum
    quantity = group_order_articles.collect(&:result).sum
    {:quantity => quantity, :price => quantity * price.fc_price}
  end

  # Update quantity/tolerance/units_to_order from group_order_articles
  def update_results!
    if order.open?
      quantity = group_order_articles.collect(&:quantity).sum
      tolerance = group_order_articles.collect(&:tolerance).sum
      update_attributes(:quantity => quantity, :tolerance => tolerance,
                        :units_to_order => calculate_units_to_order(quantity, tolerance))
    elsif order.finished?
      update_attribute(:units_to_order, group_order_articles.collect(&:result).sum)
    end
  end

  # Returns how many units of the belonging article need to be ordered given the specified order quantity and tolerance.
  # This is determined by calculating how many units can be ordered from the given order quantity, using
  # the tolerance to order an additional unit if the order quantity is not quiet sufficient.
  # There must always be at least one item in a unit that is an ordered quantity (no units are ever entirely
  # filled by tolerance items only).
  #
  # Example:
  #
  # unit_quantity | quantity | tolerance | calculate_units_to_order
  # --------------+----------+-----------+-----------------------
  #      4        |    0     |     2     |           0
  #      4        |    0     |     5     |           0
  #      4        |    2     |     2     |           1
  #      4        |    4     |     2     |           1
  #      4        |    4     |     4     |           1
  #      4        |    5     |     3     |           2
  #      4        |    5     |     4     |           2
  #
  def calculate_units_to_order(quantity, tolerance = 0)
    unit_size = price.unit_quantity
    units = quantity / unit_size
    remainder = quantity % unit_size
    units += ((remainder > 0) && (remainder + tolerance >= unit_size) ? 1 : 0)
  end

  # Calculate price for ordered quantity.
  def total_price
    units_to_order * price.unit_quantity * price.price
  end

  # Calculate gross price for ordered qunatity.
  def total_gross_price
    units_to_order * price.unit_quantity * price.gross_price
  end

  def ordered_quantities_equal_to_group_orders?
    (units_to_order * price.unit_quantity) == group_orders_sum[:quantity]
  end

  # Updates order_article and belongings during balancing process
  def update_article_and_price!(article_attributes, price_attributes, order_article_attributes)
    OrderArticle.transaction do
      # Updates self
      self.update_attributes!(order_article_attributes)

      # Updates article
      article.update_attributes!(article_attributes)

      # Updates article_price belonging to current order article
      article_price.attributes = price_attributes
      if article_price.changed?
        # Updates also price attributes of article if update_current_price is selected
        if update_current_price
          article.update_attributes!(price_attributes)
          self.article_price = article.article_prices.first # Assign new created article price to order article
        else
          # Creates a new article_price if neccessary
          # Set created_at timestamp to order ends, to make sure the current article price isn't changed
          create_article_price!(price_attributes.merge(created_at: order.ends)) and save
        end

        # Updates ordergroup values
        update_ordergroup_prices
      end
    end
  end

  def update_current_price=(value)
    @update_current_price = (value == true or value == '1') ?  true : false
  end

  # Units missing for the next full unit_quantity of the article
  def missing_units
    units = article.unit_quantity - ((quantity  % article.unit_quantity) + tolerance)
    units = 0 if units < 0
    units
  end

  private
  
  def article_and_price_exist
     errors.add(:article, "muss angegeben sein und einen aktuellen Preis haben") if !(article = Article.find(article_id)) || article.fc_price.nil?
  end

  # Associate with current article price if created in a finished order
  def init_from_balancing
    if order.present? and order.finished?
      self.article_price = article.article_prices.first
      self.units_to_order = 1
    end
  end

  def update_ordergroup_prices
    group_order_articles.each { |goa| goa.group_order.update_price! }
  end

end