import 'dart:convert';
import 'dart:math';
import 'dart:ui';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:sport/bean/sport_step.dart';
import 'package:sport/services/Converter.dart';
import 'package:sport/services/api/inject_api.dart';
import 'package:sport/services/api/resp.dart';
import 'package:sport/utils/date.dart';
import 'package:sport/widgets/appbar.dart';
import 'package:sport/widgets/chart.dart';
import 'package:sport/widgets/misc.dart';
import 'package:sport/widgets/persistent_header.dart';
import 'package:sport/widgets/space.dart';

const Color _color = Color(0xffFF5B1D);

class StepPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _PageState();
}

class _PageState extends State<StepPage> with TickerProviderStateMixin, InjectApi, InjectLoginApi {
  final List<String> _TABS = ["日", "周", "月", "年"];
  ValueNotifier<String> _tab = ValueNotifier<String>("日");
  ValueNotifier<SportStep> _valueNotifierSportDetail = ValueNotifier(null);
  ValueNotifier<DateTime> _valueNotifierDate = ValueNotifier(DateTime.now());
  PageController _pageController;

  @override
  void initState() {
    super.initState();
    _pageController = PageController(initialPage: 0);
    var now = DateTime.now();
    // api.addDaily(
    //     step: Random().nextInt(255), distance: Random().nextInt(255) * 10, time: '${now.year}-${now.month}-${now.day} ${now.hour}:${now.minute}:${now.second}');
    // api.addDaily(data: json.encode([[Random().nextInt(255), Random().nextInt(255),DateTime.now().millisecondsSinceEpoch ~/ 1000 - Random().nextInt(1000)],[Random().nextInt(255), Random().nextInt(255),DateTime.now().millisecondsSinceEpoch ~/ 1000 - Random().nextInt(1000)],[Random().nextInt(255), Random().nextInt(255),DateTime.now().millisecondsSinceEpoch ~/ 1000 - Random().nextInt(1000)]]));
    changeTab();
  }

  @override
  void dispose() {
    _pageController?.dispose();
    super.dispose();
    _tab?.dispose();
    _valueNotifierSportDetail?.dispose();
    _valueNotifierDate?.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xfff9f9f9),
      body: CustomScrollView(
        slivers: <Widget>[
          buildSliverAppBar(context, "运动步数"),
          SliverPersistentHeader(
            delegate: PersistentHeader(
                min: 70,
                max: 70,
                child: Container(
                  color: Colors.white,
                  padding: EdgeInsets.only(top: 20),
                  child: ValueListenableBuilder(
                    valueListenable: _tab,
                    builder: (BuildContext context, String value, Widget child) {
                      return Column(
                        children: <Widget>[
                          Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            mainAxisSize: MainAxisSize.min,
                            children: ["日", "/", "周", "/", "月", "/", "年"]
                                .map((e) => e == "/"
                                    ? Container(
                                        margin: EdgeInsets.symmetric(horizontal: 6.0),
                                        color: Color(0xffdcdcdc),
                                        width: 0.5,
                                        height: 14,
                                        transform: Matrix4.rotationZ(0.35),
                                      )
                                    : InkWell(
                                        onTap: () {
                                          if (_valueNotifierSportDetail.value == null) return;
                                          _tab.value = e;
                                          _valueNotifierDate.value = DateTime.now();
                                          _pageController.jumpToPage(0);
                                          // _pageController= PageController(initialPage: 0);
                                          changeTab();
                                        },
                                        child: Container(
                                          margin: EdgeInsets.symmetric(horizontal: 12.0),
                                          padding: EdgeInsets.all(8.0),
                                          decoration: value == e ? BoxDecoration(color: _color, shape: BoxShape.circle) : null,
                                          child: Text(
                                            "$e",
                                            style: value == e
                                                ? Theme.of(context).textTheme.subtitle1.copyWith(color: Colors.white)
                                                : Theme.of(context).textTheme.subtitle1,
                                          ),
                                        )))
                                .toList(),
                          ),
                        ],
                      );
                    },
                  ),
                )),
            pinned: true,
          ),
          SliverToBoxAdapter(
            child: _buildDataWidget(),
          ),
        ],
      ),
    );
  }

  Widget _buildDataWidget() {
    return Column(
      children: <Widget>[
        Container(
          margin: EdgeInsets.fromLTRB(12.0, 6.0, 12.0, 18.0),
          padding: EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 12.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              ValueListenableBuilder<SportStep>(
                valueListenable: _valueNotifierSportDetail,
                builder: (_, _value, ___) => Text(
                  toType() == 0 ? "日总步数" : "日均步数",
                  style: Theme.of(context).textTheme.bodyText1,
                ),
              ),
              ValueListenableBuilder<SportStep>(
                valueListenable: _valueNotifierSportDetail,
                builder: (_, _value, ___) => Text(
                  "${_value?.sum?.stepDayAvg ?? _value?.sum?.step ?? 0}",
                  style: Theme.of(context).textTheme.headline1.copyWith(fontSize: 25),
                ),
              ),
              ValueListenableBuilder<DateTime>(
                  valueListenable: _valueNotifierDate,
                  builder: (_, time, ___) {
                    int type = toType();
                    String text = "";
                    if (type == 0) {
                      text = "${time.year}.${'${time.month}'.padLeft(2, '0')}.${'${time.day}'.padLeft(2, '0')}  6:00 - 24:00 ";
                    } else if (type == 1) {
                      DateTime start = DateTime(time.year, time.month, time.day - time.weekday + 1);
                      DateTime end = DateTime(time.year, time.month, time.day + 6 - time.weekday + 1);
                      print("$time ${time.weekday} ==  $start $end");
                      text =
                          "${start.year}.${'${start.month}'.padLeft(2, '0')}.${'${start.day}'.padLeft(2, '0')}  至  ${end.year}.${'${end.month}'.padLeft(2, '0')}.${'${end.day}'.padLeft(2, '0')}";
                    } else if (type == 2) {
                      text = ("${time.year}年${'${time.month}'.padLeft(2, '0')}月");
                    } else if (type == 3) {
                      text = ("${time.year}年");
                    }
                    return Text(
                      text,
                      style: Theme.of(context).textTheme.bodyText1,
                      strutStyle: fixedLine,
                    );
                  }),
              Space(
                height: 12,
              ),
              ValueListenableBuilder<String>(
                  valueListenable: _tab,
                  builder: (_, time, ___) {
                    return CustomPaint(
                      painter: _Background(),
                      child: Container(
                        height: 200,
                        padding: EdgeInsets.fromLTRB(_paddingLeft, _paddingTop, 0, 0),
                        child: PageView.builder(
                          reverse: true,
                          itemCount: 10240,
                          controller: _pageController,
                          onPageChanged: (page) {
                            rollDate(-page);
                          },
                          itemBuilder: (context, index) {
                            int type = toType();
                            DateTime time = offsetDate(type, -index);
                            return FutureBuilder<RespData<SportStep>>(
                                future: createFuture(time),
                                builder: (BuildContext context, AsyncSnapshot<RespData<SportStep>> snapshot) {
                                  return snapshot.connectionState != ConnectionState.done
                                      ? Container(
                                          height: 200,
                                        )
                                      : CustomPaint(
                                          painter: _Chart(_TABS.indexOf(_tab.value), snapshot.data?.data?.records ?? [], time)..initData(),
                                          child: Container(
                                            height: 200,
                                            // color: Colors.amberAccent,
                                            padding: EdgeInsets.fromLTRB(_paddingLeft, _paddingTop, _paddingRight, 0),
                                          ),
                                        );
                                });
                          },
                        ),
                      ),
                    );
                  }),
              Space(
                height: 6,
              ),
              ValueListenableBuilder<SportStep>(
                valueListenable: _valueNotifierSportDetail,
                builder: (_, _value, ___) => (_value != null && _value.sum == null)
                    ? Container()
                    : Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          Space(
                            height: 10,
                          ),
                          Text(
                            "运动总公里",
                            style: Theme.of(context).textTheme.subtitle1,
                          ),
                          Space(
                            height: 10,
                          ),
                          RichText(
                            text: TextSpan(style: Theme.of(context).textTheme.subtitle2, children: <InlineSpan>[
                              TextSpan(
                                  text: '${_value == null ? ".." : "${(_value.sum.distance ~/ 100)}"}',
                                  style: Theme.of(context).textTheme.subtitle2.copyWith(fontWeight: FontWeight.bold, fontSize: 25, color: _color)),
                              TextSpan(text: ' 米', style: Theme.of(context).textTheme.subtitle1),
                            ]),
                          ),
                          Divider(
                            height: 24,
                          ),
                          Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: <Widget>[
                              Text(
                                "日常步数",
                                style: Theme.of(context).textTheme.subtitle1,
                              ),
                              Text("${_value == null ? ".." : "${_value.sum.stepDaily}"}", style: Theme.of(context).textTheme.subtitle1)
                            ],
                          ),
                          Space(
                            height: 8,
                          ),
                          Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: <Widget>[
                              Text(
                                "游戏步数",
                                style: Theme.of(context).textTheme.subtitle1,
                              ),
                              Text("${_value == null ? ".." : "${_value.sum.stepGame}"}", style: Theme.of(context).textTheme.subtitle1)
                            ],
                          ),
                          Space(
                            height: 8,
                          ),
                          Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: <Widget>[
                              Text(
                                "游戏步频",
                                style: Theme.of(context).textTheme.subtitle1,
                              ),
                              Text("${_value == null ? ".." : "${_value.sum.stepRate}"} /min", style: Theme.of(context).textTheme.subtitle1)
                            ],
                          ),
                          Space(
                            height: 8,
                          ),
                          Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: <Widget>[
                              Text(
                                "游戏最高步频",
                                style: Theme.of(context).textTheme.subtitle1,
                              ),
                              Text("${_value == null ? ".." : "${_value.sum.stepRateMax}"} /min", style: Theme.of(context).textTheme.subtitle1)
                            ],
                          ),
                          Space(
                            height: 8,
                          ),
                        ],
                      ),
              ),
              Space(
                height: 10,
              ),
            ],
          ),
        ),
      ],
    );
  }

  int toType() {
    return _TABS.indexOf(_tab.value);
  }

  void changeTab() async {
    _valueNotifierSportDetail.value = null;
    if (_valueNotifierDate.value == null) return;

    Future<RespData<SportStep>> data = createFuture(_valueNotifierDate.value);
    if (data != null) {
      data.then((value) => _valueNotifierSportDetail.value = value.data);
    }
  }

  Future<RespData<SportStep>> createQueryFuture(int offset) {
    int type = toType();
    DateTime next = offsetDate(type, offset);
    return createFuture(next);
  }

  Future<RespData<SportStep>> createFuture(DateTime time) {
    int type = toType();
    Future<RespData<SportStep>> data;
    switch (type) {
      case 0:
        data = api.getStepRecordListOneDay('${time.year}-${'${time.month}'.padLeft(2, '0')}-${'${time.day}'.padLeft(2, '0')}');
        break;
      case 1:
        DateTime start = DateTime(time.year, time.month, time.day - time.weekday + 1);
        DateTime end = DateTime(time.year, time.month, time.day + 6 - time.weekday + 1);
        data = api.getStepRecordListByDay('${start.year}-${'${start.month}'.padLeft(2, '0')}-${'${start.day}'.padLeft(2, '0')}',
            '${end.year}-${'${end.month}'.padLeft(2, '0')}-${'${end.day}'.padLeft(2, '0')}');
        break;
      case 2:
        DateTime start = DateTime(time.year, time.month, 1);
        DateTime end = DateTime(time.year, time.month + 1, 0);
        data = api.getStepRecordListByDay('${start.year}-${'${start.month}'.padLeft(2, '0')}-${'${start.day}'.padLeft(2, '0')}',
            '${end.year}-${'${end.month}'.padLeft(2, '0')}-${'${end.day}'.padLeft(2, '0')}');
        break;
      case 3:
        data = api.getStepRecordListByMonth(time.year);
        break;
    }
    return data;
  }

  void rollDate(int offset) {
    if (_valueNotifierSportDetail.value == null) return;

    int type = toType();
    DateTime next = offsetDate(type, offset);

    _valueNotifierDate.value = next;
    changeTab();
  }
}

double _paddingLeft = 30;
double _paddingRight = 10;
double _paddingTop = 20;
double _labelHeight = 30;
const double _max = 3500;
int _valueSplit = 200;

class _Background extends CustomPainter {
  final Paint _paint = Paint()
    ..color = Color(0xffDCDCDC)
    ..strokeWidth = 0.5
    ..isAntiAlias = true;

  final ParagraphStyle _labelStyle = ParagraphStyle(
    textAlign: TextAlign.left,
    fontSize: 8,
  );

  double _zero = 0;
  int _valueSize = 7;

  @override
  void paint(Canvas canvas, Size size) {
    double zero = _zero = size.height - _labelHeight;

    // draw 值 行数
    int valuePadding = 10;
    canvas.drawLine(Offset(_paddingLeft, zero), Offset(size.width, zero), _paint);
    ParagraphBuilder pb = ParagraphBuilder(_labelStyle)
      ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
      ..addText("0");
    ParagraphConstraints constraints = ParagraphConstraints(width: _paddingLeft - valuePadding);
    Paragraph paragraph = pb.build()..layout(constraints);
    paragraph.computeLineMetrics().forEach((element) {
      canvas.drawParagraph(paragraph, Offset(0, zero - element.baseline / 2));
    });

    canvas.drawLine(Offset(_paddingLeft, zero), Offset(_paddingLeft, 0 + _paddingTop), _paint);

    double valueSpace = (size.height - _paddingTop - _labelHeight) / _valueSize;
    for (var i = 0; i < _valueSize; i++) {
      canvas.drawLine(Offset(_paddingLeft, valueSpace * i + _paddingTop), Offset(size.width, valueSpace * i + _paddingTop), _paint);
      ParagraphBuilder pb = ParagraphBuilder(_labelStyle)
        ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
        ..addText("${(_valueSize - i) * _max ~/ _valueSize / 1000}k");
      ParagraphConstraints constraints = ParagraphConstraints(width: _paddingLeft - valuePadding);
      Paragraph paragraph = pb.build()..layout(constraints);
      paragraph.computeLineMetrics().forEach((element) {
        canvas.drawParagraph(paragraph, Offset(0, valueSpace * i - element.baseline / 2 + _paddingTop));
      });
    }
    ParagraphBuilder unit = ParagraphBuilder(_labelStyle)
      ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
      ..addText("单位：步");
    ParagraphConstraints pc = ParagraphConstraints(width: size.width);
    paragraph = unit.build()..layout(pc);
    paragraph.computeLineMetrics().forEach((element) {
      canvas.drawParagraph(paragraph, Offset(_paddingLeft + 2, element.baseline / 2));
    });
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

class _Chart extends CustomPainter {
  final Paint _paint = Paint()
    ..color = Color(0xffDCDCDC)
    ..strokeWidth = 0.5
    ..isAntiAlias = true;
  final Paint _averagePaint = Paint()
    ..color = _color
    ..strokeWidth = 0.5
    ..isAntiAlias = true;
  final ParagraphStyle _labelStyle = ParagraphStyle(
    textAlign: TextAlign.left,
    fontSize: 8,
  );

  double _zero = 0;

  int _type;
  List<Record> _records;
  DateTime _dateTime;
  Map<String, num> values;
  double _average = 0;

  _Chart(this._type, this._records, this._dateTime);

  void initData() {
    values = {};
    var records = _records ?? [];

    if (_type == 0) {
      records.forEach((element) {
        var t = DateTime.parse(element.createdAt);
        values.update("${max(6, t.hour) - 5}", (value) => value + element.step, ifAbsent: () => Converter.toInt(element.step));
      });
      for (int i = 1; i <= 24; i++) {
        values.putIfAbsent("$i", () => 0.0);
      }
    } else if (_type == 1) {
      records.forEach((element) {
        var t = DateTime.parse(element.createdAt);
        values.update("${t.weekday == 0 ? 7 : t.weekday}", (value) => value + element.step, ifAbsent: () => Converter.toInt(element.step));
      });
      for (int i = 1; i <= 7; i++) {
        values.putIfAbsent("$i", () => 0.0);
      }
    } else if (_type == 2) {
      records.forEach((element) {
        var t = DateTime.parse(element.createdAt);
        values.update("${t.day}", (value) => value + element.step, ifAbsent: () => Converter.toInt(element.step));
      });
      for (int i = 1; i <= 31; i++) {
        values.putIfAbsent("$i", () => 0.0);
      }
    } else if (_type == 3) {
      records.forEach((element) {
        values.update("${element.month}", (value) => value + element.step, ifAbsent: () => Converter.toInt(element.step));
      });
      for (int i = 1; i <= 12; i++) {
        values.putIfAbsent("$i", () => 0.0);
      }
    }
    print(values);
  }

  @override
  void paint(Canvas canvas, Size size) {
    double zero = _zero = size.height - _labelHeight;

    // draw 数据列
    double left = 20;
    double width = size.width - _paddingRight - left - 20; // 柱子的有效空间
    double valueStroke = 20; // 柱子宽度
    List<String> valueLabel = []; // 柱子数量
    int scaleCount = 0; // 刻度数量
    int scaleWeight = 3;
    if (_type == 0) {
      // 日，按时间
      scaleWeight = 2;
      valueLabel = List.generate(10, (index) => "${6 + index * scaleWeight}:00");
      valueStroke = width / valueLabel.length / scaleWeight - 5;
      scaleCount = 18;
    } else if (_type == 1) {
      scaleWeight = 1;
      valueLabel = List.generate(7, (index) => WEEK[index]);
      valueStroke = 12;
      scaleCount = 6;
    } else if (_type == 2) {
      DateTime now = _dateTime;
//      DateTime now = DateTime(2019,2);
      DateTime startTime = DateTime.parse('${now.year}-${now.month < 10 ? '0${now.month}' : now.month}-01');
      DateTime endTime = DateTime.parse('${now.year}-${now.month + 1 < 10 ? '0${now.month + 1}' : now.month + 1}-01');
      int diffDay = endTime.difference(startTime).inDays;
      scaleCount = diffDay -1;
      scaleWeight = 2;
      // valueLabel = days.map((e) => '$e/${now.month}').toList();
      valueLabel =  List.generate(diffDay ~/ scaleWeight +(scaleCount % scaleWeight == 0?1:0), (index) => "${index*2+1}");
      valueStroke = 5;
    } else if (_type == 3) {
      valueLabel = List.generate(12, (index) => '${index + 1}');
      valueStroke = 12;
      scaleCount = 12 - 1;
      scaleWeight = 1;
    }

    int labelSize = valueLabel.length;
    double labelSpace = (width) / labelSize;
    double scaleWidth = (width) / scaleCount;

    //draw 刻度
    for (var i = 0; i <= scaleCount; i++) {
      canvas.drawLine(Offset(left + scaleWidth * i, zero), Offset(left + scaleWidth * i, zero - 3), _paint);
    }

    for (var i = 0; i < labelSize; i++) {
      String label = valueLabel[i];
      ParagraphBuilder pb = ParagraphBuilder(_labelStyle)
        ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
        ..addText(label);
      ParagraphConstraints constraints = ParagraphConstraints(width: labelSpace);
      Paragraph paragraph = pb.build()..layout(constraints);
      paragraph.computeLineMetrics().forEach((element) {
        canvas.drawParagraph(paragraph, Offset(left + scaleWidth * scaleWeight * i - element.width / 2, zero + 5));
      });
//
//      double l = left + scaleWidth * scaleWeight * i - valueStroke / 2;
//      Rect rect = Rect.fromLTRB(l, zero, l + valueStroke, 0);
//      print("value draw $rect");
//      Paint valuePaint = Paint()
//        ..shader = LinearGradient(
//          begin: Alignment.bottomCenter,
//          end: Alignment.topCenter,
//          colors: <Color>[Color(0xffFF9100), Color(0xffFFE600)],
//        ).createShader(rect);
//      canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(100)), valuePaint);
    }

    // if (_type < 2) {
    int _count = 0;
    for (var i = 0; i <= scaleCount; i++) {
      String key = '${i + 1}';
      if (!values.containsKey(key)) continue;
      _count++;
      num value = values[key];
      double l = left + scaleWidth * i - valueStroke / 2;
      Rect rect = Rect.fromLTRB(l, zero, l + valueStroke, calValue(value));
      Paint valuePaint = Paint()..color = _color;
      canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(100)), valuePaint);
    }

    double total = values.values.reduce((value, element) => (value + element).toDouble());
    _average = total / _count;
    double offsetY = calValue(_average);
    double offsetX = 0;
    while (offsetX < size.width) {
      canvas.drawLine(Offset(offsetX, offsetY), Offset(offsetX + 3, offsetY), _averagePaint);
      offsetX += 5;
    }
    ParagraphBuilder unit = ParagraphBuilder(_labelStyle)
      ..pushStyle(ui.TextStyle(color: _color))
      ..addText("平均线");
    ParagraphConstraints pc = ParagraphConstraints(width: size.width);
    var paragraph = unit.build()..layout(pc);
    paragraph.computeLineMetrics().forEach((element) {
      canvas.drawParagraph(paragraph, Offset(size.width - element.width, offsetY + (offsetY < 1 ? element.baseline : -element.baseline) - 5));
    });
  }

  double calValue(num value) {
    if (value == null) return _zero;
    return min(_zero, _zero - _zero * min(_max, value) / _max);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}
