import 'dart:math';

import 'package:flutter/material.dart';
import 'package:puissance4/coordinate.dart';

import 'board.dart';
import 'cpu.dart';
import 'game_chip.dart';
import 'hole_painter.dart';

enum Color {
  YELLOW,
  RED,
}

enum Mode {
  PVP,
  PVC,
  DEMO,
}

class MatchPage extends StatefulWidget {
  final Mode mode;
  final Cpu cpu;
  final Cpu cpu2;

  const MatchPage({
    Key key,
    this.mode,
    this.cpu,
    this.cpu2,
  }) : super(key: key);

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

class _MatchPageState extends State<MatchPage> with TickerProviderStateMixin {
  final board = Board();
  Color turn;
  Color winner;

  List<List<Animation<double>>> translations = List.generate(
    7,
    (i) => List.generate(
      7,
      (i) => null,
    ),
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
      ),
      backgroundColor: Colors.blue,
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Flex(
          direction: Axis.vertical,
          mainAxisSize: MainAxisSize.max,
          children: <Widget>[
            Flexible(
              flex: 2,
              child: Container(
                constraints: BoxConstraints.loose(
                  Size(
                    500,
                    532,
                  ),
                ),
                child: Padding(
                  padding: const EdgeInsets.only(top: 32.0),
                  child: Stack(
                    overflow: Overflow.clip,
                    fit: StackFit.loose,
                    children: <Widget>[
                      Positioned.fill(
                        child: Container(
                          color: Colors.white,
                        ),
                      ),
                      buildPieces(),
                      buildBoard(),
                    ],
                  ),
                ),
              ),
            ),
            Flexible(
              flex: 1,
              child: Padding(
                padding: const EdgeInsets.all(32.0),
                child: winner != null
                    ? Text(
                        '${winner == Color.RED ? 'RED' : 'YELLOW'} WINS',
                        textAlign: TextAlign.center,
                        style: Theme.of(context)
                            .textTheme
                            .headline6
                            .copyWith(color: Colors.white),
                      )
                    : Column(
                        children: <Widget>[
                          Text(
                            '${turn == Color.RED ? 'RED' : 'YELLOW'} SPEAKS',
                            textAlign: TextAlign.center,
                            style: Theme.of(context)
                                .textTheme
                                .headline5
                                .copyWith(color: Colors.white),
                          ),
                          Padding(
                            padding: const EdgeInsets.all(8.0),
                            child: GameChip(color: turn),
                          ),
                          _buildPlayerName(context),
                        ],
                      ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Text _buildPlayerName(BuildContext context) {
    String name;

    if (widget.mode == Mode.PVC) {
      if (turn == widget.cpu.color) {
        name = 'CPU - ${widget.cpu.toString()}';
      } else {
        name = 'USER';
      }
    } else if (widget.mode == Mode.PVP) {
      if (turn == Color.RED) {
        name = 'PLAYER1';
      } else {
        name = 'PLAYER2';
      }
    } else {
      if (turn == widget.cpu.color) {
        name = 'CPU1 - ${widget.cpu.toString()}';
      } else {
        name = 'CPU2 - ${widget.cpu2.toString()}';
      }
    }
    return Text(
      name,
      textAlign: TextAlign.center,
      style: Theme.of(context)
        .textTheme
        .headline5
        .copyWith(color: Colors.white),
    );
  }

  @override
  void initState() {
    super.initState();
    turn = widget.cpu?.otherPlayer ??
        (Random().nextBool() ? Color.RED : Color.YELLOW);
    if (widget.mode == Mode.PVC && turn == widget.cpu.color) {
      cpuMove(widget.cpu);
    } else if (widget.mode == Mode.DEMO) {
      if (turn == widget.cpu.color) {
        cpuMove(widget.cpu);
      } else {
        cpuMove(widget.cpu2);
      }
    }
  }

  GridView buildPieces() {
    return GridView.custom(
      padding: const EdgeInsets.all(0),
      shrinkWrap: true,
      physics: NeverScrollableScrollPhysics(),
      gridDelegate:
          SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 7),
      childrenDelegate: SliverChildBuilderDelegate(
        (context, i) {
          final col = i % 7;
          final row = i ~/ 7;

          if (board.getBox(Coordinate(col, row)) == null) {
            return SizedBox();
          }

          return GameChip(
            translation: translations[col][row],
            color: board.getBox(Coordinate(col, row)),
          );
        },
        childCount: 49,
      ),
    );
  }

  GridView buildBoard() {
    return GridView.custom(
      padding: const EdgeInsets.all(0),
      physics: NeverScrollableScrollPhysics(),
      gridDelegate:
          SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 7),
      shrinkWrap: true,
      childrenDelegate: SliverChildBuilderDelegate(
        (context, i) {
          final col = i % 7;

          return GestureDetector(
            onTap: () {
              if (winner == null) {
                userMove(col);
              }
            },
            child: CustomPaint(
              size: Size(50, 50),
              willChange: false,
              painter: HolePainter(),
            ),
          );
        },
        childCount: 49,
      ),
    );
  }

  void userMove(int col) {
    putChip(col);
    if (winner == null && widget.mode == Mode.PVC) {
      cpuMove(widget.cpu);
    }
  }

  void cpuMove(Cpu cpu) async {
    int col = await cpu.chooseCol(board);
    putChip(col);

    if (winner == null && widget.mode == Mode.DEMO) {
      if (turn == widget.cpu.color) {
        cpuMove(widget.cpu);
      } else {
        cpuMove(widget.cpu2);
      }
    }
  }

  void putChip(int col) {
    final target = board.getColumnTarget(col);
    final player = turn;

    if (target == -1) {
      return;
    }

    final controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 1),
    )..addListener(() {
        if (mounted) {
          setState(() {});
        }
      });

    if (mounted) {
      setState(() {
        board.setBox(Coordinate(col, target), turn);
        turn = turn == Color.RED ? Color.YELLOW : Color.RED;
      });
    }

    translations[col][target] = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(CurvedAnimation(
      curve: Curves.bounceOut,
      parent: controller,
    ))
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          controller.dispose();
        }
      });

    controller.forward().orCancel;

    if (board.checkWinner(Coordinate(col, target), player)) {
      showWinnerDialog(context, player);
    }
  }

  void showWinnerDialog(BuildContext context, Color player) {
    setState(() {
      winner = player;
    });

    Future.delayed(
      Duration(seconds: 5),
      () => mounted ? Navigator.popUntil(context, (r) => r.isFirst) : null,
    );
  }

  void resetBoard() {
    setState(() {
      winner = null;
      board.reset();
    });
  }
}