#include <io.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>

/****************************************************/

#define     LED_BIT               (BIT0|BIT1|BIT2|BIT4|BIT5|BIT6|BIT7)
#define     KNOCK_BIT             BIT3  // to match contact switch on launchpad

/****************************************************/

// WDT Timer providing a system tick
#define WDT_MS 8 
#define TICKS_PER_SEC (1000/WDT_MS)

// Timer0 ticks
#define BCM_TICKS_PER_SEC (1000000 / TACCR0)    // 1MHz clock

/****************************************************/

static volatile uint32_t sys_ticks = 0; // global tick counter

/****************************************************/

volatile static uint8_t led_brightness[7] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00};
volatile static uint8_t bcm_ticks = 0;

volatile static uint32_t pattern_period = 0;

/****************************************************/

static void init(void);
static void sim(void);

static void cpu_init(void)
{
    WDTCTL = WDTPW + WDTHOLD; // Stop WDT

    // configure system clock
    BCSCTL1 = CALBC1_1MHZ; // Set range
    DCOCTL = CALDCO_1MHZ; // SMCLK = DCO = 1MHz
}

// Binary Code Modulation
static void bcm_tick(uint8_t led_ticks)
{
    uint8_t bcm = 0x00;

    switch(led_ticks)
    {
        case 0x1:
        case 0x2:
        case 0x4:
        case 0x8:
        case 0x10:
        case 0x20:
        case 0x40:
        case 0x80:
            // led_ticks is a power of 2
            if (led_brightness[0] & led_ticks)
                bcm |= BIT0;
            if (led_brightness[1] & led_ticks)
                bcm |= BIT1;
            if (led_brightness[2] & led_ticks)
                bcm |= BIT2;
            if (led_brightness[3] & led_ticks)
                bcm |= BIT4;
            if (led_brightness[4] & led_ticks)
                bcm |= BIT5;
            if (led_brightness[5] & led_ticks)
                bcm |= BIT6;
            if (led_brightness[6] & led_ticks)
                bcm |= BIT7;

            P1OUT = bcm;
    }
}

// Timer0 ISR
interrupt(TIMERA0_VECTOR) TIMERA0_ISR(void)
{
    bcm_ticks++;
    bcm_tick(bcm_ticks);

    if ((bcm_ticks & 0x80) == 0x80)
        sys_ticks++;
}

// Port1 ISR
interrupt(PORT1_VECTOR) P1_ISR(void)
{
    if((P1IFG & KNOCK_BIT) == KNOCK_BIT)
    {
        init();
        led_brightness[0] = rand()%0xFF;
        led_brightness[1] = rand()%0xFF;
        led_brightness[2] = rand()%0xFF;
        led_brightness[3] = rand()%0xFF;
        led_brightness[4] = rand()%0xFF;
        led_brightness[5] = rand()%0xFF;
        led_brightness[6] = rand()%0xFF;
    }
    P1IFG = 0x00;   // clear interrupt flags
}

/*********************/

static volatile uint8_t flies[7];

static void init(void)
{
    flies[0] = rand()%0xFF;
    flies[1] = rand()%0xFF;
    flies[2] = rand()%0xFF;
    flies[3] = rand()%0xFF;
    flies[4] = rand()%0xFF;
    flies[5] = rand()%0xFF;
    flies[6] = rand()%0xFF;

    pattern_period = 256;
    pattern_period += bcm_ticks%2048;    // don't trust rand
}

static void sim(void)
{
    uint8_t i, j;

    for (i=0;i<7;i++)
    {
        // on flash square
        if (flies[i] == 0)
        {
            led_brightness[i] = (rand()%0x80) + 0x80;

            // move every other one on
            for (j=0;j<7;j++)
            {
                uint8_t n;

                if (i==j)   // not self
                    continue;

               if (flies[j] > 0 && flies[j] <= 64)
                    n = 1;
                else
                if (flies[j] > 64 && flies[j] <= 128)
                    n = 2;
                else
                if (flies[j] > 128 && flies[j] <= 192)
                    n = 3;
                else
                if (flies[j] > 192 && flies[j] < 256)
                    n = 4;

                if ((flies[j] + n) >= 255)
                    flies[j] = 0xFF;
                else
                    flies[j] += (n-1);

            }
        }
    }

    // move each fly along
    for (i=0;i<7;i++)
        flies[i]++;
}


int main(void)
{
    uint32_t pattern_counter = 0;
    uint32_t decay_counter = 0;
    uint16_t steps_taken = 0;

    cpu_init();
      
    // setup LED pins
    P1DIR |= LED_BIT;   // All LED pins as outputs
    P1OUT &= ~LED_BIT;  // Turn off LED

    P1DIR &= ~KNOCK_BIT; // set to input
    P1IES |= KNOCK_BIT;   // interrupt on hi to lo edge
    P1IE |= KNOCK_BIT;    // interrupt enable

    // TimerA SMCLK in UP mode
    TACTL = TASSEL_2 | MC_1;
    // Enable interrupt for TACCR0 match
    TACCTL0 = CCIE;
    // Set TACCR0, starts timer
    TACCR0 = 80;    // This must be short enough to look good and long enough for TIMER0_ISR to complete

    init();

    eint();

    while(1)
    {
        uint8_t i;

        if (sys_ticks > pattern_counter)
        {
            sim();
            pattern_counter = sys_ticks + pattern_period;
            steps_taken++;
            if (steps_taken > 2000)    // after a number of steps, restart
            {
                init();
                steps_taken = 0;
            }
        }
        if (sys_ticks > decay_counter)
        {
            for (i=0;i<7;i++)
            {
                if (led_brightness[i] > 0)
                    led_brightness[i]--;
            }
            decay_counter = sys_ticks + 30;
        }
    }
}


