// Copyright (c) 2011 The Foundry Visionmongers Ltd.  All Rights Reserved.

#include "DDImage/Knob.h"
#include "DDImage/Knobs.h"
#include "DDImage/ParticleOp.h"
#include "DDImage/FlaggingKnobClosure.h"

using namespace DD::Image;

static const char* const CLASS = "ParticleGravity";
static const char* const HELP =
    "Apply gravity to the particles. \n"
    "- can be applied on each or all of the x,y,z axis in both directions.\n"
    "- is actually more of a directional acceleration node than gravity (it "
    "can go in directions other than \"down\").";

class ParticleGravity : public ParticleBehaviour
{
public:
  ParticleGravity(Node* node) : ParticleBehaviour(node)
  {
  }

  void knobs( Knob_Callback f )
  {
    float kDefaultPositions[] = { 0, 0, 0,      // from position
                                  0, -0.1, 0 }; // to position.

    ParticleBehaviour::knobs(f);
    PositionVector_knob(f, kDefaultPositions, "strength");
    Tooltip(f, "The acceleration to be applied to all particles, expressed in units/frame/frame. "
            "The direction and magnitude of the vector are applied. The position of the vector "
            "has no effect.\n"
            "Gravity affects all particles equally irrespective of mass.  For more details "
            "see Galileo. ");
    addConditionsKnobs( f );
    addDomainKnobs( f );

    if (!isLicensed())
      set_unlicensed();
  }

  virtual void applyBehaviour( const ParticleContext& context, ParticleSystem* ps )
  {
    Knob* k = knob("strength");
    if (!k || !k->positionVectorKnob())
      return;

    PositionVector_KnobI* posVecKnob = k->positionVectorKnob();
    OutputContext oc = outputContext();
    Vector3 strength = posVecKnob->getTo(oc) - posVecKnob->getFrom(oc);

    int count = ps->numParticles();
    for ( int i = 0; i < count; i++) {
      if ( conditionsApply( ps, i ) ) {
        applyAcceleration(ps, i, context, strength);
      }
    }
  }

  static const Description description;
  const char* Class() const { return CLASS; }
  const char* node_help() const { return HELP; }
};

static Op* build(Node* node) { return new ParticleGravity(node); }
const Op::Description ParticleGravity::description(CLASS, build);
