#include <AccelStepper.h> //http://airspayce.com/mikem/arduino/AccelStepper/classAccelStepper.html
#include <DS3231.h>   //www.rinkydinkelectronics.com


//I have encountered servo motors that rotate in the opposite direction. If so, swap defines for pin1 - pin2 and swap defines for pin3 - pin4

//ten hour left
#define motor1Pin1 46 // IN1 on the ULN2003 driver
#define motor1Pin2 48 // IN2 on the ULN2003 driver
#define motor1Pin3 50 // IN3 on the ULN2003 driver
#define motor1Pin4 52 // IN4 on the ULN2003 driver

//ten hour right
#define motor2Pin1 38
#define motor2Pin2 40
#define motor2Pin3 42
#define motor2Pin4 44

//hour left
#define motor3Pin1 30
#define motor3Pin2 32
#define motor3Pin3 34
#define motor3Pin4 36

//hour right
#define motor4Pin1 22
#define motor4Pin2 24
#define motor4Pin3 26
#define motor4Pin4 28

//ten minute left
#define motor5Pin1 23
#define motor5Pin2 25
#define motor5Pin3 27
#define motor5Pin4 29

//ten minute right
#define motor6Pin1 31
#define motor6Pin2 33
#define motor6Pin3 35
#define motor6Pin4 37

//minute left
#define motor7Pin1 39
#define motor7Pin2 41
#define motor7Pin3 43
#define motor7Pin4 45

//minute right
#define motor8Pin1 47
#define motor8Pin2 49
#define motor8Pin3 51
#define motor8Pin4 53


#define STEPS 1128
#define OFFSET 1060
#define HALFSTEP 8

const bool twelve_hour_mode = false;

const byte positionLeft[10] = {5,0,7,9,3,11,5,1,5,11};
const byte positionRight[10] = {6,0,11,7,4,9,9,2,1,1};

const float speedLeft[13] = {700,600,500,400,700,700,700,400,700,700,300,700,700};
const float speedRight[13] = {600,700,700,700,400,500,300,700,300,300,600,300,400};

const unsigned long durationLeft[13] = {11500,11000,17000,6500,11500,14500,11500,12000,8000,11500,8000,8000,11500};
const unsigned long durationRight[13] = {11000,11500,19500,8000,9500,12500,0,13000,4500,0,11500,4500,9500};


DS3231 rtc(SDA, SCL);


class Digit
{
  AccelStepper stepper_left, stepper_right;
  byte position_left, position_right;
  float speed_left, speed_right;
  unsigned long next_start_left=0, next_start_right=0;
  int current_digit = -1;
  int index=0;
  
  public:
    Digit(uint8_t m1P1, uint8_t m1P3, uint8_t m1P2, uint8_t m1P4, uint8_t m2P1, uint8_t m2P3, uint8_t m2P2, uint8_t m2P4):stepper_left(HALFSTEP, m1P1, m1P3, m1P2, m1P4), stepper_right(HALFSTEP, m2P1, m2P3, m2P2, m2P4)
    {
    }

 
    void setup()
    {
      stepper_left.setMaxSpeed(700);
      stepper_left.setAcceleration(400);
      stepper_left.runToNewPosition(16000);
      stepper_left.setCurrentPosition(OFFSET);

      stepper_right.setMaxSpeed(700);
      stepper_right.setAcceleration(400);
      stepper_right.runToNewPosition(-16000);
      stepper_right.setCurrentPosition(-1 * OFFSET);
    }


    void ShowDigit(int digit)
    {
      if(digit!=current_digit)
      {
        position_left = positionLeft[digit];
        position_right = positionRight[digit];
        if(current_digit == -1)
        {
          speed_left = speed_right = 700;
          next_start_left = next_start_right = millis();
        }
        else
        {
          int index = digit;
          if(digit == 0)
          {
            if(current_digit == 2)
              index = 10;
            else if(current_digit == 3)
              index = 11;
            else if(current_digit == 5)
              index = 12;
            else if(current_digit == 1)
              index = 1;
          }
          else if(digit == 1 && current_digit == 2)
            index = 2;

          speed_left = speedLeft[index];
          speed_right = speedRight[index];
          next_start_left = millis() + 60000 - durationLeft[index];
          next_start_right = millis() + 60000 - durationRight[index];
        }
        current_digit = digit;
      }
    }


    void loop()
    {
      stepper_left.run();
      stepper_right.run();
      if (next_start_left != 0 && millis() > next_start_left)
      {
        next_start_left = 0;
        stepper_left.setMaxSpeed(speed_left);
        stepper_left.moveTo(position_left*STEPS * -1);
      }
      if (next_start_right != 0 && millis() > next_start_right)
      {
        next_start_right = 0;
        stepper_right.setMaxSpeed(speed_right);
        stepper_right.moveTo(position_right * STEPS);
      }
    }
};


Digit minuteDigit(motor7Pin1, motor7Pin3, motor7Pin2, motor7Pin4, motor8Pin1, motor8Pin3, motor8Pin2, motor8Pin4);
Digit tenMinuteDigit(motor5Pin1, motor5Pin3, motor5Pin2, motor5Pin4, motor6Pin1, motor6Pin3, motor6Pin2, motor6Pin4);
Digit hourDigit(motor3Pin1, motor3Pin3, motor3Pin2, motor3Pin4, motor4Pin1, motor4Pin3, motor4Pin2, motor4Pin4);
Digit tenHourDigit(motor1Pin1, motor1Pin3, motor1Pin2, motor1Pin4, motor2Pin1, motor2Pin3, motor2Pin2, motor2Pin4);


class Clock
{
  unsigned long old_ms = 0;
  int current_min;

  public:

    void setup()
    {
      rtc.begin();
      tenHourDigit.setup();
      hourDigit.setup();
      tenMinuteDigit.setup();
      minuteDigit.setup();
      Time t = rtc.getTime();
      current_min=t.min;
    }


    void displayTime()
    {
      if (millis() - old_ms > 1000)
      {
        old_ms = millis();
        Time t = rtc.getTime();
        if (t.min != current_min)
        {
          current_min = t.min;
          if((t.min = ++t.min%60) == 0) //add one minute
            t.hour = ++t.hour%24;

          if(twelve_hour_mode)
          {
            t.hour = t.hour%12;
            if(t.hour == 0)
              t.hour = 12;
          }

          int ten_hour = t.hour/10;
          int hour = t.hour%10;
          int ten_min = t.min/10;
          int min = t.min%10;
          minuteDigit.ShowDigit(min);
          tenMinuteDigit.ShowDigit(ten_min);
          hourDigit.ShowDigit(hour);
          tenHourDigit.ShowDigit(ten_hour);
        }
      }
    }

    
    void loop()
    {
      displayTime();
      minuteDigit.loop();
      tenMinuteDigit.loop();
      hourDigit.loop();
      tenHourDigit.loop();
    }
};

Clock clock;


void setup()
{
  delay(2000);
  clock.setup();
}


void loop()
{
  clock.loop();
}
