//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================
#include "smtk/simulation/ace3p/utility/AttributeUtils.h"

#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/Definition.h"
#include "smtk/attribute/Resource.h"

#include <cassert>
#include <iostream>
#include <map>
#include <string>

namespace smtk
{
namespace simulation
{
namespace ace3p
{

void AttributeUtils::createInstancedAtts(smtk::attribute::ResourcePtr attResource)
{
  auto topView = attResource->findTopLevelView();
  if (topView == nullptr)
  {
    std::cout << "Warning: no top-level view found" << std::endl;
    return;
  }

  this->recursiveCreateInstancedAtts(attResource, topView->details());
}

smtk::attribute::AttributePtr AttributeUtils::getAnalysisAtt(
  smtk::attribute::ResourcePtr attResource,
  smtk::view::ConfigurationPtr view)
{
  if (view == nullptr)
  {
    view = attResource->findViewByType("Analysis");
    if (view == nullptr)
    {
      std::cout << "Unable to find Analysis View" << std::endl;
      return nullptr;
    }
  }

  std::map<std::string, std::string> attributes = view->details().attributes();
  std::string attName = attributes["AnalysisAttributeName"];
  // Check if attribute already exists
  smtk::attribute::AttributePtr analysisAtt = attResource->findAttribute(attName);
  if (analysisAtt != nullptr)
  {
    return analysisAtt;
  }

  // Otherwise get definition and create the attribute
  std::string attType = attributes["AnalysisAttributeType"];
  auto defn = attResource->analyses().buildAnalysesDefinition(attResource, attType);
  assert(defn != nullptr);
  analysisAtt = attResource->createAttribute(attName, defn);
  assert(analysisAtt != nullptr);

  return analysisAtt;
}

smtk::attribute::AttributePtr AttributeUtils::createAttribute(
  smtk::attribute::ResourcePtr attResource,
  const std::string& attType,
  const std::string& attName)
{
  auto existingAtt = attResource->findAttribute(attName);
  if (existingAtt != nullptr)
  {
    std::cout << "Warning: Attribute name " << attName << " already exists." << std::endl;
    return existingAtt;
  }

  auto defn = attResource->findDefinition(attType);
  if (defn == nullptr)
  {
    std::cout << "Error: missing attType \"" << attType << "\" for attName " << attName
              << std::endl;
    return nullptr;
  }

  auto newAtt = attResource->createAttribute(attName, defn);
  return newAtt;
}

void AttributeUtils::recursiveCreateInstancedAtts(
  smtk::attribute::ResourcePtr attResource,
  smtk::view::Configuration::Component& viewComp)
{
  if (viewComp.name() == "View")
  {
    std::string title;
    if (!viewComp.attribute("Title", title))
    {
      // Check for "Name" alias
      if (!viewComp.attribute("Name", title))
      {
        // skip item views, which don't have a title
        return;
      }
    }

    auto view = attResource->findView(title);
    if (view == nullptr)
    {
#ifndef NDEBUG
      std::cout << "Warning: View " << title << " not found" << std::endl;
#endif
      return;
    }

    auto details = view->details();
    std::string attName;
    std::string attType;
    if (view->type() == "Instanced")
    {
      const auto& attsComp = details.child(0);
      for (int i = 0; i < attsComp.numberOfChildren(); ++i)
      {
        const auto& attComp = attsComp.child(i);
        if (attComp.attribute("Name", attName) && (attComp.attribute("Type", attType)))
        {
          this->createAttribute(attResource, attType, attName);
        }
        else
        {
          std::cout << "Warning: <Att> element missing Name and/or Type attribute in View \""
                    << title << "\"." << std::endl;
          continue;
        }
      }
    }
    else if (view->type() == "Analysis")
    {
      smtk::simulation::ace3p::AttributeUtils::getAnalysisAtt(attResource, view);
    }
    else if (view->type() == "Selector")
    {
      if (details.attribute("SelectorName", attName) && details.attribute("SelectorType", attType))
      {
        this->createAttribute(attResource, attType, attName);
      }
      else
      {
        std::cout << "Warning: <Selector> View missing SelectorName and/or SelectorType attribute."
                  << std::endl;
      }
    }
    else
    {
      this->recursiveCreateInstancedAtts(attResource, details);
    }
  } // if ("View")

  else
  {
    // Process component children
    for (std::size_t i = 0; i < viewComp.numberOfChildren(); ++i)
    {
      auto& childComp = viewComp.child(i);
      this->recursiveCreateInstancedAtts(attResource, childComp);
    }
  } // else
}

} // namespace ace3p
} // namespace simulation
} // namespace smtk
