/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVBoxy.cpp
 * Description:
 *     Movie -- mv_Boxy
 *         Randonly display a growing and shrinking box.
 *         (as well as you can do in 3x3x3).
 *
 * 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_Boxy()
{
  unsigned long ts;
  unsigned long tbox = 1; // 0xB000000000000000000000000001;
  unsigned long sbox = 13851; //0XB000000000000011011000011011;
  unsigned long lbox = 129948655; //0XB111101111101101101111101111;
  int spd;
  POSTransFPtr trans;

  trans = GetPOSTrans(random(0, 8));
  
  ts = millis() + (DEF_MOVIE_TIME / 2);

  while (millis() < ts)
  {
    spd = random(50, 90);
 
    df_DisplayMonoFrame(trans, tbox);   
    delay(spd);
    df_DisplayMonoFrame(trans, sbox);   
    delay(spd);
    df_DisplayMonoFrame(trans, lbox);   
    delay(spd * 4);
    
    trans = GetPOSTrans(random(0, 8));
    df_DisplayMonoFrame(trans, sbox);
    delay(spd);
    df_DisplayMonoFrame(trans, tbox);
    delay(spd);
    df_clear();
    
    delay(spd);
  }
}
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: Movies.h
 * Description: Definitions for movie functions.
 *
 * 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.
 */
 
#ifndef Movies_h
#define Movies_h

#define DEF_MOVIE_TIME 10000 

void mv_TwinkleAll();
void mv_Glitter();
void mv_Pulse();
void mv_Jitter();
void mv_EdgeChase();
void mv_EdgeChase2();
void mv_Rain();
void mv_AllOn();
void mv_Flashy();
void mv_EdgeTumble();
void mv_Counter();
void mv_Snake();
void mv_Boxy();
void mv_Lightning();
#endif
/* Project: 3x3x3 Mono LED Cube (r3).
 * File: MVTwinkleAll.cpp
 * Description:
 *     Movie -- mv_TwinkleAll
 *         All LEDs are randomly faded in an out.
 *         Similar to mv_Flashy, but with a subtle difference.
 *
 * 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_TwinkleAll()
{
  unsigned long ts;
  byte x;
  byte m[27];
  
  ts = millis() + DEF_MOVIE_TIME;

  for (x = 0 ; x < 27 ; x++)
    m[x] = (displayFrame[x] == MAXBRIGHTNESSLEVEL);

  while (millis() < ts)
  {
    for (x = 0 ; x < 27 ; x++)
    {
      if (m[x])
      {
        if (random(10) > 6)
        {
          if (--displayFrame[x] == 0)
           m[x] = false;
        }
      }
      else
      {
        if (random(10) > 4)
        {
          if (++displayFrame[x] >= MAXBRIGHTNESSLEVEL)
            m[x] = true;
        }
      }
    }
    
    delay(50);
  }

  df_fadeOut();
}
/* 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;
}