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

import 'package:ecosim/src/common/models/pvector.dart';
import 'package:ecosim/src/ecosim/utils.dart';

import 'dna.dart';

// Abstract class to define the base functions that every
// boid should have
abstract class BoidBaseBehavior {
  // update on each tick
  void update();

  // update on each health tick
  void healthTick();

  // update the boids position in the world
  void updateBoidTransform();

  // update the boids acceleration that
  // controls in which direction the boid
  // should move
  void updateBoidDirection(
    List<List<Object>> objects,
    Size worldSize,
  );

  // check for the world boundaries to avoid
  // moving outside of the set simulation world size
  void boundaryBoidsDirectionUpdate(Size size);

  // handle the boids collision with other objects
  void handleBoidCollision(List<Object> items, Object collisionObject);

  // Helper function to get the closest item of a specific type
  PVector? getClosestItem(
    PVector origin,
    double range,
    List<Object> items,
    Size worldSize,
  );

  // Getter function to check if the boid is dead
  // returns true if the boid is dead
  bool get dead;

  // Function to render the boid
  void render(Canvas canvas);

  // Function to render the debug information for the boid
  void renderDebug(Canvas canvas);
}

class BoidBaseDna extends Dna {
  BoidBaseDna({
    required this.steeringForce,
    required this.speed,
    required this.foodPull,
    required this.poisonPull,
    required this.predatorPull,
    required this.foodPerceptionRadius,
    required this.poisonPerceptionRadius,
    required this.predatorPerceptionRadius,
    required super.mutationThreshold,
  });

  double steeringForce;
  double speed;
  double foodPull;
  double poisonPull;
  double predatorPull;
  double foodPerceptionRadius;
  double poisonPerceptionRadius;
  double predatorPerceptionRadius;

  /// returns a new BoidDna with random values in preset ranges
  factory BoidBaseDna.rand({required double mutationThreshold}) {
    final rng = math.Random();
    return BoidBaseDna(
      steeringForce: rng.getRandomDoubleInRange(0.0005, 0.005),
      speed: rng.getRandomDoubleInRange(0.5, 1.5),
      foodPull: rng.getRandomDoubleInRange(-1.5, 1.5),
      poisonPull: rng.getRandomDoubleInRange(-1.5, 1.5),
      predatorPull: rng.getRandomDoubleInRange(-1.5, 1.5),
      foodPerceptionRadius: rng.getRandomDoubleInRange(50.0, 120.0),
      poisonPerceptionRadius: rng.getRandomDoubleInRange(50.0, 120.0),
      predatorPerceptionRadius: rng.getRandomDoubleInRange(50.0, 120.0),
      mutationThreshold: mutationThreshold,
    );
  }

  /// mutate the DNA values
  void mutate() {
    steeringForce += getRandMutationDelta() * 0.01;
    speed += getRandMutationDelta();
    foodPull += getRandMutationDelta();
    poisonPull += getRandMutationDelta();
    predatorPull += getRandMutationDelta();
    foodPerceptionRadius += getRandMutationDelta();
    poisonPerceptionRadius += getRandMutationDelta();
  }
}
