// 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.

// An example showing usage of [StarBorder].

import 'package:flutter/material.dart';

const int _kParameterPrecision = 2;

void main() => runApp(const StarBorderApp());

class StarBorderApp extends StatelessWidget {
  const StarBorderApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('StarBorder Example'),
          backgroundColor: const Color(0xff323232),
        ),
        body: const StarBorderExample(),
      ),
    );
  }
}

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

  @override
  State<StarBorderExample> createState() => _StarBorderExampleState();
}

class _StarBorderExampleState extends State<StarBorderExample> {
  final OptionModel _model = OptionModel();
  final TextEditingController _textController = TextEditingController();

  @override
  void initState() {
    super.initState();
    _model.addListener(_modelChanged);
  }

  @override
  void dispose() {
    _model.removeListener(_modelChanged);
    _textController.dispose();
    super.dispose();
  }

  void _modelChanged() {
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTextStyle(
      style: const TextStyle(
        color: Colors.black,
        fontSize: 14.0,
        fontFamily: 'Roboto',
        fontStyle: FontStyle.normal,
      ),
      child: ListView(
        children: <Widget>[
          ColoredBox(color: Colors.grey.shade200, child: Options(_model)),
          Padding(
            padding: const EdgeInsets.all(18.0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                Expanded(
                  child: ExampleBorder(
                    border: StarBorder(
                      side: const BorderSide(),
                      points: _model.points,
                      innerRadiusRatio: _model.innerRadiusRatio,
                      pointRounding: _model.pointRounding,
                      valleyRounding: _model.valleyRounding,
                      rotation: _model.rotation,
                      squash: _model.squash,
                    ),
                    title: 'Star',
                  ),
                ),
                Expanded(
                  child: ExampleBorder(
                    border: StarBorder.polygon(
                      side: const BorderSide(),
                      sides: _model.points,
                      pointRounding: _model.pointRounding,
                      rotation: _model.rotation,
                      squash: _model.squash,
                    ),
                    title: 'Polygon',
                  ),
                ),
              ],
            ),
          ),
          Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Expanded(
                child: Container(
                  color: Colors.black12,
                  margin: const EdgeInsets.all(16.0),
                  padding: const EdgeInsets.all(16.0),
                  child: SelectableText(_model.starCode),
                ),
              ),
              Expanded(
                child: Container(
                  color: Colors.black12,
                  margin: const EdgeInsets.all(16.0),
                  padding: const EdgeInsets.all(16.0),
                  child: SelectableText(_model.polygonCode),
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class ExampleBorder extends StatelessWidget {
  const ExampleBorder({super.key, required this.border, required this.title});

  final StarBorder border;
  final String title;

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      padding: const EdgeInsets.all(20),
      width: 150,
      height: 100,
      decoration: ShapeDecoration(color: Colors.blue.shade100, shape: border),
      child: Text(title),
    );
  }
}

class Options extends StatefulWidget {
  const Options(this.model, {super.key});

  final OptionModel model;

  @override
  State<Options> createState() => _OptionsState();
}

class _OptionsState extends State<Options> {
  @override
  void initState() {
    super.initState();
    widget.model.addListener(_modelChanged);
  }

  @override
  void didUpdateWidget(Options oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.model != oldWidget.model) {
      oldWidget.model.removeListener(_modelChanged);
      widget.model.addListener(_modelChanged);
    }
  }

  @override
  void dispose() {
    super.dispose();
    widget.model.removeListener(_modelChanged);
  }

  void _modelChanged() {
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.fromLTRB(5.0, 0.0, 5.0, 10.0),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Row(
            children: <Widget>[
              Expanded(
                child: ControlSlider(
                  label: 'Point Rounding',
                  value: widget.model.pointRounding,
                  onChanged: (double value) {
                    widget.model.pointRounding = value;
                  },
                ),
              ),
              Expanded(
                child: ControlSlider(
                  label: 'Valley Rounding',
                  value: widget.model.valleyRounding,
                  onChanged: (double value) {
                    widget.model.valleyRounding = value;
                  },
                ),
              ),
            ],
          ),
          Row(
            children: <Widget>[
              Expanded(
                child: ControlSlider(
                  label: 'Squash',
                  value: widget.model.squash,
                  onChanged: (double value) {
                    widget.model.squash = value;
                  },
                ),
              ),
              Expanded(
                child: ControlSlider(
                  label: 'Rotation',
                  value: widget.model.rotation,
                  max: 360,
                  onChanged: (double value) {
                    widget.model.rotation = value;
                  },
                ),
              ),
            ],
          ),
          Row(
            children: <Widget>[
              Expanded(
                child: Row(
                  children: <Widget>[
                    Expanded(
                      child: ControlSlider(
                        label: 'Points',
                        value: widget.model.points,
                        min: 3,
                        max: 20,
                        precision: 1,
                        onChanged: (double value) {
                          widget.model.points = value;
                        },
                      ),
                    ),
                    Tooltip(
                      message:
                          'Round the number of points to the nearest integer.',
                      child: Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: OutlinedButton(
                          child: const Text('Nearest'),
                          onPressed: () {
                            widget.model.points = widget.model.points
                                .roundToDouble();
                          },
                        ),
                      ),
                    ),
                  ],
                ),
              ),
              Expanded(
                child: ControlSlider(
                  label: 'Inner Radius',
                  value: widget.model.innerRadiusRatio,
                  onChanged: (double value) {
                    widget.model.innerRadiusRatio = value;
                  },
                ),
              ),
            ],
          ),
          ElevatedButton(
            onPressed: () {
              widget.model.reset();
            },
            child: const Text('Reset'),
          ),
        ],
      ),
    );
  }
}

class OptionModel extends ChangeNotifier {
  double get pointRounding => _pointRounding;
  double _pointRounding = 0.0;
  set pointRounding(double value) {
    if (value != _pointRounding) {
      _pointRounding = value;
      if (_valleyRounding + _pointRounding > 1) {
        _valleyRounding = 1.0 - _pointRounding;
      }
      notifyListeners();
    }
  }

  double get valleyRounding => _valleyRounding;
  double _valleyRounding = 0.0;
  set valleyRounding(double value) {
    if (value != _valleyRounding) {
      _valleyRounding = value;
      if (_valleyRounding + _pointRounding > 1) {
        _pointRounding = 1.0 - _valleyRounding;
      }
      notifyListeners();
    }
  }

  double get squash => _squash;
  double _squash = 0.0;
  set squash(double value) {
    if (value != _squash) {
      _squash = value;
      notifyListeners();
    }
  }

  double get rotation => _rotation;
  double _rotation = 0.0;
  set rotation(double value) {
    if (value != _rotation) {
      _rotation = value;
      notifyListeners();
    }
  }

  double get innerRadiusRatio => _innerRadiusRatio;
  double _innerRadiusRatio = 0.4;
  set innerRadiusRatio(double value) {
    if (value != _innerRadiusRatio) {
      _innerRadiusRatio = value.clamp(0.0001, double.infinity);
      notifyListeners();
    }
  }

  double get points => _points;
  double _points = 5;
  set points(double value) {
    if (value != _points) {
      _points = value;
      notifyListeners();
    }
  }

  String get starCode {
    return 'Container(\n'
        '  decoration: ShapeDecoration(\n'
        '    shape: StarBorder(\n'
        '      points: ${points.toStringAsFixed(_kParameterPrecision)},\n'
        '      rotation: ${rotation.toStringAsFixed(_kParameterPrecision)},\n'
        '      innerRadiusRatio: ${innerRadiusRatio.toStringAsFixed(_kParameterPrecision)},\n'
        '      pointRounding: ${pointRounding.toStringAsFixed(_kParameterPrecision)},\n'
        '      valleyRounding: ${valleyRounding.toStringAsFixed(_kParameterPrecision)},\n'
        '      squash: ${squash.toStringAsFixed(_kParameterPrecision)},\n'
        '    ),\n'
        '  ),\n'
        ');';
  }

  String get polygonCode {
    return 'Container(\n'
        '  decoration: ShapeDecoration(\n'
        '    shape: StarBorder.polygon(\n'
        '      sides: ${points.toStringAsFixed(_kParameterPrecision)},\n'
        '      rotation: ${rotation.toStringAsFixed(_kParameterPrecision)},\n'
        '      cornerRounding: ${pointRounding.toStringAsFixed(_kParameterPrecision)},\n'
        '      squash: ${squash.toStringAsFixed(_kParameterPrecision)},\n'
        '    ),\n'
        '  ),\n'
        ');';
  }

  void reset() {
    final OptionModel defaultModel = OptionModel();
    _pointRounding = defaultModel.pointRounding;
    _valleyRounding = defaultModel.valleyRounding;
    _rotation = defaultModel.rotation;
    _squash = defaultModel.squash;
    _innerRadiusRatio = defaultModel._innerRadiusRatio;
    _points = defaultModel.points;
    notifyListeners();
  }
}

class ControlSlider extends StatelessWidget {
  const ControlSlider({
    super.key,
    required this.label,
    required this.value,
    required this.onChanged,
    this.min = 0.0,
    this.max = 1.0,
    this.precision = _kParameterPrecision,
  });

  final String label;
  final double value;
  final void Function(double value) onChanged;
  final double min;
  final double max;
  final int precision;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(4.0),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Expanded(flex: 2, child: Text(label, textAlign: TextAlign.end)),
          Expanded(
            flex: 5,
            child: Slider(
              onChanged: onChanged,
              min: min,
              max: max,
              value: value,
            ),
          ),
          Expanded(child: Text(value.toStringAsFixed(precision))),
        ],
      ),
    );
  }
}
