// Copyright 2015 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.

part of flutter_sprites;

/// A NineSliceSprite is similar to a [Sprite], but it it can strech its
/// inner area to fit the size of the [Node]. This is ideal for fast drawing
/// of things like buttons.
class NineSliceSprite extends NodeWithSize with SpritePaint {

  /// Creates a new NineSliceSprite from the privided [texture], [size], and
  /// texture [insets].
  NineSliceSprite(Texture texture, Size size, EdgeInsets insets) : super(size) {
    assert(texture != null && !texture.rotated);
    assert(size != null);
    assert(insets != null);
    pivot = const Point(0.5, 0.5);
    this.texture = texture;
    this.insets = insets;
  }

  /// Creates a new NineSliceSprite from the provided [image], [size], and
  /// texture [insets].
  NineSliceSprite.fromImage(ui.Image image, Size size, EdgeInsets insets)
    : this(new Texture(image), size, insets);

  /// The texture that the sprite will render to screen. Cannot be null.
  ///
  ///     my9Sprite.texture = myTexture;
  Texture get texture => _texture;

  Texture _texture;

  set texture(Texture texture) {
    _texture = texture;
    _isDirty = true;
    if (texture == null) {
      _cachedPaint = new Paint();
    } else {
      Matrix4 matrix = new Matrix4.identity();
      ImageShader shader = new ImageShader(texture.image,
        TileMode.repeated, TileMode.repeated, matrix.storage);

      _cachedPaint = new Paint()
        ..shader = shader;
    }
  }

  /// The insets of the texture as normalized values. The insets define the
  /// areas of the texture that will not be deformed as the sprite stretches.
  EdgeInsets get insets => _insets;

  EdgeInsets _insets;

  set insets(EdgeInsets insets) {
    assert(insets != null);
    _insets = insets;
    _isDirty = true;
  }

  /// If true, the center part of the sprite will be drawn, this is the default
  /// behavior.
  bool get drawCenterPart => _drawCenterPart;

  bool _drawCenterPart = true;

  set drawCenterPart(bool drawCenterPart) {
    _drawCenterPart = drawCenterPart;
    _isDirty = true;
  }

  @override
  set size(Size size) {
    super.size = size;
    _isDirty = true;
  }

  Paint _cachedPaint = new Paint()
    ..filterQuality = FilterQuality.low
    ..isAntiAlias = false;

  // Cached values.
  bool _isDirty = true;
  List<Point> _vertices;
  List<Point> _textureCoordinates;
  List<Color> _colors;
  List<int> _indices;

  @override
  void paint(Canvas canvas) {
    applyTransformForPivot(canvas);

    // Setup paint object for opacity and transfer mode.
    _updatePaint(_cachedPaint);

    if (_isDirty) {
      // Calcuate vertices and indices.
      _vertices = <Point>[
        Point.origin,

      ];

      // Texture width and height.
      double tw = texture.frame.width;
      double th = texture.frame.height;
      _textureCoordinates = <Point>[];
      _vertices = <Point>[];
      _colors = <Color>[];

      for (int y = 0; y < 4; y += 1) {
        double vy;
        double ty;

        switch(y) {
          case 0:
            vy = 0.0;
            ty = texture.frame.top;
            break;
          case 1:
            vy = insets.top * th;
            ty = texture.frame.top + insets.top * th;
            break;
          case 2:
            vy = size.height - insets.bottom * th;
            ty = texture.frame.bottom - insets.bottom * th;
            break;
          case 3:
            vy = size.height;
            ty = texture.frame.bottom;
            break;
        }

        for (int x = 0; x < 4; x += 1) {
          double vx;
          double tx;

          switch(x) {
            case 0:
              vx = 0.0;
              tx = texture.frame.left;
              break;
            case 1:
              vx = insets.left * tw;
              tx = texture.frame.left + insets.left * tw;
              break;
            case 2:
              vx = size.width - insets.right * tw;
              tx = texture.frame.right - insets.right * tw;
              break;
            case 3:
              vx = size.width;
              tx = texture.frame.right;
              break;
          }

          _vertices.add(new Point(vx, vy));
          _textureCoordinates.add(new Point(tx, ty));
          _colors.add(const Color(0xffffffff));
        }
      }

      // Build indices.
      _indices = <int>[];
      for (int y = 0; y < 3; y += 1) {
        for (int x = 0; x < 3; x += 1) {
          // Check if we should skip the middle rectangle.
          if (!drawCenterPart && x == 1 && y == 1)
            continue;

          // Add a rectangle (two triangles).
          int index = y * 4 + x;

          _indices.add(index);
          _indices.add(index + 1);
          _indices.add(index + 4);

          _indices.add(index + 1);
          _indices.add(index + 5);
          _indices.add(index + 4);
        }
      }
    }

    canvas.drawVertices(
      VertexMode.triangles,
      _vertices,
      _textureCoordinates,
      _colors,
      TransferMode.modulate,
      _indices,
      _cachedPaint
    );
  }
}
