#ifndef NEOPATTERNS_H
#define NEOPATTERNS_H

#include <Adafruit_NeoPixel.h>
#include <math.h>
#include <vector>
#include <algorithm> // std::remove
#include "Rocket.h"
#include "Particle.h"
// class Rocket;
// class Particle;

// Ideas
// Drop (Middle high, than to both sides diming out)
#define MAX_DROPS 10
#define MAX_RINGS 1
// Two or more chasers
// Chaser changing direction randomly

// Pattern types supported:
enum  pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE, RANDOM_FADE, SMOOTH, RANDOM_FADE_SINGLE, PLASMA, FILL, RANDOM, FIRE, FIREWORKS, DROP, RINGS, SCANNER_RANDOM, BVB };
// Patern directions supported:
enum  direction { FORWARD, REVERSE };


class NeoPatterns : public Adafruit_NeoPixel
{
  public:
    NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)(), void (*callbackDebug)(String));


    void Update();

    void Reverse();
    void None(uint8_t interval = 40);
    void RainbowCycle(uint8_t interval, direction dir = FORWARD);
    void RainbowCycleUpdate();
    void TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD);
    void TheaterChaseUpdate();
    void BVBChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD);
    void BVBChaseUpdate();
    void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD);
    void ColorWipeUpdate();
    void Scanner(uint32_t color1 = 16711680, uint8_t interval = 40, bool colorful = false, bool spiral = false);
    void ScannerUpdate();
    void ScannerRandom(uint32_t color1 = 16711680, uint8_t interval = 40, bool colorful = false, bool spiral = false);
    void ScannerRandomUpdate();
    void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD);
    void FadeUpdate();
    void RandomFade(uint8_t interval = 100);
    void RandomFadeUpdate();
    void RandomFadeSingle(uint8_t interval = 100, uint8_t speed = 5);
    void RandomFadeSingleUpdate();
    void Fire(uint8_t interval = 100);
    void FireUpdate();
    void Fireworks();
    void FireworksUpdate();
    void explosion(int pos, float rocketspeed);
    void Drop(uint8_t interval = 100);
    void DropUpdate();
    void Rings(uint8_t interval = 100);
    void RingsUpdate();
    void RandomBuffer();
    void Random();
    void Smooth(uint8_t wheelSpeed = 16, uint8_t smoothing = 80, uint8_t strength = 50, uint8_t interval = 40);
    void SmoothUpdate();
    void Plasma(float phase = 0, float phaseIncrement = 0.03, float colorStretch = 0.3, uint8_t interval = 60); // 0.08 and 0.11
    void PlasmaUpdate();

    void SetColor1(uint32_t color);
    void SetColor2(uint32_t color);
    //Utilities
    void ColorSet(uint32_t color);
    void ColorSetParameters(String parameters);
    uint8_t Red(uint32_t color);
    uint8_t Green(uint32_t color);
    uint8_t Blue(uint32_t color);
    uint32_t Wheel(byte WheelPos);
    uint32_t Wheel(byte WheelPos, float brightness);
    uint8_t numToSpiralPos(int num);
    uint8_t xyToPos(int x, int y);
    uint8_t numToPos(int num);
    uint8_t getAverage(uint8_t array[], uint8_t i, int x, int y);
    uint32_t parseColor(String value);


#define EXPLOSION_SIZE_MIN 5
#define EXPLOSION_SIZE_MAX 10
    // 60 LED Strip: 50, 100, 0.985 is a good choice (with Interval = 25)
    // Start 0, with maximum speed (100), the rocket should explode at the LATEST at position 50 (of 60). (Which is 10 pixels before maximum)
    // #define EXPLOSION_SPEED 0.25f
    // #define ROCKET_SPEED_MIN 50
    // ROCKET_SPEED_MAX should not be >100, as this would skip LEDs.
    // #define ROCKET_SPEED_MAX 100
    // #define ROCKET_SLOWDOWN 0.985f
#define ROCKET_LAUNCH_TIMEOUT_MIN 1000
#define ROCKET_LAUNCH_TIMEOUT_MAX 3000

    uint32_t maxRocketID = 0;
    uint32_t maxParticleID = 0;
    uint32_t currentRocketMillis = 0;
    uint32_t rocketTimeout;
    float explosion_speed = 0.25f;
    uint8_t rocket_speed_min = 50;
    uint8_t rocket_speed_max = 100;
    double rocket_slowdown = 0.985f;

  private:
    std::vector <Rocket> rocket_arr;
    std::vector <Particle> particle_arr;

    // Member Variables:
    pattern ActivePattern;       // which pattern is running
    pattern SavedPattern;
    direction Direction;         // direction to run the pattern
    direction SavedDirection;

    unsigned long Interval;       // milliseconds between updates
    unsigned long SavedInterval;
    unsigned long lastUpdate;     // last update of position

    uint32_t Color1, Color2;      // What colors are in use
    uint32_t SavedColor1;
    uint16_t TotalSteps;      // total number of steps in the pattern
    uint16_t SavedTotalSteps;
    uint16_t Index;      // current step within the pattern
    uint16_t SavedIndex;
    uint8_t Every;                // Turn every "Every" pixel in Color1/Color2

    byte wPos;
    bool colorful;
    bool spiral;
    uint8_t wPosSlow;
    uint8_t WheelSpeed;
    uint8_t Smoothing;
    uint8_t Strength;
    uint8_t movingPoint_x;
    uint8_t movingPoint_y;
    uint8_t *pixelR;
    uint8_t *pixelG;
    uint8_t *pixelB;
    uint8_t *pixelR_buffer;
    uint8_t *pixelG_buffer;
    uint8_t *pixelB_buffer;

    // Drops
    uint8_t *drop;
    uint8_t *dropBrightness;

    // Rings
    uint8_t *ring;
    uint8_t *ringBrightness;
    uint8_t *ringDistance;

    uint8_t FontChar;

    float PlasmaPhase;
    float SavedPlasmaPhase;
    float PlasmaPhaseIncrement;
    float SavedPlasmaPhaseIncrement;
    float PlasmaColorStretch;
    float SavedPlasmaColorStretch;


    uint32_t DimColor(uint32_t color);
    void Increment();
    void (*OnComplete)();      // Callback on completion of pattern
    void (*OnDebugOutput)(String);      // Callback on completion of pattern

    // Convenient 2D point structure
    struct Point {
      float x;
      float y;
    };
};


#endif
