#include "spi_matrix.h"

#define DIGITS 8

void matrix_init(matrix_t *matrix, buffered_transceiver_t *transceiver,
                 uint32_t shift_period) {
  matrix->transceiver = transceiver;
  matrix->shift_period = shift_period;
  matrix->slot = MATRIX_SLOT0;
  matrix->state = MATRIX_OFF;

  matrix->state_cycle = 0;
  matrix->cycle = 0;

  matrix->intensity = 1;
}

void matrix_setup(matrix_t *matrix) {
  matrix->state = MATRIX_OFF;
  matrix->slot = MATRIX_SLOT0;
}

void matrix_enable(matrix_t *matrix, bool enable) {
  matrix->state = MATRIX_STABLE;
  matrix->slot = MATRIX_SLOT0;

  matrix_send(matrix, MATRIX_REG_SHUTDOWN, enable ? 1 : 0);
  matrix_send(matrix, MATRIX_REG_DECODE_MODE, 0);
  matrix_send(matrix, MATRIX_REG_INTENSITY, matrix->intensity);
  matrix_send(matrix, MATRIX_REG_SCAN_LIMIT, 0x7);
}

void matrix_increase_intensity(matrix_t *matrix) {
  if (matrix->intensity < 0xF) {
    matrix->intensity++;
  }

  matrix_send(matrix, MATRIX_REG_INTENSITY, matrix->intensity);
}

void matrix_decrease_intensity(matrix_t *matrix) {
  if (matrix->intensity > 0x0) {
    matrix->intensity--;
  }

  matrix_send(matrix, MATRIX_REG_INTENSITY, matrix->intensity);
}

void matrix_send(matrix_t *matrix, matrix_register_t reg, uint8_t value) {
  uint16_t data = (((uint16_t)reg) << 8) | (uint16_t)value;
  buffered_transceiver_transmit(matrix->transceiver, (void*)&data, 1);
}

void matrix_set_buffer(matrix_t *matrix, matrix_slot_t slot, uint8_t *buffer) {
  if (slot == MATRIX_SLOT_OTHER) {

    if (matrix->slot == MATRIX_SLOT0) {
      slot = MATRIX_SLOT1;
    } else {
      slot = MATRIX_SLOT0;
    }
  }

  matrix->buffers[slot] = buffer;
}

void matrix_swap(matrix_t *matrix) {
  if (matrix->slot == MATRIX_SLOT0) {
    matrix->slot = MATRIX_SLOT1;
  } else {
    matrix->slot = MATRIX_SLOT0;
  }
}

void matrix_animate_swap(matrix_t *matrix) {
  matrix->state_cycle = 0;
  matrix->cycle = 0;
  matrix->state = MATRIX_ANIMATING;
}

void matrix_update(matrix_t *matrix, buffered_transceiver_t *transceiver) {
  uint8_t* buffer = matrix->buffers[matrix->slot];
  uint8_t* other_buffer = matrix->buffers[1 - matrix->slot];

  switch (matrix->state) {
  case MATRIX_OFF:
    // all digits 0
    for (uint8_t digit = 0; digit < DIGITS; digit++) {
      matrix_send(matrix, MATRIX_REG_DIGIT0 + digit, 0);
    }
    break;
  case MATRIX_STABLE:
    // send all digits
    for (uint8_t digit = 0; digit < DIGITS; digit++) {
      matrix_send(matrix, MATRIX_REG_DIGIT0 + digit, buffer[digit]);
    }
    break;
  case MATRIX_ANIMATING:
    // update the cycle
    matrix->cycle++;
    if (matrix->cycle >= matrix->shift_period) {
      matrix->cycle = 0;
      matrix->state_cycle++;
    }

    if (matrix->state_cycle == DIGITS) {
      matrix->state = MATRIX_STABLE;
      matrix_swap(matrix);
    }

    for (uint8_t digit = 0; digit < DIGITS - matrix->state_cycle; digit++) {
      matrix_send(matrix, MATRIX_REG_DIGIT0 + digit, buffer[digit + matrix->state_cycle]);
    }

    for (uint8_t digit = DIGITS - matrix->state_cycle; digit < DIGITS; digit++) {
      matrix_send(matrix, MATRIX_REG_DIGIT0 + digit, other_buffer[digit - (DIGITS - matrix->state_cycle)]);
    }
    break;
  }
}
