/*
 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#ifndef SVGTextFragment_h
#define SVGTextFragment_h

#include "core/layout/line/GlyphOverflow.h"
#include "platform/transforms/AffineTransform.h"
#include "platform/wtf/Allocator.h"

namespace blink {

// A SVGTextFragment describes a text fragment of a LayoutSVGInlineText which
// can be laid out at once.
struct SVGTextFragment {
  DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
  SVGTextFragment()
      : character_offset(0),
        metrics_list_offset(0),
        length(0),
        is_text_on_path(false),
        is_vertical(false),
        x(0),
        y(0),
        width(0),
        height(0),
        length_adjust_scale(1),
        length_adjust_bias(0) {}

  enum TransformType {
    kTransformRespectingTextLength,
    kTransformIgnoringTextLength
  };

  FloatRect BoundingBox(float baseline) const {
    FloatRect fragment_rect(x, y - baseline, width, height);
    if (!IsTransformed())
      return fragment_rect;
    return BuildNormalFragmentTransform().MapRect(fragment_rect);
  }

  FloatRect OverflowBoundingBox(float baseline) const {
    FloatRect fragment_rect(
        x - glyph_overflow.left, y - baseline - glyph_overflow.top,
        width + glyph_overflow.left + glyph_overflow.right,
        height + glyph_overflow.top + glyph_overflow.bottom);
    if (!IsTransformed())
      return fragment_rect;
    return BuildNormalFragmentTransform().MapRect(fragment_rect);
  }

  FloatQuad BoundingQuad(float baseline) const {
    FloatQuad fragment_quad(FloatRect(x, y - baseline, width, height));
    if (!IsTransformed())
      return fragment_quad;
    return BuildNormalFragmentTransform().MapQuad(fragment_quad);
  }

  AffineTransform BuildFragmentTransform(
      TransformType type = kTransformRespectingTextLength) const {
    if (type == kTransformIgnoringTextLength) {
      AffineTransform result = transform;
      TransformAroundOrigin(result);
      return result;
    }
    return BuildNormalFragmentTransform();
  }

  bool IsTransformed() const {
    return AffectedByTextLength() || !transform.IsIdentity();
  }

  // The first laid out character starts at LayoutSVGInlineText::characters() +
  // characterOffset.
  unsigned character_offset;
  unsigned metrics_list_offset;
  unsigned length : 30;
  unsigned is_text_on_path : 1;
  unsigned is_vertical : 1;

  float x;
  float y;
  float width;
  float height;

  GlyphOverflow glyph_overflow;

  // Includes rotation/glyph-orientation-(horizontal|vertical) transforms, as
  // well as orientation related shifts
  // (see SVGTextLayoutEngine, which builds this transformation).
  AffineTransform transform;

  // Contains lengthAdjust related transformations, which are not allowd to
  // influence the SVGTextQuery code.
  float length_adjust_scale;
  float length_adjust_bias;

 private:
  AffineTransform BuildNormalFragmentTransform() const {
    if (is_text_on_path)
      return BuildTransformForTextOnPath();
    return BuildTransformForTextOnLine();
  }

  bool AffectedByTextLength() const { return length_adjust_scale != 1; }

  void TransformAroundOrigin(AffineTransform& result) const {
    // Returns (translate(x, y) * result) * translate(-x, -y).
    result.SetE(result.E() + x);
    result.SetF(result.F() + y);
    result.Translate(-x, -y);
  }

  AffineTransform BuildTransformForTextOnPath() const {
    // For text-on-path layout, multiply the transform with the
    // lengthAdjustTransform before orienting the resulting transform.
    // T(x,y) * M(transform) * M(lengthAdjust) * T(-x,-y)
    AffineTransform result = !AffectedByTextLength()
                                 ? transform
                                 : transform * LengthAdjustTransform();
    if (!result.IsIdentity())
      TransformAroundOrigin(result);
    return result;
  }

  AffineTransform LengthAdjustTransform() const {
    AffineTransform result;
    if (!AffectedByTextLength())
      return result;
    // Load a transform assuming horizontal direction, then swap if vertical.
    result.SetMatrix(length_adjust_scale, 0, 0, 1, length_adjust_bias, 0);
    if (is_vertical) {
      result.SetD(result.A());
      result.SetA(1);
      result.SetF(result.E());
      result.SetE(0);
    }
    return result;
  }

  AffineTransform BuildTransformForTextOnLine() const {
    // For text-on-line layout, orient the transform first, then multiply
    // the lengthAdjustTransform with the oriented transform.
    // M(lengthAdjust) * T(x,y) * M(transform) * T(-x,-y)
    if (transform.IsIdentity())
      return LengthAdjustTransform();

    AffineTransform result = transform;
    TransformAroundOrigin(result);
    result.PreMultiply(LengthAdjustTransform());
    return result;
  }
};

}  // namespace blink

#endif
