import os

base_dir = 'dragon_charts_flutter'
lib_dir = os.path.join(base_dir, 'lib')
src_dir = os.path.join(lib_dir, 'src')
example_dir = os.path.join(base_dir, 'example')

directories = [base_dir, lib_dir, src_dir, example_dir]

for directory in directories:
    os.makedirs(directory, exist_ok=True)

files = {
    'lib/dragon_charts_flutter.dart': """
library dragon_charts_flutter;

export 'src/chart_data.dart';
export 'src/chart_element.dart';
export 'src/chart_grid_lines.dart';
export 'src/chart_axis_labels.dart';
export 'src/chart_data_series.dart';
export 'src/chart_data_transform.dart';
export 'src/custom_line_chart.dart';
export 'src/chart_tooltip.dart';
""",
    'lib/src/chart_data.dart': """
class ChartData {
  final double x;
  final double y;

  ChartData({required this.x, required this.y});
}
""",
    'lib/src/chart_element.dart': """
import 'package:flutter/material.dart';
import 'chart_data_transform.dart';

abstract class ChartElement {
  void paint(Canvas canvas, Size size, ChartDataTransform transform, double animation);
}
""",
    'lib/src/chart_grid_lines.dart': """
import 'package:flutter/material.dart';
import 'chart_element.dart';
import 'chart_data_transform.dart';

class ChartGridLines extends ChartElement {
  final bool isVertical;
  final int count;

  ChartGridLines({required this.isVertical, required this.count});

  @override
  void paint(Canvas canvas, Size size, ChartDataTransform transform, double animation) {
    Paint gridPaint = Paint()
      ..color = Colors.grey.withOpacity(0.2)
      ..strokeWidth = 1.0;

    if (isVertical) {
      for (double i = 0; i <= count; i++) {
        double x = i * size.width / count;
        canvas.drawLine(Offset(x, 0), Offset(x, size.height), gridPaint);
      }
    } else {
      for (double i = 0; i <= count; i++) {
        double y = i * size.height / count;
        canvas.drawLine(Offset(0, y), Offset(size.width, y), gridPaint);
      }
    }
  }
}
""",
    'lib/src/chart_axis_labels.dart': """
import 'package:flutter/material.dart';
import 'chart_element.dart';
import 'chart_data_transform.dart';

class ChartAxisLabels extends ChartElement {
  final bool isVertical;
  final int count;
  final String Function(double value) labelBuilder;
  final double reservedExtent;

  ChartAxisLabels({
    required this.isVertical,
    required this.count,
    required this.labelBuilder,
    this.reservedExtent = 30.0,
  });

  @override
  void paint(Canvas canvas, Size size, ChartDataTransform transform, double animation) {
    if (isVertical) {
      for (double i = 0; i <= count; i++) {
        double y = i * size.height / count;
        TextPainter textPainter = TextPainter(
          text: TextSpan(
              text: labelBuilder(transform.invertY(y)),
              style: const TextStyle(color: Colors.grey, fontSize: 10)),
          textDirection: TextDirection.ltr,
        );
        textPainter.layout();

        if (i == 0 || (y - textPainter.height / 2) >= reservedExtent * i) {
          textPainter.paint(canvas, Offset(-textPainter.width - 5, y - textPainter.height / 2));
        }
      }
    } else {
      for (double i = 0; i <= count; i++) {
        double x = i * size.width / count;
        TextPainter textPainter = TextPainter(
          text: TextSpan(
              text: labelBuilder(transform.invertX(x)),
              style: const TextStyle(color: Colors.grey, fontSize: 10)),
          textDirection: TextDirection.ltr,
        );
        textPainter.layout();

        if (i == 0 || (x - textPainter.width / 2) >= reservedExtent * i) {
          textPainter.paint(canvas, Offset(x - textPainter.width / 2, size.height + 5));
        }
      }
    }
  }
}
""",
    'lib/src/chart_data_series.dart': """
import 'package:flutter/material.dart';
import 'chart_element.dart';
import 'chart_data.dart';
import 'chart_data_transform.dart';
import 'dart:math';

enum LineType { straight, bezier }

class ChartDataSeries extends ChartElement {
  final List<ChartData> data;
  final Color color;
  final LineType lineType;

  ChartDataSeries({
    required this.data,
    required this.color,
    this.lineType = LineType.straight,
  });

  ChartDataSeries animateTo(ChartDataSeries newDataSeries, double animationValue) {
    List<ChartData> interpolatedData = [];

    for (int i = 0; i < min(data.length, newDataSeries.data.length); i++) {
      double oldY = data[i].y;
      double newY = newDataSeries.data[i].y;
      double interpolatedY = oldY + (newY - oldY) * animationValue;

      double oldX = data[i].x;
      double newX = newDataSeries.data[i].x;
      double interpolatedX = oldX + (newX - oldX) * animationValue;

      interpolatedData.add(ChartData(
        x: interpolatedX,
        y: interpolatedY,
      ));
    }

    return ChartDataSeries(
      data: interpolatedData,
      color: color,
      lineType: lineType,
    );
  }

  @override
  void paint(Canvas canvas, Size size, ChartDataTransform transform, double animation) {
    Paint linePaint = Paint()
      ..color = color
      ..strokeWidth = 2.0
      ..style = PaintingStyle.stroke;

    Paint pointPaint = Paint()
      ..color = color
      ..style = PaintingStyle.fill;

    Path path = Path();
    bool first = true;

    if (lineType == LineType.straight) {
      for (var point in data) {
        double x = transform.transformX(point.x);
        double y = transform.transformY(point.y);

        if (first) {
          path.moveTo(x, y);
          first = false;
        } else {
          path.lineTo(x, y);
        }

        canvas.drawCircle(Offset(x, y), 4.0, pointPaint);
      }
    } else if (lineType == LineType.bezier) {
      if (data.isNotEmpty) {
        path.moveTo(transform.transformX(data[0].x), transform.transformY(data[0].y));

        for (int i = 0; i < data.length - 1; i++) {
          double x1 = transform.transformX(data[i].x);
          double y1 = transform.transformY(data[i].y);
          double x2 = transform.transformX(data[i + 1].x);
          double y2 = transform.transformY(data[i + 1].y);

          double controlPointX1 = x1 + (x2 - x1) / 3;
          double controlPointY1 = y1;
          double controlPointX2 = x1 + 2 * (x2 - x1) / 3;
          double controlPointY2 = y2;

          path.cubicTo(controlPointX1, controlPointY1, controlPointX2, controlPointY2, x2, y2);

          canvas.drawCircle(Offset(x1, y1), 4.0, pointPaint);
        }
        canvas.drawCircle(Offset(transform.transformX(data.last.x), transform.transformY(data.last.y)), 4.0, pointPaint);
      }
    }
    canvas.drawPath(path, linePaint);
  }
}
""",
    'lib/src/chart_data_transform.dart': """
class ChartDataTransform {
  final double minX, maxX, minY, maxY;
  final double width, height;

  ChartDataTransform({
    required this.minX,
    required this.maxX,
    required this.minY,
    required this.maxY,
    required this.width,
    required this.height,
  });

  double transformX(double x) => (x - minX) / (maxX - minX) * width;

  double transformY(double y) => height - (y - minY) / (maxY - minY) * height;

  double invertX(double dx) => minX + (dx / width) * (maxX - minX);

  double invertY(double dy) => minY + (1 - dy / height) * (maxY - minY);
}
""",
    'lib/src/custom_line_chart.dart': """
import 'package:flutter/material.dart';
import 'chart_element.dart';
import 'chart_data_series.dart';
import 'chart_data_transform.dart';
import 'chart_tooltip.dart';

class GraphExtent {
  final bool auto;
  final double padding;
  final double? min;
  final double? max;

  const GraphExtent({
    this.auto = true,
    this.padding = 0.1,
    this.min,
    this.max,
  });

  const GraphExtent.tight() : this(auto: true, padding: 0.0);
}

class CustomLineChart extends StatefulWidget {
  final List<ChartElement> elements;
  final Duration animationDuration;
  final Widget Function(BuildContext, List<ChartData>) tooltipBuilder;
  final GraphExtent domainExtent;
  final GraphExtent rangeExtent;
  final Color backgroundColor;

  const CustomLineChart({
    super.key,
    required this.elements,
    required this.tooltipBuilder,
    this.animationDuration = const Duration(milliseconds: 500),
    this.domainExtent = const GraphExtent(auto: true, padding: 0.1),
    this.rangeExtent = const GraphExtent(auto: true, padding: 0.1),
    this.backgroundColor = Colors.black,
  });

  @override
  _CustomLineChartState createState() => _CustomLineChartState();
}

class _CustomLineChartState extends State<CustomLineChart> with SingleTickerProviderStateMixin {
  OverlayEntry? _tooltipOverlay;
  Offset? _hoverPosition;
  List<Offset>? _highlightedPoints;
  List<Color> _highlightedColors = [];
  late AnimationController _controller;
  late Animation<double> _animation;

  List<ChartElement> oldElements = [];
  List<ChartElement> currentElements = [];

  double minX = double.infinity;
  double maxX = double.negativeInfinity;
  double minY = double.infinity;
  double maxY = double.negativeInfinity;

  late Animation<double> minXAnimation;
  late Animation<double> maxXAnimation;
  late Animation<double> minYAnimation;
  late Animation<double> maxYAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: widget.animationDuration);
    _animation = CurvedAnimation(parent: _controller, curve: Curves.easeInOut);
    _controller.addListener(() {
      setState(() {});
    });
    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        setState(() {
          oldElements = List.from(widget.elements);
        });
      }
    });
    oldElements = List.from(widget.elements);
    currentElements = List.from(widget.elements);
    _updateDomainRange();
    _controller.forward();
  }

  @override
  void didUpdateWidget(covariant CustomLineChart oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.elements != widget.elements) {
      setState(() {
        oldElements = List.from(currentElements);
        currentElements = List.from(widget.elements);
        _controller.reset();
        _updateDomainRange();
        _controller.forward();
      });
    } else {
      _updateDomainRange();
    }
  }

  void _updateDomainRange() {
    double newMinX = double.infinity;
    double newMaxX = double.negativeInfinity;
    double newMinY = double.infinity;
    double newMaxY = double.negativeInfinity;

    for (var element in widget.elements) {
      if (element is ChartDataSeries) {
        for (var dataPoint in element.data) {
          double xValue = dataPoint.x;
          if (xValue < newMinX) newMinX = xValue;
          if (xValue > newMaxX) newMaxX = xValue;
          if (dataPoint.y < newMinY) newMinY = dataPoint.y;
          if (dataPoint.y > newMaxY) newMaxY = dataPoint.y;
        }
      }
    }

    if (widget.domainExtent.auto) {
      double domainPaddingValue = (newMaxX - newMinX) * widget.domainExtent.padding;
      newMinX -= domainPaddingValue;
      newMaxX += domainPaddingValue;
    } else {
      newMinX = widget.domainExtent.min ?? newMinX;
      newMaxX = widget.domainExtent.max ?? newMaxX;
    }

    if (widget.rangeExtent.auto) {
      double rangePaddingValue = (newMaxY - newMinY) * widget.rangeExtent.padding;
      newMinY -= rangePaddingValue;
      newMaxY += rangePaddingValue;
    } else {
      newMinY = widget.rangeExtent.min ?? newMinY;
      newMaxY = widget.rangeExtent.max ?? newMaxY;
    }

    minXAnimation = Tween<double>(begin: minX, end: newMinX).animate(_controller);
    maxXAnimation = Tween<double>(begin: maxX, end: newMaxX).animate(_controller);
    minYAnimation = Tween<double>(begin: minY, end: newMinY).animate(_controller);
    maxYAnimation = Tween<double>(begin: maxY, end: newMaxY).animate(_controller);

    minX = newMinX;
    maxX = newMaxX;
    minY = newMinY;
    maxY = newMaxY;
  }

  void _showTooltip(BuildContext context, Offset position, List<ChartData> dataPoints) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _hideTooltip();

      final RenderBox? renderBox = context.findRenderObject() as RenderBox?;
      final Size? size = renderBox?.size;

      if (size != null) {
        double left = position.dx + 10;
        double top = position.dy - 30;

        if (left + 100 > size.width) {
          left = size.width - 100;
        }
        if (top < 0) {
          top = 0;
        }

        _tooltipOverlay = OverlayEntry(
          builder: (context) => Positioned(
            left: left,
            top: top,
            child: Material(
              color: Colors.transparent,
              child: widget.tooltipBuilder(context, dataPoints),
            ),
          ),
        );

        Overlay.of(context).insert(_tooltipOverlay!);
      }
    });
  }

  void _hideTooltip() {
    if (_tooltipOverlay != null) {
      _tooltipOverlay!.remove();
      _tooltipOverlay = null;
    }
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        Size size = Size(constraints.maxWidth, constraints.maxHeight);

        ChartDataTransform transform = ChartDataTransform(
          minX: minXAnimation.value,
          maxX: maxXAnimation.value,
          minY: minYAnimation.value,
          maxY: maxYAnimation.value,
          width: size.width,
          height: size.height,
        );

        return Container(
          color: widget.backgroundColor,
          child: GestureDetector(
            onPanUpdate: (details) {
              setState(() {
                _hoverPosition = details.localPosition;
              });
            },
            child: MouseRegion(
              onHover: (details) {
                final localPosition = details.localPosition;
                List<ChartData> highlightedData = [];
                _highlightedPoints = [];
                _highlightedColors = [];
                for (var element in widget.elements) {
                  if (element is ChartDataSeries) {
                    for (var point in element.data) {
                      double x = transform.transformX(point.x);
                      double y = transform.transformY(point.y);
                      if ((Offset(x, y) - localPosition).distance < 10) {
                        highlightedData.add(point);
                        _highlightedPoints!.add(Offset(x, y));
                        _highlightedColors.add(element.color);
                      }
                    }
                  }
                }
                if (highlightedData.isNotEmpty) {
                  _showTooltip(context, localPosition, highlightedData);
                } else {
                  _hideTooltip();
                }
                setState(() {
                  _hoverPosition = localPosition;
                });
              },
              onExit: (details) {
                _hideTooltip();
                setState(() {
                  _hoverPosition = null;
                  _highlightedPoints = null;
                  _highlightedColors = [];
                });
              },
              child: AnimatedBuilder(
                animation: _animation,
                builder: (context, child) {
                  List<ChartElement> animatedElements = [];
                  for (int i = 0; i < currentElements.length; i++) {
                    if (currentElements[i] is ChartDataSeries && oldElements[i] is ChartDataSeries) {
                      animatedElements.add((oldElements[i] as ChartDataSeries).animateTo(currentElements[i] as ChartDataSeries, _animation.value));
                    } else {
                      animatedElements.add(currentElements[i]);
                    }
                  }
                  return CustomPaint(
                    size: size,
                    painter: _LineChartPainter(
                      elements: animatedElements,
                      transform: transform,
                      highlightedPoints: _highlightedPoints,
                      highlightedColors: _highlightedColors,
                      animation: _animation.value,
                    ),
                  );
                },
              ),
            ),
          ),
        );
      },
    );
  }

  @override
  void dispose() {
    _hideTooltip();
    _controller.dispose();
    super.dispose();
  }
}

class _LineChartPainter extends CustomPainter {
  final List<ChartElement> elements;
  final ChartDataTransform transform;
  final List<Offset>? highlightedPoints;
  final List<Color> highlightedColors;
  final double animation;

  _LineChartPainter({
    required this.elements,
    required this.transform,
    required this.highlightedPoints,
    required this.highlightedColors,
    required this.animation,
  });

  @override
  void paint(Canvas canvas, Size size) {
    for (var element in elements) {
      element.paint(canvas, size, transform, animation);
    }

    if (highlightedPoints != null) {
      for (int i = 0; i < highlightedPoints!.length; i++) {
        var point = highlightedPoints![i];
        var color = highlightedColors[i];
        Paint highlightPaint = Paint()
          ..color = color
          ..style = PaintingStyle.fill;
        canvas.drawCircle(point, 6.0, highlightPaint);
      }
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}
""",
    'lib/src/chart_tooltip.dart': """
import 'package:flutter/material.dart';
import 'chart_data.dart';

class ChartTooltip extends StatelessWidget {
  final List<ChartData> dataPoints;
  final Color backgroundColor;

  const ChartTooltip({
    Key? key,
    required this.dataPoints,
    this.backgroundColor = Colors.black,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(8),
      decoration: BoxDecoration(
        color: backgroundColor,
        borderRadius: BorderRadius.circular(4.0),
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: dataPoints.map((data) {
          return Row(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              const Icon(Icons.circle, color: Colors.white),
              Text(
                '(${data.x}, ${data.y})',
                style: const TextStyle(color: Colors.white, fontSize: 12),
              ),
              const SizedBox(height: 36),
            ],
          );
        }).toList(),
      ),
    );
  }
}
""",
    'example/main.dart': """
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:dragon_charts_flutter/dragon_charts_flutter.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<ChartData> data1 = [];
  List<ChartData> data2 = [];

  @override
  void initState() {
    for (int i = 0; i < 10; i++) {
      data1.add(ChartData(x: i.toDouble(), y: Random().nextDouble()));
      data2.add(ChartData(x: i.toDouble(), y: Random().nextDouble()));
    }

    super.initState();
    Timer.periodic(const Duration(seconds: 2), (timer) {
      setState(() {
        data2 = data2.map((element) {
          return ChartData(x: element.x, y: Random().nextDouble());
        }).toList();

        if (Random().nextBool()) {
          data2.add(ChartData(x: data2.last.x + 1, y: Random().nextDouble()));
          data1.add(ChartData(x: data1.last.x + 1, y: Random().nextDouble()));
        }
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Custom Line Chart with Animation')),
        body: Padding(
          padding: const EdgeInsets.all(32),
          child: CustomLineChart(
            domainExtent: const GraphExtent.tight(),
            elements: [
              ChartGridLines(isVertical: false, count: 5),
              ChartAxisLabels(isVertical: true, count: 5, labelBuilder: (value) => value.toStringAsFixed(2)),
              ChartAxisLabels(isVertical: false, count: 5, labelBuilder: (value) => value.toStringAsFixed(2)),
              ChartDataSeries(data: data1, color: Colors.blue),
              ChartDataSeries(data: data2, color: Colors.red, lineType: LineType.bezier),
            ],
            tooltipBuilder: (context, dataPoints) {
              return ChartTooltip(dataPoints: dataPoints, backgroundColor: Colors.black);
            },
          ),
        ),
      ),
    );
  }
}
"""
}

for file_path, content in files.items():
    with open(os.path.join(base_dir, file_path), 'w') as file:
        file.write(content)

print("Flutter package structure created successfully.")
