/*
 * Copyright (C) 2012 Open Source Robotics Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

#include "gazebo/common/Console.hh"
#include "gazebo/rendering/ogre_gazebo.h"
#include "gazebo/rendering/selection_buffer/MaterialSwitcher.hh"
#include "gazebo/rendering/RenderTypes.hh"

using namespace gazebo;
using namespace rendering;

/////////////////////////////////////////////////
MaterialSwitcher::MaterialSwitcher()
: lastTechnique(nullptr)
{
  this->currentColor = ignition::math::Color(0.0, 0.0, 0.1);
}

/////////////////////////////////////////////////
MaterialSwitcher::~MaterialSwitcher()
{
}

/////////////////////////////////////////////////
Ogre::Technique *MaterialSwitcher::handleSchemeNotFound(
    uint16_t /*_schemeIndex*/, const Ogre::String & /*_schemeName*/,
    Ogre::Material *_originalMaterial, uint16_t /*_lodIndex*/,
    const Ogre::Renderable *_rend)
{
  if (_rend)
  {
    if (typeid(*_rend) == typeid(Ogre::SubEntity))
    {
      const Ogre::SubEntity *subEntity =
        static_cast<const Ogre::SubEntity *>(_rend);

      if (!(subEntity->getParent()->getVisibilityFlags() &
          GZ_VISIBILITY_SELECTABLE))
      {
        const_cast<Ogre::SubEntity *>(subEntity)->setCustomParameter(1,
            Ogre::Vector4(0, 0, 0, 0));
        return nullptr;
      }

      if (this->lastEntity == subEntity->getParent()->getName())
      {
        const_cast<Ogre::SubEntity *>(subEntity)->setCustomParameter(1,
            Ogre::Vector4(this->currentColor.R(), this->currentColor.G(),
                          this->currentColor.B(), 1.0));
      }
      else
      {
        // load the selection buffer material
        if (this->plainTechnique == nullptr)
        {
          // plain opaque material
          Ogre::ResourcePtr res =
            Ogre::MaterialManager::getSingleton().load("gazebo/plain_color",
                Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

          // OGRE 1.9 changes the shared pointer definition
          #if (OGRE_VERSION < ((1 << 16) | (9 << 8) | 0))
          Ogre::MaterialPtr plainMaterial = static_cast<Ogre::MaterialPtr>(res);
          #else
          Ogre::MaterialPtr plainMaterial = res.staticCast<Ogre::Material>();
          #endif

          this->plainTechnique = plainMaterial->getTechnique(0);
          Ogre::Pass *plainPass = this->plainTechnique->getPass(0);
          plainPass->setDepthCheckEnabled(true);
          plainPass->setDepthWriteEnabled(true);

          // overlay material
          Ogre::MaterialPtr overlayMaterial =
              plainMaterial->clone("plain_color_overlay");
          this->overlayTechnique =
              overlayMaterial->getTechnique(0);
          if (!this->overlayTechnique || !this->overlayTechnique->getPass(0))
          {
            gzerr << "Problem creating the selection buffer overlay material"
                << std::endl;
            return nullptr;
          }
          Ogre::Pass *overlayPass = this->overlayTechnique->getPass(0);
          overlayPass->setDepthCheckEnabled(false);
          overlayPass->setDepthWriteEnabled(false);
        }

        // Make sure we keep the same depth properties so that
        // certain overlay objects can be picked by the mouse.
        Ogre::Technique *newTechnique = this->plainTechnique;

        Ogre::Technique *originalTechnique = _originalMaterial->getTechnique(0);
        if (originalTechnique)
        {
          Ogre::Pass *originalPass = originalTechnique->getPass(0);
          if (originalPass)
          {
            // check if it's an overlay material by assuming the
            // depth check and depth write properties are off.
            bool depthCheck = originalPass->getDepthCheckEnabled();
            bool depthWrite = originalPass->getDepthWriteEnabled();
            if (!depthCheck && !depthWrite)
              newTechnique = this->overlayTechnique;
          }
        }

        this->lastTechnique = newTechnique;

        this->GetNextColor();

        const_cast<Ogre::SubEntity *>(subEntity)->setCustomParameter(1,
            Ogre::Vector4(this->currentColor.R(), this->currentColor.G(),
              this->currentColor.B(), 1.0));

        this->lastEntity = subEntity->getParent()->getName();
        this->colorDict[this->currentColor.AsRGBA()] = this->lastEntity;
      }

      return this->lastTechnique;
    }
    // else
    //    gzerr << "Object is not a SubEntity["
    //          << _rend->getMaterial()->getName() << "] Type[" <<
    //          typeid(*_rend).name() << "]\n";
  }
  // else
  //  gzerr << "Rendering scheme without a Renderable: " << _schemeName
  //        << ", " + _originalMaterial->getName() << std::endl;
  return nullptr;
}

/////////////////////////////////////////////////
const std::string &MaterialSwitcher::GetEntityName(
    const ignition::math::Color &_color) const
{
  ColorMapConstIter iter = this->colorDict.find(_color.AsRGBA());

  if (iter != this->colorDict.end())
    return (*iter).second;
  else
    return this->emptyString;
}

/////////////////////////////////////////////////
void MaterialSwitcher::GetNextColor()
{
  auto color = this->currentColor.AsARGB();
  color++;
  this->currentColor.SetFromARGB(color);
}

/////////////////////////////////////////////////
void MaterialSwitcher::Reset()
{
  this->currentColor = ignition::math::Color(0.0, 0.0, 0.1);
  this->lastEntity.clear();
  this->colorDict.clear();
}
