/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVSnake.cpp
 * Description:
 *     Movie -- mv_Snake
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */
 
#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

#define SNAKELEN 4

void mv_Snake()
{
  unsigned long ts, t;
  int ss, sl, sp, i, j, k;
  byte nx, ny, nz;
 
  ts = millis() + DEF_MOVIE_TIME;

  points[0] = Point((byte)random(0, 3), (byte)random(0, 3), (byte)random(0, 3));
  sl = 1;
  ss = 0;
  displayFrame[points[0].Pos()] = MAXBRIGHTNESSLEVEL;
  
  sp = random(100, 500);
  
  while (millis() < ts)
  {
    t = millis() + sp;
    
    do
    {
      nx = points[ss].x;
      ny = points[ss].y;
      nz = points[ss].z;
      
      switch(random(0, 3))
      {
        case 0:
          nx = nx + random(0, 3) - 1;
          break;
          
        case 1:
          ny = ny + random(0, 3) - 1;
          break;
          
        default:
          nz = nz + random(0, 3) - 1;
          break;
      }
    } while (millis() < t && (nx > 2 || ny > 2 || nz > 2 || displayFrame[POS(nx, ny, nz)] != 0));
    
    while (millis() < t)
      ;
      
    if (nx > 2 || ny > 2 || nz > 2 || displayFrame[POS(nx, ny, nz)] != 0)
    {
       for (i = 0 ; i < 27 ; i++)
         displayFrame[i] = MAXBRIGHTNESSLEVEL;
         
       delay(50);
       
       df_clear();

       points[0] = Point((byte)random(0, 3), (byte)random(0, 3), (byte)random(0, 3));
       sl = 1;
       ss = 0;
       displayFrame[points[ss].Pos()] = MAXBRIGHTNESSLEVEL;
    }
    else
    {
      i = ss;
      for (j = 0 ; j < sl ; j++)
      {
        k = points[i].Pos();
        if (displayFrame[k] > 0)
          --displayFrame[k];
        if (displayFrame[k] > 0)
          --displayFrame[k];

        if (i == 0)
          i = SNAKELEN - 1;
        else
          --i;
      }
      
      if (++ss >= SNAKELEN)
        ss = 0;
      
      points[ss].Set(nx, ny, nz);
      displayFrame[points[ss].Pos()] = MAXBRIGHTNESSLEVEL;
      
      if (sl < SNAKELEN)
        ++sl;
    }
  }
  
  df_clear();
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVRain.cpp
 * Description:
 *     Movie -- mv_Rain
 *         A number of LEDs are lit on the top plane, and they
 *         then "fall down" the cude.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */
 
#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

class Drop
{
  public:
    byte pos;
    byte count;
    unsigned long time;
    byte spd;
    
  void reset()
  {
    pos = POS(2, random(0, 3), random(0, 3));
    count = 2;
    spd = random(30, 100);
    time = millis() + spd;
  }
  
  void displayDrop(boolean state)
  {
    displayFrame[pos] = state ? MAXBRIGHTNESSLEVEL : 0;
  }
  
  void drop()
  {
    if (millis() > time)
    {
      if (spd == 0)
      {
        reset();
        displayDrop(true);
      }
      else
      {
        displayDrop(false);
        if (count == 0)
        {
          spd = 0;
          time = millis() + random(50, 100);
        }
        else
        {
          --count;
          pos -= 3;
          displayDrop(true);
          time = millis() + spd;
        }
      }
    }
  }
};

void mv_Rain()
{
  unsigned long ts;
  byte y;
  Drop drops[2];
  
  ts = millis() + (DEF_MOVIE_TIME * 2);

  for (y = 0 ; y < 2 ; y++)
  {
    drops[y].reset();
    drops[y].displayDrop(true);
  }
 
  while (millis() < ts)
  {
    for (y = 0 ; y < 2 ; y++)
    {
      drops[y].drop();
    }
  }

  df_clear();
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVPulse.cpp
 * Description:
 *     Movie -- mv_Pulse
 *         Fade in and out all LEDs together.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */
 
#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_Pulse()
{
  unsigned long ts;
  byte x, y;
  
  ts = millis() + (DEF_MOVIE_TIME / 2);

  while (millis() < ts)
  {
    for (y = 0 ; y <= MAXBRIGHTNESSLEVEL ; y++)
    {
      for (x = 0 ; x < 27 ; x++)
        displayFrame[x] = y;
        
      delay(50);
    }

    delay(100);
    
    for (y = MAXBRIGHTNESSLEVEL - 1 ; y > 0 ; y--)
    {
      for (x = 0 ; x < 27 ; x++)
        displayFrame[x] = y;
        
      delay(50);
    }

    df_clear();
    
    delay(500);
  }
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVJitter.cpp
 * Description:
 *     Movie -- mv_Jitter
 *         A single LED moves quickly and randomly around the
 *         cube.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_Jitter()
{
  unsigned long ts, t;
  byte x, y, z, nx, ny, nz;
  
  ts = millis() + DEF_MOVIE_TIME;

  x = random(0, 3);
  y = random(0, 3);
  z = random(0, 3);
  nx = x;
  ny = y;
  nz = z;

  displayFrame[POS(x, y, z)] = MAXBRIGHTNESSLEVEL;
  
  while (millis() < ts)
  {
    t = millis() + 80;
        
    do
    {
      // Work out the next LED to light, adjacent to the current LED.
      
      switch(random(0, 3))
      {
        case 0:
          switch(random(0, 3))
          {
            case 0:
              if (nx > 0)
                --nx;
               break;
            case 2:
              if (nx < 2)
                ++nx;
              break;
          }
          break;
    
        case 1:
          switch(random(0, 3))
          {
            case 0:
              if (ny > 0)
                --ny;
              break;
            case 2:
              if (ny < 2)
                ++ny;
              break;
          }
          break;
          
        default:
          switch(random(0, 3))
          {
            case 0:
              if (nz > 0)
                --nz;
              break;
            case 2:
              if (nz < 2)
                ++nz;
              break;
          }
          break;
      }
    } while ( x == nx && y == ny && z == nz);
    
    while (millis() < t)
      ;
      
    displayFrame[POS(x,y,z)] = 0;
    displayFrame[POS(nx,ny,nz)] = MAXBRIGHTNESSLEVEL;
    
    x = nx;
    y = ny;
    z = nz;
  }

  displayFrame[POS(nx,ny,nz)] = 0;
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVGlitter.cpp
 * Description:
 *     Movie -- mv_Glitter
 *         Lights random LEDs for short periods of time creating
 *         a "glitter" effect.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistribtued, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_Glitter()
{
  unsigned long ts;
  byte x;
  byte m[3];
  byte n[3] = {0, 0, 0};
  
  ts = millis() + DEF_MOVIE_TIME;

  while (millis() < ts)
  {
    for (x = 0 ; x < 3 ; x++)
    {
      if (n[x] == 0)
      {
        displayFrame[m[x]] = 0;
        m[x] = random(0, 27);
        n[x] = random(30, 50);
        displayFrame[m[x]] = MAXBRIGHTNESSLEVEL;
      }
      else
        --n[x];
    }
    delay(1);
  }
  
  for (x = 0 ; x < 3 ; x++)
    displayFrame[m[x]] = 0;
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVFlashy.cpp
 * Description:
 *     Movie -- mv_Flashy
 *         Fades all LEDs in and out at random speeds.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistribtued, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_Flashy()
{
  unsigned long ts;
  byte y;
  Light lights[27];
  
  for (y = 0 ; y < 27 ; y++)
  {
    lights[y].speed = random(50, 200);
    lights[y].up = true;
    lights[y].t = millis() + lights[y].speed;
  }
  
  ts = millis() + DEF_MOVIE_TIME;

  y = 0;
  while (millis() < ts)
  {
    if (millis() > lights[y].t)
    {
      displayFrame[y] += (lights[y].up ? 1 : -1);
      if (displayFrame[y] == 0 || displayFrame[y] == 7)
        lights[y].up = !lights[y].up;
        
      lights[y].t = millis() + lights[y].speed;
    }
    
    if (++y > 26)
      y = 0;
  }
  
  df_fadeOut();
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVEdgeTumble.cpp
 * Description:
 *     Movie -- mv_EdgeTumble
 *         LEDs are lit along one edge, and then it "tumbles" from
 *         edge to edge randomly. The LED in the center is faded
 *         in and out.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

const byte TUMBLESPEED = 25;
const byte TUMBLEDELAY = 150;
const byte FLASHERSPEED = 50;

int getN(byte, byte);

int m[7][2][2] = { 
    {{1, 0}, {2, 0}},
    {{1, 0}, {2, 1}},
    {{1, 1}, {2, 1}},
    {{1, 1}, {2, 2}},
    {{1, 1}, {1, 2}},
    {{0, 1}, {1, 2}},
    {{0, 1}, {0, 2}}
};

unsigned long ft = 0;
boolean flashUp = true;
boolean flashOn = true;

void delayAndFlash(int d)
{
  unsigned long t, ct;
  byte a;
  
  t = millis() + d;
  
  do
  {
    ct = millis();
    
    if (ct > ft)
    {
      a = displayFrame[13];
      if (flashUp)
      {
        if (flashOn)
          if (++a >= MAXBRIGHTNESSLEVEL)
            flashUp = false;
      }
      else
      {
        if (--a < 1)
          flashUp = true;
      }
      displayFrame[13] = a;
      
      ft = ct + FLASHERSPEED;
    }
  } while (ct < t);
}

void mv_EdgeTumble()
{
  unsigned long ts;
  byte a, b, f, l;
  int nx, ny, nz;
  int px, py, pz;
  byte fn, fd;
  Point p[3], v;
  
  ft = millis() + 1000;
  flashUp = (displayFrame[13] < MAXBRIGHTNESSLEVEL);
  flashOn = true;
    
  ts = millis() + (DEF_MOVIE_TIME * 2);
  l = 7;
  
  // Random start point.
  
  a = random(0, 8);
  p[0].Set((a & 1) * 2, (a & 2), (a & 4) / 2);
  
  // Random starting direction.
  
  nx = 0;
  ny = 0;
  nz = 0;
  switch(random(0, 3))
  {
    case 0:
      nx = (p[0].x == 0) ? 1 : -1;
      break;
      
    case 1:
      ny = (p[0].y == 0) ? 1 : -1;
      break;
      
    case 2:
      nz = (p[0].z == 0) ? 1 : -1;
      break;
  }
  
  // Draw initial line.
  
  for (a = 1 ; a < 3 ; a++)
    p[a].Set(p[0].x+(nx*a), p[0].y+(ny*a), p[0].z+(nz*a));
    
  displayFrame[p[0].Pos()] = 7;
  displayFrame[p[1].Pos()] = 7;
  displayFrame[p[2].Pos()] = 7;
  
  while (l > 0)
  {
    if (millis() > ts)
    {
      --l;
      flashOn = false;
    }

    px = 0;
    py = 0;
    pz = 0;

    fn = 1;
    fd = 1;
        
    switch(random(0, 2))
    {
      case 0:
        px = (p[0].x == 0) ? 1 : -1;

        if (nx != 0 || nz != 0)
          pz = (p[0].z == 0) ? 1 : -1;

        if (ny != 0)
          py = (p[0].y == 0) ? 1 : -1;
          
        if (ny != 0 || nz != 0)
        {
          fn = 5;
          fd=255;
        }
        break;
        
      case 1:
        py = (p[0].y == 0) ? 1 : -1;

        if (nx != 0)
          px = (p[0].x == 0) ? 1 : -1;
        
        if (ny != 0 || nz != 0)
          pz = (p[0].z == 0) ? 1 : -1;
          
        if (nz != 0)
        {
          fn = 5;
          fd=255;
        }
        break;
    }
       
    displayFrame[p[0].Pos()] = l;
    
    for (f = 0 ; f < 6 ; f++, fn += fd)
    {
      for (a = 1, b = 0 ; a < 3 ; a++, b++)
      {
        displayFrame[p[a].Pos()] = 0;
        
        if (px == 0)
        {
          p[a].y = p[0].y + (m[fn][b][0] * py);
          p[a].z = p[0].z + (m[fn][b][1] * pz);
        }
        else if (py == 0)
        {
          p[a].x = p[0].x + (m[fn][b][0] * px);
          p[a].z = p[0].z + (m[fn][b][1] * pz);
        }
        else
        {
          p[a].x = p[0].x + (m[fn][b][0] * px);
          p[a].y = p[0].y + (m[fn][b][1] * py);
        }
        
        displayFrame[p[a].Pos()] = l;
      }      
            
      delayAndFlash(TUMBLESPEED);
    }
   
    v = p[0];
    p[0] = p[2];
    p[2] = v; 

    nx = getN(p[0].x, p[2].x);
    ny = getN(p[0].y, p[2].y);
    nz = getN(p[0].z, p[2].z);
    
    delayAndFlash(TUMBLEDELAY);
  }
  
  df_fadeOut();
}

int getN(byte n1, byte n2)
{
  if (n1 == n2)
    return 0;
  else if (n1 < n2)
    return 1;
  else
    return -1;
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVEdgeChase.cpp
 * Description:
 *     Movie -- mv_EdgeChase
 *         LEDs are lit along an edge from one corner to another
 *         at a random speed. After a random delay another edge
 *         is lit. If the lighting of an edge runs in to an already
 *         lit LED, then the display is cleared and it starts again.
 *
 *     Movie -- mv_EdgeChase2
 *         LEDs are lit along an edge from one corner to another,
 *         but as it moves along, each previous LED is dimmed
 *         until it goes out.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_EdgeChase()
{
  unsigned long ts, t;
  byte a, b, x, y, z;
  int nx, ny, nz;
  boolean r = false;
  int d1, d2;
  
  ts = millis() + DEF_MOVIE_TIME;

  d1 = random(40, 120);
  d2 = random(20, 60);
  
  a = random(0, 8);
  
  x = (a & 1) * 2;
  y = (a & 2);
  z = (a & 4) / 2;
  
  a = 3;
  
  displayFrame[POS(x, y, z)] = MAXBRIGHTNESSLEVEL;
  
  while (millis() < ts)
  {
    t = millis() + d1;
        
    nx = 0;
    ny = 0;
    nz = 0;
    
    do
    {
      b = random(0, 3);
    } while (a == b);

    a = b;
    
    switch(a)
    {
      case 0:
        nx = (x == 0) ? 1 : -1;
        break;
        
      case 1:
        ny = (y == 0) ? 1 : -1;
        break;
        
      case 2:
        nz = (z == 0) ? 1 : -1;
        break;
    }
    
    while (millis() < t)
      ;
    
    if (r)
      df_clear();
    
    displayFrame[POS(x, y, z)] = 4;
    displayFrame[POS(x+nx,y+ny,z+nz)] = MAXBRIGHTNESSLEVEL;
    
    delay(d2);
    
    displayFrame[POS(x+nx,y+ny,z+nz)] = 4;
    r = (displayFrame[POS(x+nx+nx,y+ny+ny,z+nz+nz)] != 0);
    displayFrame[POS(x+nx+nx,y+ny+ny,z+nz+nz)] = MAXBRIGHTNESSLEVEL;
    
    delay(d2);
    
    displayFrame[POS(x+nx,y+ny,z+nz)] = 1;
    
    x = x + nx + nx;
    y = y + ny + ny;
    z = z + nz + nz;
  }

  df_clear();
}

void mv_EdgeChase2()
{
  unsigned long ts, t;
  byte a, b, d, i, j, k, x, y, z;
  int nx, ny, nz;
  byte ss, sl;
  
  ts = millis() + DEF_MOVIE_TIME;

  d = random(50, 200);
  
  a = random(0, 8);
  
  x = (a & 1) * 2;
  y = (a & 2);
  z = (a & 4) / 2;
  points[0].Set(x, y, z);
  ss = 0;
  sl = 1;
  
  a = 3;
  
  displayFrame[points[0].Pos()] = MAXBRIGHTNESSLEVEL;

  t = millis() + d;
          
  while (millis() < ts)
  {
    nx = 0;
    ny = 0;
    nz = 0;
    
    do
    {
      b = random(0, 3);
    } while (a == b);

    a = b;
    
    switch(a)
    {
      case 0:
        nx = (x == 0) ? 1 : -1;
        break;
        
      case 1:
        ny = (y == 0) ? 1 : -1;
        break;
        
      case 2:
        nz = (z == 0) ? 1 : -1;
        break;
    }
    
    for (i = 0 ; i < 2 ; i++)
    {
      while (millis() < t)
        ;
        
      j = ss;
      for (k = 0 ; k < sl ; k++)
      {
        if (displayFrame[points[j].Pos()] > 0)
          --displayFrame[points[j].Pos()];
          
        if (j == 0)
          j = 7;
        else
          --j;
      }
      
      if (++ss > 7)
        ss = 0;
      
      if (sl < 8)
        ++sl;
  
      x = x + nx;
      y = y + ny;
      z = z + nz;
  
      points[ss].Set(x, y, z);
      displayFrame[points[ss].Pos()] = MAXBRIGHTNESSLEVEL;
      
      t = millis() + d;
    }
  }

  df_clear();
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVCounter.cpp
 * Description: 
 *     Movie -- mv_Counter
 *         LED on bottom plane moves around the edge of the
 *         plane. On each complete circuit, the LED on the
 *         plane above moves along one, and when that has done
 *         a complete circuit, the led on the top plane moves
 *         along one.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

struct Item
{
  public:
    byte x;
    byte d;
    
    Item() : x(0), d(0) {};
    
    boolean inc()
    {
      boolean o = false;
      
      switch(d)
      {
        case 0:
          if (++x == 2)
            d = 1;
          break;
          
        case 1:
          x += 9;
          if (x > 18)
            d = 2;
          break;
          
        case 2:
          if (--x == 18)
            d = 3;
          break;
          
        case 3:
          x -= 9;
          if (x < 9)
          {
            d = 0;
            o = true;
          }
          break;
      }
      
      return o;
    }
};

void mv_Counter()
{
  Item i[3];
  unsigned long ts, t;
  boolean o;
  int del;
  
  i[0].x = 0;
  i[1].x = 0;
  i[2].x = 0;
  
  displayFrame[i[0].x] = 7;
  displayFrame[i[1].x + 3] = 7;
  displayFrame[i[2].x + 6] = 7;
  
  delay(1000);
  
  del = random(20, 70);
  
  ts = millis() + (DEF_MOVIE_TIME * 2);

  while (millis() < ts)
  {
    t = millis() + del;
    
    displayFrame[i[0].x] = 0;
    o = i[0].inc();
    displayFrame[i[0].x] = 7;
    if (o)
    {
      displayFrame[i[1].x + 3] = 0;
      o = i[1].inc();
      displayFrame[i[1].x + 3] = 7;
      if (o)
      {
        displayFrame[i[2].x + 6] = 0;
        i[2].inc();
        displayFrame[i[2].x + 6] = 7;
      }
    }
    
    while (millis() < t)
      ;
  }  
    
  df_fadeOut();
    
  delay(500);
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVAllOn.cpp
 * Description:
 *     Movie -- mv_AllOn
 *         Turn all LEDs on for a period, then fade out.
 *
 * Copyright (C) 2014 Marc Symonds
 * All rights reserved.
 *
 * This software may be used and redistributed, with or without
 * modification, as long as it is understood that this software
 * is provided as-is without any explicit or implied warranties
 * of merchantablity or fitness of purpose.
 */

#include "Arduino.h"
#include "Movies.h"
#include "DisplayFrame.h"

void mv_AllOn()
{
  unsigned long ts;
  
  ts = millis() + (DEF_MOVIE_TIME / 10);

  df_allOn();
  
  while (millis() < ts)
    ;
    
  df_fadeOut();
    
  delay(500);
}