import 'package:flutter/material.dart';

class MonoWidget extends StatelessWidget {
  final double progress;
  final String lang;
  final double fontSize;
  final Color? background;
  final Color lineColor;
  final Color infoTextColor;
  final TextStyle style;

  const MonoWidget({
    Key? key,
    required this.lang,
    required this.progress,
    required this.style,
    this.fontSize = 14,
    this.background,
    this.infoTextColor = Colors.redAccent,
    this.lineColor = Colors.blue,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    if (lang == 'oz' || lang == '') {
      final paint = _OzPainter(
        progress: progress,
        lineColor: lineColor,
        infoTextColor: infoTextColor,
        style: style,
      );
      return LayoutBuilder(
        builder: (_, constraints) => Container(
          width: paint.width,
          height: paint.height,
          color: background,
          child: CustomPaint(painter: paint),
        ),
      );
    }
    if (lang == 'uz') {
      final paint = _UzPainter(
        progress: progress,
        lineColor: lineColor,
        infoTextColor: infoTextColor,
        style: style,
      );
      return LayoutBuilder(
        builder: (_, constraints) => Container(
          width: paint.width,
          height: paint.height,
          color: background,
          child: CustomPaint(painter: paint),
        ),
      );
    } else {
      final paint = _RuPainter(
        progress: progress,
        lineColor: lineColor,
        infoTextColor: infoTextColor,
        style: style,
      );
      return LayoutBuilder(
        builder: (_, constraints) => Container(
          width: paint.width,
          height: paint.height,
          color: background,
          child: CustomPaint(painter: paint),
        ),
      );
    }
  }
}

class _OzPainter extends CustomPainter {
  late double width;
  late double height;
  late double fontSize;
  late Offset offset;

  final _time1 = 0.6;
  final _time2 = 0.8;
  final text = "Jarimasiz hayot go'zal";
  final replaceText = "Xavotirsiz";

  final double progress;
  final Color lineColor;
  final Color infoTextColor;
  final TextStyle style;

  _OzPainter({
    this.progress = 0,
    required this.lineColor,
    required this.infoTextColor,
    required this.style,
  }) {
    this.fontSize = this.style.fontSize ?? 14;
    this.offset = Offset(0, this.fontSize);
    final tp = TextPainter(
      text: TextSpan(style: style, text: text),
      textDirection: TextDirection.ltr,
    );
    tp.layout();
    width = tp.width;
    height = tp.height * 2;
  }

  @override
  void paint(Canvas canvas, Size size) {
    _paintText(canvas);
    if (progress > _time1) _paintLine(canvas);
    if (progress > _time2) _paintText2(canvas);
  }

  void _paintText(Canvas canvas) {
    int index = text.length * progress ~/ _time1;
    final span = TextSpan(
      style: style,
      text: text.substring(0, index > text.length ? text.length : index),
    );
    final tp = TextPainter(
      text: span,
      textDirection: TextDirection.ltr,
    );
    tp.layout();
    width = tp.width;
    tp.paint(canvas, offset);
  }

  void _paintLine(Canvas canvas) {
    double k = (progress - _time1) / (1 - _time1) * 3;
    if (k > 1) k = 1;

    final span = TextSpan(style: style, text: replaceText);
    final tp = TextPainter(text: span, textDirection: TextDirection.ltr);
    tp.layout();
    double lnWidth = tp.width * 0.96;
    double lnHeight = tp.height * 0.3;

    var paint = Paint()
      ..color = lineColor
      ..strokeWidth = 3
      ..style = PaintingStyle.stroke;

    var path = Path();
    path.moveTo(offset.dx, offset.dy + fontSize);
    path.lineTo(
      offset.dx + lnWidth * k,
      offset.dy + fontSize - lnHeight * k,
    );
    canvas.drawPath(path, paint);
  }

  void _paintText2(Canvas canvas) {
    final k = (progress - _time2) / (1 - _time2);
    int index = replaceText.length * k ~/ _time2;
    final span = TextSpan(
      style: style.copyWith(color: infoTextColor),
      text: replaceText.substring(
        0,
        index > replaceText.length ? replaceText.length : index,
      ),
    );
    final tp = TextPainter(text: span, textDirection: TextDirection.ltr);
    tp.layout();
    tp.paint(canvas, Offset(offset.dx, offset.dy - fontSize));
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

class _UzPainter extends CustomPainter {
  late double width;
  late double height;
  late double fontSize;
  late Offset offset;

  final _time1 = 0.6;
  final _time2 = 0.8;
  final text = "Жаримасиз ҳаёт гўзал";
  final replaceText = "Xавотирсиз";

  final double progress;
  final Color lineColor;
  final Color infoTextColor;
  final TextStyle style;

  _UzPainter({
    this.progress = 0,
    required this.lineColor,
    required this.infoTextColor,
    required this.style,
  }) {
    this.fontSize = this.style.fontSize ?? 14;
    this.offset = Offset(0, this.fontSize);
    final tp = TextPainter(
      text: TextSpan(style: style, text: text),
      textDirection: TextDirection.ltr,
    );
    tp.layout();
    width = tp.width;
    height = tp.height * 2;
  }

  @override
  void paint(Canvas canvas, Size size) {
    _paintText(canvas);
    if (progress > _time1) _paintLine(canvas);
    if (progress > _time2) _paintText2(canvas);
  }

  void _paintText(Canvas canvas) {
    int index = text.length * progress ~/ _time1;
    final span = TextSpan(
      style: style,
      text: text.substring(0, index > text.length ? text.length : index),
    );
    final tp = TextPainter(
      text: span,
      textDirection: TextDirection.ltr,
    );
    tp.layout();
    width = tp.width;
    tp.paint(canvas, offset);
  }

  void _paintLine(Canvas canvas) {
    double k = (progress - _time1) / (1 - _time1) * 3;
    if (k > 1) k = 1;

    final span = TextSpan(style: style, text: replaceText);
    final tp = TextPainter(text: span, textDirection: TextDirection.ltr);
    tp.layout();
    double lnWidth = tp.width * 0.96;
    double lnHeight = tp.height * 0.3;

    var paint = Paint()
      ..color = lineColor
      ..strokeWidth = 3
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    var path = Path();
    path.moveTo(offset.dx, offset.dy + fontSize);
    path.lineTo(
      offset.dx + lnWidth * k,
      offset.dy + fontSize - lnHeight * k,
    );
    canvas.drawPath(path, paint);
  }

  void _paintText2(Canvas canvas) {
    final k = (progress - _time2) / (1 - _time2);
    int index = replaceText.length * k ~/ _time2;
    final span = TextSpan(
      style: style.copyWith(color: infoTextColor),
      text: replaceText.substring(
        0,
        index > replaceText.length ? replaceText.length : index,
      ),
    );
    final tp = TextPainter(text: span, textDirection: TextDirection.ltr);
    tp.layout();
    tp.paint(canvas, Offset(offset.dx, offset.dy - fontSize));
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

class _RuPainter extends CustomPainter {
  late double width;
  late double height;
  late double fontSize;
  late Offset offset;

  final _time1 = 0.6;
  final _time2 = 0.8;
  final text = "Жизнь без штрафa прекрасна";
  final replaceText = "страхa";

  final double progress;
  final Color lineColor;
  final Color infoTextColor;
  final TextStyle style;

  _RuPainter({
    this.progress = 0,
    required this.lineColor,
    required this.infoTextColor,
    required this.style,
  }) {
    this.fontSize = this.style.fontSize ?? 14;
    this.offset = Offset(0, this.fontSize);
    final tp = TextPainter(
      text: TextSpan(style: style, text: text),
      textDirection: TextDirection.ltr,
    );
    tp.layout();
    width = tp.width;
    height = tp.height * 2;
  }

  @override
  void paint(Canvas canvas, Size size) {
    _paintText(canvas);
    if (progress > _time1) _paintLine(canvas);
    if (progress > _time2) _paintText2(canvas);
  }

  void _paintText(Canvas canvas) {
    int index = text.length * progress ~/ _time1;
    final span = TextSpan(
      style: style,
      text: text.substring(0, index > text.length ? text.length : index),
    );
    final tp = TextPainter(
      text: span,
      textDirection: TextDirection.ltr,
    );
    tp.layout();
    width = tp.width;
    tp.paint(canvas, offset);
  }

  void _paintLine(Canvas canvas) {
    double k = (progress - _time1) / (1 - _time1) * 3;
    if (k > 1) k = 1;

    var span = TextSpan(style: style, text: "Жизнь без ");
    var tp = TextPainter(text: span, textDirection: TextDirection.ltr)
      ..layout();
    final marginLeft = tp.width * 0.96;
    span = TextSpan(style: style, text: "штрафa");
    tp = TextPainter(text: span, textDirection: TextDirection.ltr)..layout();
    final lnWidth = tp.width * 0.96;
    final lnHeight = tp.height * 0.3;

    var paint = Paint()
      ..color = lineColor
      ..strokeWidth = 3
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    var path = Path();
    path.moveTo(offset.dx + marginLeft, offset.dy + fontSize);
    path.lineTo(
      offset.dx + marginLeft + lnWidth * k,
      offset.dy + fontSize - lnHeight * k,
    );
    canvas.drawPath(path, paint);
  }

  void _paintText2(Canvas canvas) {
    final k = (progress - _time2) / (1 - _time2);
    int index = replaceText.length * k ~/ _time2;
    var span = TextSpan(style: style, text: "Жизнь без ");
    var tp = TextPainter(text: span, textDirection: TextDirection.ltr)
      ..layout();
    final marginLeft = tp.width * 0.96;
    span = TextSpan(
      style: style.copyWith(color: infoTextColor),
      text: replaceText.substring(
        0,
        index > replaceText.length ? replaceText.length : index,
      ),
    );
    tp = TextPainter(text: span, textDirection: TextDirection.ltr)..layout();
    tp.paint(canvas, Offset(offset.dx + marginLeft, offset.dy - fontSize));
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
