/*!
  \file
  \brief Wii リモコン

  \author Satofumi KAMIMURA

  $Id: WiiJoystick_win.cpp 1334 2009-09-24 02:24:29Z satofumi $

  contains WiiYourself! wiimote code by gl.tter http://gl.tter.org

  WiiYourself! を再度ラッパーしている。Linux と同じインターフェースにするため

  \todo 適切に排他制御を行うべき。データ読み書きと、コールバックのあたり
  \todo wii_ が NULL かどうかの判定を行うべき
*/

#include <string>
#include "WiiYourself_1_0/wiimote.h"
#include "WiiJoystick.h"
#include "MovingAverage.h"

using namespace qrk;
using namespace std;


struct WiiJoystick::pImpl
{
  string error_message_;
  wiimote* wii_;

  MovingAverage<double>* x_average_;
  MovingAverage<double>* y_average_;
  MovingAverage<double>* z_average_;


  pImpl(void)
    : error_message_("no error."), wii_(new wiimote),
      x_average_(new MovingAverage<double>(1)),
      y_average_(new MovingAverage<double>(1)),
      z_average_(new MovingAverage<double>(1)) {
  }


  ~pImpl(void)
  {
    // 切断処理
    delete wii_;
    wii_ = NULL;
  }


  bool connect(void)
  {
    disconnect();

    if (! wii_->Connect(wiimote::FIRST_AVAILABLE)) {
      error_message_ = "unable to connect.";
      return false;
    }
    wii_->SetReportType(wiimote::IN_BUTTONS_ACCEL_IR);

    return true;
  }


  void disconnect(void)
  {
    wii_->Disconnect();
  }
};


WiiJoystick::WiiJoystick(void) : pimpl(new pImpl)
{
}


WiiJoystick::~WiiJoystick(void)
{
}


bool WiiJoystick::findController(int timeout)
{
  // !!! 実装の必要はないのか？

  return false;
}


const char* WiiJoystick::what(void) const
{
  return pimpl->error_message_.c_str();
}


bool WiiJoystick::connect(int id)
{
  (void)id;

  return pimpl->connect();
}


void WiiJoystick::disconnect(void)
{
  pimpl->disconnect();
}


bool WiiJoystick::isConnected(void) const
{
  return pimpl->wii_->IsConnected();
}


size_t WiiJoystick::axisNum(void) const
{
  return 2;
}


int WiiJoystick::axisValue(size_t index)
{
  // !!! 排他制御？

  pimpl->wii_->RefreshState();

  int value = 0;
  if (index == AxisX) {
    value =
      (pimpl->wii_->Button.Left() ? -32767 : 0) +
      (pimpl->wii_->Button.Right() ? +32767 : 0);

  } else if (index == AxisY) {
    value =
      (pimpl->wii_->Button.Up() ? +32767 : 0) +
      (pimpl->wii_->Button.Down() ? -32767 : 0);
  }

  return value;
}


size_t WiiJoystick::buttonsNum(void) const
{
  return 7;
}


bool WiiJoystick::isButtonPressed(size_t index)
{
  // !!! 排他制御

  pimpl->wii_->RefreshState();

  bool pressed = false;
  switch (index) {
  case BUTTON_A:
    pressed = pimpl->wii_->Button.A();
    break;

  case BUTTON_B:
    pressed = pimpl->wii_->Button.B();
    break;

  case BUTTON_MINUS:
    pressed = pimpl->wii_->Button.Minus();
    break;

  case BUTTON_PLUS:
    pressed = pimpl->wii_->Button.Plus();
    break;

  case BUTTON_HOME:
    pressed = pimpl->wii_->Button.Home();
    break;

  case BUTTON_1:
    pressed = pimpl->wii_->Button.One();
    break;

  case BUTTON_2:
    pressed = pimpl->wii_->Button.Two();
    break;
  }

  return pressed;
}


int WiiJoystick::buttonPressedTimes(size_t index)
{
  // !!! 実装すること

  static_cast<void>(index);

  // !!!
  return 0;
}


void WiiJoystick::acceleration(Point3d<double>& acceleration,
                                  size_t* timestamp)
{
  pimpl->wii_->RefreshState();

  acceleration.x = pimpl->wii_->Acceleration.X;
  acceleration.y = pimpl->wii_->Acceleration.Y;
  acceleration.z = pimpl->wii_->Acceleration.Z;

  size_t timestamp_value = 0;
  if (timestamp) {
    // !!! ハンドラ内で更新したタイムスタンプを返すようにする
    *timestamp = timestamp_value;
  }
}


void WiiJoystick::rotation(Angle& x_axis, Angle& y_axis, Angle& z_axis,
                           size_t* timestamp)
{
  (void)x_axis;
  (void)y_axis;
  (void)z_axis;
  (void)timestamp;

  // !!!
}


size_t WiiJoystick::batteryPercent(void)
{
  // !!! 排他制御

  pimpl->wii_->RefreshState();

  return pimpl->wii_->BatteryPercent;
}


bool WiiJoystick::irPosition(vector<ir_position>& positions)
{
  // !!! 排他制御

  pimpl->wii_->RefreshState();

  // !!! 未実装

  //positions = pimpl->callback_->wii_hash_[pimpl->wii_]->ir_positions_;
  //return true;

  return false;
}


bool WiiJoystick::setLed(unsigned char led_value)
{
  // ビット並びが逆だったので、反転
  unsigned char swapped_value =
    ((led_value & 0x08) ? 0x01 : 0x00) |
    ((led_value & 0x04) ? 0x02 : 0x00) |
    ((led_value & 0x02) ? 0x04 : 0x00) |
    ((led_value & 0x01) ? 0x08 : 0x00);

  pimpl->wii_->SetLEDs(swapped_value);

  return true;
}


bool WiiJoystick::setRumble(bool rumble)
{
  pimpl->wii_->SetRumble(rumble);
  // !!!
  return true;
}


// 加速度の移動平均数
void WiiJoystick::setAccelerationAverageSize(size_t num)
{
  pimpl->wii_->SetAccelerationAverageSize(num);
}
