/*
 * Copyright (c) 2012, Willow Garage, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Willow Garage, Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <QStringList>

#include <rviz/properties/vector_property.h>
#include <rviz/properties/property_tree_model.h>

namespace rviz
{
VectorProperty::VectorProperty(const QString& name,
                               const Ogre::Vector3& default_value,
                               const QString& description,
                               Property* parent)
  : Property(name, QVariant(), description, parent), vector_(default_value), ignore_child_updates_(false)
{
  x_ = new Property("X", vector_.x, "X coordinate", this);
  y_ = new Property("Y", vector_.y, "Y coordinate", this);
  z_ = new Property("Z", vector_.z, "Z coordinate", this);
  updateString();
  connect(x_, &Property::aboutToChange, this, &VectorProperty::emitAboutToChange);
  connect(y_, &Property::aboutToChange, this, &VectorProperty::emitAboutToChange);
  connect(z_, &Property::aboutToChange, this, &VectorProperty::emitAboutToChange);
  connect(x_, &Property::changed, this, &VectorProperty::updateFromChildren);
  connect(y_, &Property::changed, this, &VectorProperty::updateFromChildren);
  connect(z_, &Property::changed, this, &VectorProperty::updateFromChildren);
}

bool VectorProperty::setVector(const Ogre::Vector3& new_vector)
{
  if (new_vector != vector_)
  {
    Q_EMIT aboutToChange();
    vector_ = new_vector;
    ignore_child_updates_ = true;
    x_->setValue(vector_.x);
    y_->setValue(vector_.y);
    z_->setValue(vector_.z);
    ignore_child_updates_ = false;
    updateString();
    Q_EMIT changed();
    if (model_)
      model_->emitDataChanged(this);
    return true;
  }
  return false;
}

bool VectorProperty::setValue(const QVariant& new_value)
{
  QStringList strings = new_value.toString().split(';');
  if (strings.size() >= 3)
  {
    bool x_ok = true;
    float x = strings[0].toFloat(&x_ok);
    bool y_ok = true;
    float y = strings[1].toFloat(&y_ok);
    bool z_ok = true;
    float z = strings[2].toFloat(&z_ok);
    if (x_ok && y_ok && z_ok)
    {
      return setVector(Ogre::Vector3(x, y, z));
    }
  }
  return false;
}

void VectorProperty::updateFromChildren()
{
  if (!ignore_child_updates_)
  {
    vector_.x = x_->getValue().toFloat();
    vector_.y = y_->getValue().toFloat();
    vector_.z = z_->getValue().toFloat();
    updateString();
    Q_EMIT changed();
  }
}

void VectorProperty::emitAboutToChange()
{
  if (!ignore_child_updates_)
  {
    Q_EMIT aboutToChange();
  }
}

void VectorProperty::updateString()
{
  value_ =
      QString("%1; %2; %3").arg(vector_.x, 0, 'g', 5).arg(vector_.y, 0, 'g', 5).arg(vector_.z, 0, 'g', 5);
}

void VectorProperty::load(const Config& config)
{
  float x, y, z;
  if (config.mapGetFloat("X", &x) && config.mapGetFloat("Y", &y) && config.mapGetFloat("Z", &z))
  {
    // Calling setVector() once explicitly is better than letting the
    // Property class load the X, Y, and Z children independently,
    // which would result in at least 3 calls to setVector().
    setVector(Ogre::Vector3(x, y, z));
  }
}

void VectorProperty::save(Config config) const
{
  // Saving the child values explicitly avoids having Property::save()
  // save the summary string version of the property.
  if (getReadOnly())
    return;
  config.mapSetValue("X", x_->getValue());
  config.mapSetValue("Y", y_->getValue());
  config.mapSetValue("Z", z_->getValue());
}

void VectorProperty::setReadOnly(bool read_only)
{
  Property::setReadOnly(read_only);
  x_->setReadOnly(read_only);
  y_->setReadOnly(read_only);
  z_->setReadOnly(read_only);
}

} // end namespace rviz
