#pragma once
#include "raylib.h"
#include "raymath.h"
#include "NervousSystem.hpp"
#include <vector>
#include <cmath>

struct Eye {
  int unitIdx;
  float angleOffset;
  float fov;
  float range;
};

class Spider {
 public:
  NervousSystem brain;
  Vector2 position;
  float angle;

  std::vector<Eye> eyes;
  std::vector<int> vibrationUnits;
  std::vector<int> motorUnits;
  int freezeUnit;
  Spider(Vector2 startPos) : position(startPos), angle(0.0f) {
    auto addEye = [&](float offset, float fov, float range) {
      int id = brain.addUnit();
      eyes.push_back({id, offset, fov, range});
    };

    addEye(-5.0f, 30.0f, 600.0f);
    addEye(5.0f, 30.0f, 600.0f);

    addEye(-20.0f, 60.0f, 500.0f);
    addEye(20.0f, 60.0f, 500.0f);

    addEye(-45.0f, 120.0f, 350.0f);
    addEye(45.0f, 120.0f, 350.0f);

    addEye(-60.0f, 180.0f, 200.0f);
    addEye(60.0f, 180.0f, 200.0f);

    for (int i = 0; i < 8; i++) {
      vibrationUnits.push_back(brain.addUnit());
    }

    for (int i = 0; i < 8; i++) {
      motorUnits.push_back(brain.addUnit());
    }

    freezeUnit = brain.addUnit();

    for (int m : motorUnits) {
      brain.connect(freezeUnit, m, -5.0f);
    }

    for (int i = 0; i < 8; i++) {
      for (int m = 0; m < 8; m++) {
        if ((i < 4 && m >= 4) || (i >= 4 && m < 4)) {
          brain.connect(vibrationUnits[i], motorUnits[m], 2.2f);
        }

        if (i == m) {
          brain.connect(vibrationUnits[i], motorUnits[m], 0.4f);
        }
      }
    }

    for (int e = 0; e < 8; e++) {
      float eyeAng = eyes[e].angleOffset;
      float focusMultiplier = (std::abs(eyeAng) <= 30.0f) ? 2.8f : 1.5f;

      int targetLegIdx = -1;

      if (eyeAng < -15.0f) {
        targetLegIdx = 7;
      } else if (eyeAng > 15.0f) {
        targetLegIdx = 0;
      } else if (std::abs(eyeAng) <= 15.0f) {
        brain.connect(eyes[e].unitIdx, motorUnits[0], focusMultiplier * 0.8f);
        brain.connect(eyes[e].unitIdx, motorUnits[7], focusMultiplier * 0.8f);
      }

      if (targetLegIdx != -1) {
        brain.connect(eyes[e].unitIdx, motorUnits[targetLegIdx], focusMultiplier);
      }
    }
  }

  void update() {
    brain.tick();

    float turnStep = 5.0f * DEG2RAD;
    float moveStep = 3.0f;

    for (int i = 0; i < 8; i++) {
      if (brain.getUnit(motorUnits[i]).hasFired()) {
        if (i < 4)
          angle += turnStep;
        else
          angle -= turnStep;

        Vector2 forward = {cosf(angle), sinf(angle)};
        position = Vector2Add(position, Vector2Scale(forward, moveStep));
      }
    }
  }

  void draw() {
    DrawCircleV(position, 15, (Color){30, 30, 30, 255});
    Vector2 headPos = Vector2Add(position, Vector2Rotate({12, 0}, angle));
    DrawCircleV(headPos, 10, BLACK);

    for (const auto& eye : eyes) {
      float totalAngle = angle * RAD2DEG + eye.angleOffset;

      DrawCircleSector(headPos, eye.range, totalAngle - eye.fov / 2, totalAngle + eye.fov / 2, 10, Fade(RED, 0.1f));

      DrawCircleSectorLines(headPos, eye.range, totalAngle - eye.fov / 2, totalAngle + eye.fov / 2, 10,
                            Fade(RED, 0.2f));
    }

    for (int side : {-1, 1}) {
      Vector2 eyePos = Vector2Add(headPos, Vector2Rotate({6, (float)side * 4}, angle));
      DrawCircleV(eyePos, 2, RED);
    }

    for (int i = 0; i < 8; i++) {
      float legOffset = (i < 4) ? (-135.0f + i * 30.0f) : (45.0f + (i - 4) * 30.0f);
      float legAngle = angle + legOffset * DEG2RAD;

      const auto& unit = brain.getUnit(motorUnits[i]);
      float length = 35.0f + (unit.hasFired() ? 15.0f : unit.getCharge() * 5.0f);

      Vector2 legEnd = Vector2Add(position, Vector2Rotate({length, 0}, legAngle));

      Color legColor = ColorLerp(BLACK, RED, unit.getCharge());
      if (unit.hasFired()) legColor = MAROON;

      DrawLineEx(position, legEnd, 2.0f, legColor);
      DrawCircleV(legEnd, 3, legColor);
    }
    if (brain.getUnit(freezeUnit).hasFired()) {
      DrawText("!!! STRACH !!!", position.x - 40, position.y - 40, 10, RED);
    }
  }
};