/*
 * Copyright (C) 2023 tajaouart.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contact: developer@tajaouart.com
 */

import 'dart:async';
import 'package:flutter/material.dart';

import 'chart_painter.dart';
import 'point.dart';

class RealTimeChart extends StatefulWidget {
  const RealTimeChart({
    required this.stream,
    required this.maxY,
    required this.valueDesc,
    required this.valueType,
    this.maxValueDesc,
    this.maxValueType,
    this.speed = 1,
    super.key,
  });

  final Stream<double> stream;
  final double maxY;
  final String valueDesc;
  final String valueType;
  final String? maxValueDesc;
  final String? maxValueType;
  final Duration updateDelay = const Duration(milliseconds: 100);

  final int speed;

  @override
  State<RealTimeChart> createState() => _RealTimeChartState();
}

class _RealTimeChartState extends State<RealTimeChart> {
  StreamSubscription<double>? _streamSubscription;

  List<Point<double>> data = [];

  Timer? timer;

  @override
  void initState() {
    super.initState();

    _streamSubscription = widget.stream.listen(_streamListener);

    double canvasWidth = 200;

    // Start a periodic timer to update the data for visualization
    timer = Timer.periodic(widget.updateDelay, (_) {
      // delete data that is no longer displayed on the graph.
      data.removeWhere((element) => element.x < canvasWidth * -1.5);

      // Clone the data to avoid modifying the original list while iterating
      List<Point<double>> newData = data.map((e) => e).toList();

      // Increment the x value of each data point
      for (var element in newData) {
        element.x = element.x - widget.speed;
      }

      // Trigger a rebuild with the updated data
      setState(() {
        data = newData;
      });
    });
  }

  @override
  void dispose() {
    if (timer != null) timer!.cancel();
    _streamSubscription?.cancel();
    super.dispose();
  }

  void _streamListener(double value) {
    // Insert the new data point in the beginning of the list
    data.insert(0, Point(0, value));
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Container(
          width: 200,
          height: 200,
          decoration: BoxDecoration(
            border: Border.all(
              color: Theme.of(context).colorScheme.primary,
            ),
          ),
          child: ClipRect(
            child: GridPaper(
              divisions: 6,
              subdivisions: 1,
              interval: 200.0,
              color: Theme.of(context).colorScheme.primary,
              child: CustomPaint(
                painter: ChartPainter(
                  data: data,
                  maxY: widget.maxY,
                  context: context,
                ),
              ),
            ),
          ),
        ),
        const SizedBox(
          height: 8,
        ),
        Text(
          widget.valueDesc,
          style: const TextStyle(fontWeight: FontWeight.bold),
        ),
        (data.isNotEmpty)
            ? Text(
                '${data[0].y.toString().split('.').first} ${widget.valueType}',
              )
            : const SizedBox(
                height: 20,
                width: 20,
                child: CircularProgressIndicator(),
              ),
        const SizedBox(
          height: 8,
        ),
        if (widget.maxValueDesc != null)
          Text(
            widget.maxValueDesc!,
            style: const TextStyle(fontWeight: FontWeight.bold),
          ),
        if (widget.maxValueType != null)
          Text(
            '${widget.maxY.toString().split('.').first} ${widget.maxValueType!}',
          )
      ],
    );
  }
}
