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

static const char* const CLASS = "Normalise";

static const char* const HELP =
  "Example normalises input to 1.0";

// Standard plug-in include files.

#include "DDImage/Iop.h"
#include "DDImage/NukeWrapper.h"
using namespace DD::Image;
#include "DDImage/Row.h"
#include "DDImage/Tile.h"
#include "DDImage/Knobs.h"
#include "DDImage/Thread.h"


using namespace std;

class Normalise : public Iop
{
  float _maxValue;
  bool _firstTime;
  Lock _lock;
  
public:

  int maximum_inputs() const { return 1; }
  int minimum_inputs() const { return 1; }
  
  //! Constructor. Initialize user controls to their default values.

  Normalise (Node* node) : Iop (node)
  {
    _maxValue = 0;
    _firstTime = true;
  }

  ~Normalise () {}
  
  void _validate(bool);
  void _request(int x, int y, int r, int t, ChannelMask channels, int count);
  void _open();
  
  //! This function does all the work.

  void engine ( int y, int x, int r, ChannelMask channels, Row& out );

  //! Return the name of the class.

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

  //! Information to the plug-in manager of DDNewImage/Nuke.

  static const Iop::Description description;

}; 


/*! This is a function that creates an instance of the operator, and is
   needed for the Iop::Description to work.
 */
static Iop* NormaliseCreate(Node* node)
{
  return new Normalise(node);
}

/*! The Iop::Description is how NUKE knows what the name of the operator is,
   how to create one, and the menu item to show the user. The menu item may be
   0 if you do not want the operator to be visible.
 */
const Iop::Description Normalise::description ( CLASS, "Merge/Normalise",
                                                     NormaliseCreate );


void Normalise::_validate(bool for_real)
{
  copy_info(); // copy bbox channels etc from input0, which will validate it.
}

void Normalise::_request(int x, int y, int r, int t, ChannelMask channels, int count)
{
  // request all input input as we are going to search the whole input area
  ChannelSet readChannels = input0().info().channels();
  input(0)->request( readChannels, count );
}

void Normalise::_open()
{
  _firstTime = true;
}


/*! For each line in the area passed to request(), this will be called. It must
   calculate the image data for a region at vertical position y, and between
   horizontal positions x and r, and write it to the passed row
   structure. Usually this works by asking the input for data, and modifying
   it.

 */
void Normalise::engine ( int y, int x, int r,
                              ChannelMask channels, Row& row )
{
  {
    Guard guard(_lock);
    if ( _firstTime ) {
      // do anaylsis.
      Format format = input0().format();

      // these useful format variables are used later
      const int fx = format.x();
      const int fy = format.y();
      const int fr = format.r();
      const int ft = format.t();

      ChannelSet readChannels = input0().info().channels();

      Interest interest( input0(), fx, fy, fr, ft, readChannels, true );
      interest.unlock();
      
      // fetch each row and find the highest number pixel
      _maxValue = 0; 
      for ( int ry = fy; ry < ft; ry++) {
        progressFraction( ry, ft - fy );
        Row row( fx, fr );
        row.get( input0(), ry, fx, fr, readChannels );
        if ( aborted() )
          return;
          
        foreach( z, readChannels ) {
          const float *CUR = row[z] + fx;
          const float *END = row[z] + fr;
          while ( CUR < END ) {
            _maxValue = std::max( (float)*CUR, _maxValue );
            CUR++;
          }
        }
      }
      _firstTime = false;
    }
  } // end lock
  
  Row in( x,r);
  in.get( input0(), y, x, r, channels );
  if ( aborted() )
    return;
  
  foreach( z, channels ) {
    float *CUR = row.writable(z) + x;
    const float* inptr = in[z] + x;
    const float *END = row[z] + r;
    while ( CUR < END ) {
        *CUR++ = *inptr++ * ( 1. / _maxValue );
    }
  }
}
