/*!
  \file
  \brief 取得設定ウィジット

  \author Satofumi KAMIMURA

  $Id: cCaptureSettingWidget.cpp 1727 2010-02-26 08:56:46Z satofumi $
*/

#include "cCaptureSettingWidget.h"
#include "cRangeViewWidget.h"
#include "RangeSensorParameter.h"
#include <QShortcut>
#include "urg_ctrl.h"

using namespace qrk;


struct cCaptureSettingWidget::pImpl
{
  cCaptureSettingWidget* widget_;

  urg_t& urg_;
  QButtonGroup mode_group_;
  QButtonGroup times_group_;

  RangeSensorParameter parameter_;

  cRangeViewWidget range_view_widget_;

  bool apply_completed_;


  pImpl(cCaptureSettingWidget* widget, urg_t& urg)
    : widget_(widget), urg_(urg), range_view_widget_(widget_),
      apply_completed_(true)
  {
  }


  void initializeForm(void)
  {
    // フォーム
    widget_->intensity_button_->hide();
    widget_->dummy_label_->hide();
    widget_->preview_layout_->addWidget(&range_view_widget_);

    // モード
    mode_group_.addButton(widget_->intensity_button_, ME);
    mode_group_.addButton(widget_->gd_button_, GD);
    mode_group_.addButton(widget_->md_button_, MD);
    times_group_.addButton(widget_->infinity_times_button_);
    times_group_.addButton(widget_->n_times_button_);

    // フォームイベント
    connect(widget_->apply_button_, SIGNAL(clicked()),
            widget_, SLOT(applyPressed()));
    connect(widget_->cancel_button_, SIGNAL(clicked()),
            widget_, SLOT(close()));
    connect(widget_->load_button_, SIGNAL(clicked()),
            widget_, SLOT(loadPressed()));
    connect(&mode_group_, SIGNAL(buttonClicked(int)),
            widget_, SLOT(modeChanged(int)));

    connect(widget_->range_first_spinbox_, SIGNAL(editingFinished()),
            widget_, SLOT(firstFinished()));
    connect(widget_->range_last_spinbox_, SIGNAL(editingFinished()),
            widget_, SLOT(lastFinished()));
    connect(widget_->range_first_spinbox_, SIGNAL(valueChanged(int)),
            widget_, SLOT(isRangeChanged()));
    connect(widget_->range_last_spinbox_, SIGNAL(valueChanged(int)),
            widget_, SLOT(isRangeChanged()));

    connect(&times_group_, SIGNAL(buttonClicked(int)),
            widget_, SLOT(timesChanged()));

    connect(widget_->line_group_spinbox_, SIGNAL(valueChanged(int)),
            widget_, SLOT(lineGroupsChanged()));
    connect(widget_->frame_skips_spinbox_, SIGNAL(valueChanged(int)),
            widget_, SLOT(frameSkipsChanged()));
    connect(widget_->capture_times_spinbox_, SIGNAL(valueChanged(int)),
            widget_, SLOT(captureTimesChanged()));


    // キー割り付け
    (void) new QShortcut(Qt::CTRL + Qt::Key_Q, widget_, SLOT(quitPressed()));
    (void) new QShortcut(Qt::CTRL + Qt::Key_W, widget_, SLOT(close()));

    // 初期設定
    setMode(MD);
    setTimes(InfinityTimes);
  }


  void createSettings(CaptureSettings& settings)
  {
    settings.type = static_cast<CaptureType>(mode_group_.checkedId());

    settings.capture_first = widget_->range_first_spinbox_->value();
    settings.capture_last = widget_->range_last_spinbox_->value();
    settings.skip_lines = widget_->line_group_spinbox_->value();
    settings.skip_frames = widget_->frame_skips_spinbox_->value();
    if (widget_->infinity_times_button_->isChecked()) {
      settings.remain_times = 0;
    } else {
      settings.remain_times = widget_->capture_times_spinbox_->value();
    }
  }


  void setMode(int type)
  {
    QAbstractButton* selected_button = mode_group_.button(type);
    if (selected_button) {
      selected_button->click();
    }
  }


  void setTimes(size_t times)
  {
    if (times <= 99) {
      widget_->n_times_button_->click();
    } else {
      widget_->infinity_times_button_->click();
    }
  }


  void setTimesEnabled(bool enable)
  {
    bool infinity_checked = widget_->infinity_times_button_->isChecked();
    bool n_times_checked = (! infinity_checked);

    widget_->infinity_times_button_->setEnabled(enable);
    widget_->n_times_button_->setEnabled(enable);
    widget_->capture_times_spinbox_->setEnabled(enable && n_times_checked);
  }


  void updateApplyButton(void)
  {
    if (apply_completed_) {
      widget_->apply_button_->setEnabled(true);
    }
  }
};


cCaptureSettingWidget::cCaptureSettingWidget(urg_t& urg, QWidget* parent)
  : QWidget(parent), pimpl(new pImpl(this, urg))
{
  setupUi(this);
  pimpl->initializeForm();
}


cCaptureSettingWidget::~cCaptureSettingWidget(void)
{
}


void cCaptureSettingWidget::closeEvent(QCloseEvent* event)
{
  static_cast<void>(event);

  emit widgetClose("cCaptureSettingWidget");
  close();
}


void cCaptureSettingWidget::applyPressed(void)
{
  apply_button_->setEnabled(false);

  pimpl->apply_completed_ = false;

  CaptureSettings settings;
  pimpl->createSettings(settings);

  // 新しいパラメータを送信
  emit setCaptureSettings(settings);

  // 再接続の要求を送信
  emit reconnectRequest();
}


void cCaptureSettingWidget::loadPressed(void)
{
  if (! urg_isConnected(&pimpl->urg_)) {
    return;
  }

  // !!! 関数化して、UrgViewerWindows 内の実装と共通にすべき
  urg_parameter_t urg_parameter;
  urg_parameters(&pimpl->urg_, &urg_parameter);
  pimpl->parameter_.model = urg_parameter.sensor_type;
  pimpl->parameter_.distance_min = urg_parameter.distance_min_;
  pimpl->parameter_.distance_max = urg_parameter.distance_max_;
  pimpl->parameter_.area_total = urg_parameter.area_total_;
  pimpl->parameter_.area_min = urg_parameter.area_min_;
  pimpl->parameter_.area_max = urg_parameter.area_max_;
  pimpl->parameter_.area_front = urg_parameter.area_front_;
  pimpl->parameter_.scan_rpm = urg_parameter.scan_rpm_;
  pimpl->range_view_widget_.setParameter(pimpl->parameter_);

  // 読み出した取得範囲の情報を、フォームに反映させる
  int first_index = pimpl->parameter_.area_min;
  int last_index = pimpl->parameter_.area_max;
  range_first_spinbox_->setValue(first_index);
  range_last_spinbox_->setValue(last_index);

  setApplyEnabled(true);

  emit rangeChanged(first_index, last_index);
}


void cCaptureSettingWidget::reconnectCompleted(void)
{
  pimpl->apply_completed_ = true;
}


void cCaptureSettingWidget::firstFinished(void)
{
  // last の値を first 以上にする
  int first_value = range_first_spinbox_->value();
  if (first_value > range_last_spinbox_->value()) {
    range_last_spinbox_->setValue(first_value);
  }
}


void cCaptureSettingWidget::lastFinished(void)
{
  int last_value = range_last_spinbox_->value();

  // !!! この実装が汚いのをなんとかする
  if (pimpl->parameter_.area_min != pimpl->parameter_.area_max) {
    // パラメータ読み出し後のみ、制限をつける
    if (last_value > pimpl->parameter_.area_max) {
      last_value = pimpl->parameter_.area_max;
      range_last_spinbox_->setValue(last_value);
    }
  }

  // first の値を last 以下にする
  if (last_value < range_first_spinbox_->value()) {
    range_first_spinbox_->setValue(last_value);
  }
}


void cCaptureSettingWidget::isRangeChanged(void)
{
  emit rangeChanged(range_first_spinbox_->value(),
                    range_last_spinbox_->value());

  pimpl->updateApplyButton();
}


void cCaptureSettingWidget::modeChanged(int type)
{
  // GD, MD 各モードでの setEnable() を調整する
  bool md_enable = ((type == MD) || (type == ME)) ? true : false;

  frame_skips_label_->setEnabled(md_enable);
  frame_skips_spinbox_->setEnabled(md_enable);

  capture_times_label_->setEnabled(md_enable);
  pimpl->setTimesEnabled(md_enable);

  pimpl->updateApplyButton();
}


void cCaptureSettingWidget::timesChanged(void)
{
  // Infinity のときに、n_times_spinbox_ を無効にする
  pimpl->setTimesEnabled(true);

  pimpl->updateApplyButton();
}


void cCaptureSettingWidget::setIntensityMode(bool enable_cluster)
{
  intensity_button_->show();
  setMode(ME);
  gd_button_->hide();
  md_button_->hide();

  // UTM 以外の場合は "まとめる数" の属性が指定できないため、無効にする
  line_group_label_->setEnabled(enable_cluster);
  line_group_spinbox_->setEnabled(enable_cluster);
}


void cCaptureSettingWidget::setMode(CaptureType type)
{
  pimpl->setMode(type);
}


void cCaptureSettingWidget::setTimes(size_t times)
{
  pimpl->setTimes(times);
}


void cCaptureSettingWidget::setApplyEnabled(bool enable)
{
  apply_button_->setEnabled(enable);
}


void cCaptureSettingWidget::quitPressed(void)
{
  emit quit();
}


void cCaptureSettingWidget::setConnected(bool connection)
{
  load_button_->setEnabled(connection);
}


void cCaptureSettingWidget::lineGroupsChanged(void)
{
  pimpl->updateApplyButton();
}


void cCaptureSettingWidget::frameSkipsChanged(void)
{
  pimpl->updateApplyButton();
}


void cCaptureSettingWidget::captureTimesChanged(void)
{
  pimpl->updateApplyButton();
}
