/* Focus on diagram (e.g. by clicking on the matrix)
  w                p
  a   s            o
  d             i
*/



#include "FastLED.h"


// Matrix size
#define HEIGHT 16
#define WIDTH 24

// Define pins
#define DATA_PIN 3
#define BUTTON_0 9
#define BUTTON_1 10
#define BUTTON_2 11
#define BUTTON_3 12
#define BUTTON_4 5
#define BUTTON_5 6
#define BUTTON_6 7

// LED brightness
#define BRIGHTNESS 255

// Define the array of leds
#define NUM_LEDS HEIGHT * WIDTH
CRGB leds[NUM_LEDS];

byte pressed = 0;
bool button0Pressed = false;
bool button1Pressed = false;
bool button2Pressed = false;
bool button3Pressed = false;
bool button4Pressed = false;
bool button5Pressed = false;
bool button6Pressed = false;
void setup() {
  pinMode(BUTTON_0, INPUT_PULLUP);
  pinMode(BUTTON_1, INPUT_PULLUP);
  pinMode(BUTTON_2, INPUT_PULLUP);
  pinMode(BUTTON_3, INPUT_PULLUP);
  pinMode(BUTTON_4, INPUT_PULLUP);
  pinMode(BUTTON_5, INPUT_PULLUP);
  pinMode(BUTTON_6, INPUT_PULLUP);
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
}

void loop() {
  // Read the button inputs
  button0Pressed = digitalRead(BUTTON_0) == LOW;
  button1Pressed = digitalRead(BUTTON_1) == LOW;
  button2Pressed = digitalRead(BUTTON_2) == LOW;
  button3Pressed = digitalRead(BUTTON_3) == LOW;
  button4Pressed = digitalRead(BUTTON_4) == LOW;
  button5Pressed = digitalRead(BUTTON_5) == LOW;
  button6Pressed = digitalRead(BUTTON_6) == LOW;
  if (button0Pressed) {
    pressed = 1;
  } else if (button1Pressed) {
    pressed = 2;
  } else if (button2Pressed) {
    pressed = 3;
  } else if (button3Pressed) {
    pressed = 4;
  } else if (button4Pressed) {
    pressed = 5;
  }  else if (button5Pressed) {
    pressed = 6;
  }  else if (button6Pressed) {
    pressed = 7;
  }
  else pressed = 0;
  draw();
  FastLED.show();
  delay(16);
}
#define MAP_W WIDTH
#define MAP_H HEIGHT
#define RAY_LENGHT_M MAP_W + MAP_H
int lightersPosY = 10;
int lightersPosX = 10;
byte PosX;
byte PosY;
byte lightersSpeed = 4;
int alpha = 0;
float angle;
byte rot = 10;
bool ThreeD = false;
bool mape[MAP_W][MAP_H] = {
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
};
byte POV = WIDTH;
byte PoV = POV / 2;

bool loadingFlag = true;
void draw() {
  if (loadingFlag) {
    loadingFlag = false;
  }
  FastLED.clear();
  if (pressed == 1) {
    lightersPosX += sin(angle) * lightersSpeed;
    lightersPosY += cos(angle) * lightersSpeed;

    if (lightersPosX < 0) lightersPosX = (MAP_W - 1) * 4;
    if (lightersPosX > (MAP_W - 1) * 4) lightersPosX = 0;
    if (lightersPosY < 0) lightersPosY = (MAP_H - 1) * 4;
    if (lightersPosY > (MAP_H - 1) * 4) lightersPosY = 0;

    PosX = lightersPosX / 4;
    PosY = lightersPosY / 4;
  }
  else if (pressed == 2) {
    float angle = radians(alpha);
    lightersPosX -= sin(angle) * lightersSpeed;
    lightersPosY -= cos(angle) * lightersSpeed;

    if (lightersPosX < 0) lightersPosX = (MAP_W - 1) * 4;
    if (lightersPosX > (MAP_W - 1) * 4) lightersPosX = 0;
    if (lightersPosY < 0) lightersPosY = (MAP_H - 1) * 4;
    if (lightersPosY > (MAP_H - 1) * 4) lightersPosY = 0;

    PosX = lightersPosX / 4;
    PosY = lightersPosY / 4;
  }
  if (pressed == 3) {
    alpha += rot;
    angle = radians(alpha);
  }
  else if (pressed == 4) {
    alpha -= rot;
    angle = radians(alpha);
  }
  if (pressed == 5) {
    POV ++;
    if (POV >= 255) POV = 254; PoV = POV / 2;
  }
  else if (pressed == 6) {
    POV --;
    if (POV <= 0) POV = 1;
    PoV = POV / 2;
  }
  if (pressed == 7) {
    ThreeD = !ThreeD;
    delay(100);
  }
  for (byte pov = 0; pov < POV+1; pov++) {
    byte dist = 0;
    byte hdist = 0;
    float angleR = radians(alpha + pov - PoV - 1);
    float angle_a = sin(angleR);
    float angle_b = cos(angleR);
    for (byte i = 0; i < (RAY_LENGHT_M); i++) {
      byte ENDPosX = PosX + (i * angle_a);
      byte ENDPosY = PosY + (i * angle_b);
      if (mape[ENDPosX][ENDPosY] || ENDPosX < 0 || ENDPosX >= MAP_W || ENDPosY < 0 || ENDPosY >= MAP_H) {
        if (ThreeD) {
          dist = map(i, 0, RAY_LENGHT_M, 255, 0);
          hdist = map(i, 0, RAY_LENGHT_M, HEIGHT / 2, 1);
        } else {
          leds[XY(PosX + ((i - 1) * angle_a), PosY + ((i - 1) * angle_b))] += CHSV(0, 0, 100);
        } break;
      }
      else if (!ThreeD) {
        leds[XY(ENDPosX, ENDPosY)] += CHSV(50, 200, 100);
      }
    }
    if (ThreeD) {
      for (byte y = 0; y < HEIGHT / 2 - hdist; y++) {
        leds[XY(map(pov, 0, POV, 0, WIDTH - 1), y)] = CHSV(50, 100, ~(y * (256 / HEIGHT * 2)));
      } for (byte y = HEIGHT / 2 - hdist; y < HEIGHT / 2 + hdist; y++) {
        leds[XY(map(pov, 0, POV, 0, WIDTH - 1), y)] = CHSV(50, 200, dist);
      }
    }
  }
}

uint16_t XY (uint8_t x, uint8_t y) {
  return (y * WIDTH + x);
}