// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "printing/page_setup.h"

#include <algorithm>

#include "base/logging.h"

namespace printing {

PageMargins::PageMargins()
    : header(0),
      footer(0),
      left(0),
      right(0),
      top(0),
      bottom(0) {
}

void PageMargins::Clear() {
  header = 0;
  footer = 0;
  left = 0;
  right = 0;
  top = 0;
  bottom = 0;
}

bool PageMargins::Equals(const PageMargins& rhs) const {
  return header == rhs.header &&
      footer == rhs.footer &&
      left == rhs.left &&
      top == rhs.top &&
      right == rhs.right &&
      bottom == rhs.bottom;
}

PageSetup::PageSetup() {
  Clear();
}

PageSetup::PageSetup(const PageSetup& other) = default;

PageSetup::~PageSetup() {}

void PageSetup::Clear() {
  physical_size_.SetSize(0, 0);
  printable_area_.SetRect(0, 0, 0, 0);
  overlay_area_.SetRect(0, 0, 0, 0);
  content_area_.SetRect(0, 0, 0, 0);
  effective_margins_.Clear();
  text_height_ = 0;
  forced_margins_ = false;
}

bool PageSetup::Equals(const PageSetup& rhs) const {
  return physical_size_ == rhs.physical_size_ &&
      printable_area_ == rhs.printable_area_ &&
      overlay_area_ == rhs.overlay_area_ &&
      content_area_ == rhs.content_area_ &&
      effective_margins_.Equals(rhs.effective_margins_) &&
      requested_margins_.Equals(rhs.requested_margins_) &&
      text_height_ == rhs.text_height_;
}

void PageSetup::Init(const gfx::Size& physical_size,
                     const gfx::Rect& printable_area,
                     int text_height) {
  DCHECK_LE(printable_area.right(), physical_size.width());
  // I've seen this assert triggers on Canon GP160PF PCL 5e and HP LaserJet 5.
  // Since we don't know the dpi here, just disable the check.
  // DCHECK_LE(printable_area.bottom(), physical_size.height());
  DCHECK_GE(printable_area.x(), 0);
  DCHECK_GE(printable_area.y(), 0);
  DCHECK_GE(text_height, 0);
  physical_size_ = physical_size;
  printable_area_ = printable_area;
  text_height_ = text_height;

  SetRequestedMarginsAndCalculateSizes(requested_margins_);
}

void PageSetup::SetRequestedMargins(const PageMargins& requested_margins) {
  forced_margins_ = false;
  SetRequestedMarginsAndCalculateSizes(requested_margins);
}

void PageSetup::ForceRequestedMargins(const PageMargins& requested_margins) {
  forced_margins_ = true;
  SetRequestedMarginsAndCalculateSizes(requested_margins);
}

void PageSetup::FlipOrientation() {
  if (physical_size_.width() && physical_size_.height()) {
    gfx::Size new_size(physical_size_.height(), physical_size_.width());
    int new_y = physical_size_.width() -
                (printable_area_.width() + printable_area_.x());
    gfx::Rect new_printable_area(printable_area_.y(),
                                 new_y,
                                 printable_area_.height(),
                                 printable_area_.width());
    Init(new_size, new_printable_area, text_height_);
  }
}

void PageSetup::SetRequestedMarginsAndCalculateSizes(
    const PageMargins& requested_margins) {
  requested_margins_ = requested_margins;
  if (physical_size_.width() && physical_size_.height()) {
    if (forced_margins_)
      CalculateSizesWithinRect(gfx::Rect(physical_size_), 0);
    else
      CalculateSizesWithinRect(printable_area_, text_height_);
  }
}

void PageSetup::CalculateSizesWithinRect(const gfx::Rect& bounds,
                                         int text_height) {
  // Calculate the effective margins. The tricky part.
  effective_margins_.header = std::max(requested_margins_.header,
                                       bounds.y());
  effective_margins_.footer = std::max(requested_margins_.footer,
                                       physical_size_.height() -
                                           bounds.bottom());
  effective_margins_.left = std::max(requested_margins_.left,
                                     bounds.x());
  effective_margins_.top = std::max(std::max(requested_margins_.top,
                                             bounds.y()),
                                    effective_margins_.header + text_height);
  effective_margins_.right = std::max(requested_margins_.right,
                                      physical_size_.width() -
                                          bounds.right());
  effective_margins_.bottom =
      std::max(std::max(requested_margins_.bottom,
                        physical_size_.height() - bounds.bottom()),
               effective_margins_.footer + text_height);

  // Calculate the overlay area. If the margins are excessive, the overlay_area
  // size will be (0, 0).
  overlay_area_.set_x(effective_margins_.left);
  overlay_area_.set_y(effective_margins_.header);
  overlay_area_.set_width(std::max(0,
                                   physical_size_.width() -
                                       effective_margins_.right -
                                       overlay_area_.x()));
  overlay_area_.set_height(std::max(0,
                                    physical_size_.height() -
                                        effective_margins_.footer -
                                        overlay_area_.y()));

  // Calculate the content area. If the margins are excessive, the content_area
  // size will be (0, 0).
  content_area_.set_x(effective_margins_.left);
  content_area_.set_y(effective_margins_.top);
  content_area_.set_width(std::max(0,
                                   physical_size_.width() -
                                       effective_margins_.right -
                                       content_area_.x()));
  content_area_.set_height(std::max(0,
                                    physical_size_.height() -
                                        effective_margins_.bottom -
                                        content_area_.y()));
}

}  // namespace printing
