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

import 'package:flutter/painting.dart';
import 'package:flutter/widgets.dart';

import '../flutter_test_alternative.dart';

void main() {
  test('TextSpan equals', () {
    const TextSpan a1 = TextSpan(text: 'a');
    const TextSpan a2 = TextSpan(text: 'a');
    const TextSpan b1 = TextSpan(children: <TextSpan>[ a1 ]);
    const TextSpan b2 = TextSpan(children: <TextSpan>[ a2 ]);
    const TextSpan c1 = TextSpan(text: null);
    const TextSpan c2 = TextSpan(text: null);

    expect(a1 == a2, isTrue);
    expect(b1 == b2, isTrue);
    expect(c1 == c2, isTrue);

    expect(a1 == b2, isFalse);
    expect(b1 == c2, isFalse);
    expect(c1 == a2, isFalse);

    expect(a1 == c2, isFalse);
    expect(b1 == a2, isFalse);
    expect(c1 == b2, isFalse);
  });

  test('TextSpan toStringDeep', () {
    const TextSpan test = TextSpan(
      text: 'a',
      style: TextStyle(
        fontSize: 10.0,
      ),
      children: <TextSpan>[
        TextSpan(
          text: 'b',
          children: <TextSpan>[
            TextSpan(),
          ],
        ),
        null,
        TextSpan(
          text: 'c',
        ),
      ],
    );
    expect(test.toStringDeep(), equals(
      'TextSpan:\n'
      '  inherit: true\n'
      '  size: 10.0\n'
      '  "a"\n'
      '  TextSpan:\n'
      '    "b"\n'
      '    TextSpan:\n'
      '      (empty)\n'
      '  <null child>\n'
      '  TextSpan:\n'
      '    "c"\n'
    ));
  });

  test('TextSpan toPlainText', () {
    const TextSpan textSpan = TextSpan(
      text: 'a',
      children: <TextSpan>[
        TextSpan(text: 'b'),
        TextSpan(text: 'c'),
      ],
    );
    expect(textSpan.toPlainText(), 'abc');
  });

  test('WidgetSpan toPlainText', () {
    const TextSpan textSpan = TextSpan(
      text: 'a',
      children: <InlineSpan>[
        TextSpan(text: 'b'),
        WidgetSpan(child: SizedBox(width: 10, height: 10)),
        TextSpan(text: 'c'),
      ],
    );
    expect(textSpan.toPlainText(), 'ab\uFFFCc');
  });

  test('TextSpan toPlainText with semanticsLabel', () {
    const TextSpan textSpan = TextSpan(
      text: 'a',
      children: <TextSpan>[
        TextSpan(text: 'b', semanticsLabel: 'foo'),
        TextSpan(text: 'c'),
      ],
    );
    expect(textSpan.toPlainText(), 'afooc');
    expect(textSpan.toPlainText(includeSemanticsLabels: false), 'abc');
  });

  test('TextSpan widget change test', () {
    const TextSpan textSpan1 = TextSpan(
      text: 'a',
      children: <InlineSpan>[
        TextSpan(text: 'b'),
        WidgetSpan(child: SizedBox(width: 10, height: 10)),
        TextSpan(text: 'c'),
      ],
    );

    const TextSpan textSpan2 = TextSpan(
      text: 'a',
      children: <InlineSpan>[
        TextSpan(text: 'b'),
        WidgetSpan(child: SizedBox(width: 10, height: 10)),
        TextSpan(text: 'c'),
      ],
    );

    const TextSpan textSpan3 = TextSpan(
      text: 'a',
      children: <InlineSpan>[
        TextSpan(text: 'b'),
        WidgetSpan(child: SizedBox(width: 11, height: 10)),
        TextSpan(text: 'c'),
      ],
    );

    const TextSpan textSpan4 = TextSpan(
      text: 'a',
      children: <InlineSpan>[
        TextSpan(text: 'b'),
        WidgetSpan(child: Text('test')),
        TextSpan(text: 'c'),
      ],
    );

    const TextSpan textSpan5 = TextSpan(
      text: 'a',
      children: <InlineSpan>[
        TextSpan(text: 'b'),
        WidgetSpan(child: Text('different!')),
        TextSpan(text: 'c'),
      ],
    );

    const TextSpan textSpan6 = TextSpan(
      text: 'a',
      children: <InlineSpan>[
        TextSpan(text: 'b'),
        WidgetSpan(
          child: SizedBox(width: 10, height: 10),
          alignment: PlaceholderAlignment.top,
        ),
        TextSpan(text: 'c'),
      ],
    );

    expect(textSpan1.compareTo(textSpan3), RenderComparison.layout);
    expect(textSpan1.compareTo(textSpan4), RenderComparison.layout);
    expect(textSpan1.compareTo(textSpan1), RenderComparison.identical);
    expect(textSpan2.compareTo(textSpan2), RenderComparison.identical);
    expect(textSpan3.compareTo(textSpan3), RenderComparison.identical);
    expect(textSpan2.compareTo(textSpan3), RenderComparison.layout);
    expect(textSpan4.compareTo(textSpan5), RenderComparison.layout);
    expect(textSpan3.compareTo(textSpan5), RenderComparison.layout);
    expect(textSpan2.compareTo(textSpan5), RenderComparison.layout);
    expect(textSpan1.compareTo(textSpan5), RenderComparison.layout);
    expect(textSpan1.compareTo(textSpan6), RenderComparison.layout);
  });

  test('TextSpan nested widget change test', () {
    const TextSpan textSpan1 = TextSpan(
      text: 'a',
      children: <InlineSpan>[
        TextSpan(text: 'b'),
        WidgetSpan(
          child: Text.rich(
            TextSpan(
              children: <InlineSpan>[
                WidgetSpan(child: SizedBox(width: 10, height: 10)),
                TextSpan(text: 'The sky is falling :)'),
              ],
            ),
          ),
        ),
        TextSpan(text: 'c'),
      ],
    );

    const TextSpan textSpan2 = TextSpan(
      text: 'a',
      children: <InlineSpan>[
        TextSpan(text: 'b'),
        WidgetSpan(
          child: Text.rich(
            TextSpan(
              children: <InlineSpan>[
                WidgetSpan(child: SizedBox(width: 10, height: 11)),
                TextSpan(text: 'The sky is falling :)'),
              ],
            ),
          ),
        ),
        TextSpan(text: 'c'),
      ],
    );

    expect(textSpan1.compareTo(textSpan2), RenderComparison.layout);
    expect(textSpan1.compareTo(textSpan1), RenderComparison.identical);
    expect(textSpan2.compareTo(textSpan2), RenderComparison.identical);
  });

  test('GetSpanForPosition with WidgetSpan', () {
    const TextSpan textSpan = TextSpan(
      text: 'a',
      children: <InlineSpan>[
        TextSpan(text: 'b'),
        WidgetSpan(
          child: Text.rich(
            TextSpan(
              children: <InlineSpan>[
                WidgetSpan(child: SizedBox(width: 10, height: 10)),
                TextSpan(text: 'The sky is falling :)'),
              ],
            ),
          ),
        ),
        TextSpan(text: 'c'),
      ],
    );

    expect(textSpan.getSpanForPosition(const TextPosition(offset: 0)).runtimeType, TextSpan);
    expect(textSpan.getSpanForPosition(const TextPosition(offset: 1)).runtimeType, TextSpan);
    expect(textSpan.getSpanForPosition(const TextPosition(offset: 2)).runtimeType, WidgetSpan);
    expect(textSpan.getSpanForPosition(const TextPosition(offset: 3)).runtimeType, TextSpan);
  });

  test('TextSpan with a null child should throw FlutterError', () {
    const TextSpan text = TextSpan(
      text: 'foo bar',
      children: <InlineSpan>[
        null,
      ],
    );
    FlutterError error;
    try {
      text.computeToPlainText(StringBuffer());
    } on FlutterError catch (e) {
      error = e;
    }
    expect(error, isNotNull);
    expect(error.toStringDeep(),
      'FlutterError\n'
      '   TextSpan contains a null child.\n'
      '   A TextSpan object with a non-null child list should not have any\n'
      '   nulls in its child list.\n'
      '   The full text in question was:\n'
      '     TextSpan("foo bar")\n'
    );
  });

}