#include <timeObj.h>
#include <mapper.h>
#include <idlers.h>
#include <blinker.h>

#define CHECK_MS        10       // How often we look. Must be small number.
#define CHANCE_TO_LIGHT 2        // We have a chance of lighting ever "CHANCE_TO_LIGHT"
#define OUT_OF          5000     // out of "OUT_OF" times we check.
#define FADE_MS         2000      // We fade in and out about this many Ms.
#define FADE_DITHER     50       // How much in ms +/- to randomise fade.
#define LITE_MS_LOW     1000      // Ms
#define LITE_MS_HIGH    1000     // Ms

mapper   fadeInMapper(0,FADE_MS,0,255);
mapper   fadeOutMapper(0,FADE_MS,255,0);

class firefly :   public idler {

   public:
            enum  flyStates { liteOff, fadeIn, liteOn, fadeOut };
            
            firefly(void);
            ~firefly(void);

            void  begin(int inPinNum);
   virtual  void  idle();

            flyStates   flyState;
            int         pinNum;
            int         fadeInMs;
            int         fadeOutMs;
            int         holdlitMs;
            timeObj     checkTimer;
            int         lightTime;
};


firefly::firefly(void) { flyState = liteOff; }


firefly::~firefly(void) { }


void firefly::begin(int inPinNum) {

   pinNum = inPinNum;
   pinMode(pinNum,OUTPUT);
   digitalWrite(pinNum,LOW);
   checkTimer.setTime(CHECK_MS);
   hookup();  
}


void firefly::idle(void) {

   int lightValue;
   
   if (checkTimer.ding()) {
      checkTimer.start();
      switch (flyState) {
         case liteOff   :
            if (random(0,OUT_OF)<=CHANCE_TO_LIGHT) {
               lightTime = 0;
               fadeInMs = random(FADE_MS-50,FADE_MS+50);
               fadeOutMs = random(FADE_MS-50,FADE_MS+50);
               holdlitMs = random(LITE_MS_LOW,LITE_MS_HIGH);
               flyState = fadeIn;
            }
         break;
         case fadeIn    :
            if (lightTime<=fadeInMs) {
               lightValue = fadeInMapper.map(lightTime);
               analogWrite(pinNum,lightValue);
               lightTime = lightTime + CHECK_MS;
            } else {
               lightTime = 0;
               flyState = liteOn;
            }
            break;
         case liteOn    :
            if (lightTime<=holdlitMs) {
               lightTime = lightTime + CHECK_MS;
            } else {
               lightTime = 0;
               flyState = fadeOut;
            }
            break;
         case fadeOut   :
            if (lightTime<=fadeOutMs) {
               lightValue = fadeOutMapper.map(lightTime);
               analogWrite(pinNum,lightValue);
               lightTime = lightTime + CHECK_MS;
            } else {
               lightTime = 0;
               analogWrite(pinNum,0);
               flyState = liteOff;
            }
         break;
      }
   }
}

// *************************************************
// THE MAIN PROGRAM
// *************************************************

firefly fireflyArray[13];
blinker  heartbeat;      // Flashes pin 13 to show everything is still alive.

void setup(void) {

   fireflyArray[0].begin(0);
   fireflyArray[1].begin(1);
   fireflyArray[2].begin(2);
   fireflyArray[3].begin(3);
   fireflyArray[4].begin(4);
   fireflyArray[5].begin(5);
   fireflyArray[6].begin(6);
   fireflyArray[7].begin(7);
   fireflyArray[8].begin(8);
   fireflyArray[9].begin(9);
   fireflyArray[10].begin(10);
   fireflyArray[11].begin(11);
   fireflyArray[12].begin(12);
   heartbeat.setOnOff(true);
}

void loop(void) { idle(); }