//============================================================================
//Copyright © 2012, open source hardware engineering
// 
//This application is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 3 of the License, or (at your option) any later version.
//
//This application is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR When PARTICULAR PURPOSE. See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, see <http://www.gnu.org/licenses/>
//
//=============================================================================
#include "Arduino.h"

extern "C" {
  #include "utility/wxspi.h"
}

#include "WxSpi.h"

WxSpiInterface::WxSpiInterface() { }

void WxSpiInterface::declare_slave_pin(byte pin)
{
  wxspi_setup_ss_pin(pin); // setup the hardware port
  slave_pin = pin;       // record this as the current slave pin
}

void WxSpiInterface::select_slave(byte pin)
{
  // set the pin number to be used for subsequent slave selection
  slave_pin = pin;
}

unsigned long WxSpiInterface::begin(unsigned long clock_rate_hz, byte mode)
{
  if (mode > 3) return 0UL;
  //
  // divide the cpu clock rate successively by powers of two from
  // 2 up to 128, stopping as soon as the divided rate <= clock_rate_hz.
  // 
  unsigned long clkHz = F_CPU >> 1;
  byte log2div;

  for (log2div = 1; log2div < 7 && clkHz > clock_rate_hz; log2div++) 
  {
    clkHz = clkHz >> 1;
  }

  wxspi_setup(log2div, mode);
  return ((unsigned long)F_CPU) >> log2div;
}

int WxSpiInterface::flip_count() { return get_flip_count(); }

boolean WxSpiInterface::busy() { return wxspi_interface_busy(); }

boolean WxSpiInterface::overflow() { return wxspi_overflow(); }

void WxSpiInterface::abort() { wxspi_abort(); }

boolean WxSpiInterface::begin_write(const unsigned int *commands)
{
  if (wxspi_interface_busy()) return false;
  start_wxspi_transaction(slave_pin, commands);
  return true;
}

byte WxSpiInterface::read_count()
{
  byte rc = (byte)0;
  if (!wxspi_interface_busy()) 
  {
    rc = wxspi_data_available();
  }
  return rc;
}

boolean WxSpiInterface::read(byte *buffer, byte buffer_length)
{
  boolean rc = false;
  if (!wxspi_interface_busy() && (wxspi_data_available() <= buffer_length))
  {
    get_wxspi_data(buffer, buffer_length);
    rc = true;
  }
  return rc;
}

boolean WxSpiInterface::write_read(const unsigned int *commands, byte *replies, byte max_reply_length)
{
  while (wxspi_interface_busy());

#if 0
  unsigned int *tmp;
  byte *btmp;
  Serial.print("SPI Out: ");
  for (tmp = (unsigned int*)commands; true; tmp++)
  {
	  Serial.print(*tmp & 0xff,HEX);
	  Serial.print(" ");
	  if (*tmp & SPI_LAST) break;
  }
  Serial.println(".");
#endif

  start_wxspi_transaction(slave_pin, commands);
  while (wxspi_interface_busy());
  boolean rc = false;
  if (!wxspi_overflow() && (wxspi_data_available() <= max_reply_length))
  {  
    get_wxspi_data(replies, max_reply_length);
    rc = true;
  }

#if 0
  Serial.print("SPI In: ");
  btmp = replies;
  for (tmp = (unsigned int*)commands; true; tmp++, btmp++)
  {
	  Serial.print(*btmp,HEX);
	  Serial.print(" ");
	  if (*tmp & SPI_LAST) break;
  }
  Serial.println(".");
#endif

  return true;
}

boolean WxSpiInterface::read_wait(byte *replies, byte max_reply_length)
{
  while (wxspi_interface_busy());
  if (wxspi_overflow()) return false;
  if (wxspi_data_available() > max_reply_length) return false;
  get_wxspi_data(replies, max_reply_length);
  return true;
}

void WxSpiInterface::abort_wait()
{
  wxspi_abort();
  while (wxspi_interface_busy());
}

//
// create pre-instantiated object
//

WxSpiInterface WxSpi = WxSpiInterface();
