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

static const char* const CLASS = "Draw2D";
static const char* const HELP =
  "Sample source code to draw arbitrary 2d graphics in the viewer.";

#include "DDImage/NoIop.h"
#include "DDImage/Row.h"
#include "DDImage/Knobs.h"
#include "DDImage/gl.h"
#include "DDImage/ViewerContext.h"
#include "DDImage/DDMath.h"
#include "DDImage/Knob.h"

using namespace DD::Image;

class TestOp : public NoIop
{
  float x, y, r, t;
  int recursion;
public:
#if DD_IMAGE_VERSION_MAJOR >= 5
  TestOp(Node* node) : NoIop(node)
#else
  TestOp() : NoIop()
#endif
  { x = y = r = t = 0;
    recursion = 12; }
  virtual void knobs(Knob_Callback);
  const char* Class() const { return CLASS; }
  const char* node_help() const { return HELP; }
  static const Iop::Description d;
  HandlesMode doAnyHandles(ViewerContext* ctx);
  void build_handles(ViewerContext* ctx);
  void draw_handle(ViewerContext* ctx);
};

void TestOp::knobs(Knob_Callback f)
{
  BBox_knob(f, &x, "box");
  Int_knob(f, &recursion, IRange(0, 20), "recursion");
  SetFlags(f, Knob::SLIDER);
}

#if DD_IMAGE_VERSION_MAJOR >= 5
static Iop* build(Node* node) { return new TestOp(node); }
#else
static Iop* build() { return new TestOp(); }
#endif
const Iop::Description TestOp::d(CLASS, 0, build);

Op::HandlesMode TestOp::doAnyHandles(ViewerContext* ctx)
{
  if (ctx->transform_mode() == VIEWER_2D)
    return eHandlesCooked;

  return NoIop::doAnyHandles(ctx);
}

// This method is called to build a list of things to call to actually draw the viewer
// overlay. The reason for the two passes is so that 3D movements in the viewer can be
// faster by not having to call every node, but only the ones that asked for draw_handle
// to be called:
void TestOp::build_handles(ViewerContext* ctx)
{

  // Cause any input iop's to draw (you may want to skip this depending on what you do)
  build_input_handles(ctx);

  // Cause any knobs to draw (we don't have any so this makes no difference):
  build_knob_handles(ctx);

  // Don't draw anything unless viewer is in 2d mode:
  if (ctx->transform_mode() != VIEWER_2D)
    return;

  // make it call draw_handle():
  add_draw_handle(ctx);

}

// Recursive dragon-curve drawer
static void dragon(int recursion, bool flip, bool flip2)
{
  if (recursion > 0) {
    glPushMatrix();
    glScalef(flip ? -M_SQRT1_2 : M_SQRT1_2, M_SQRT1_2, 1);
    glRotatef(45, 0, 0, 1);
    dragon(recursion - 1, false, flip ^ flip2);
    glTranslatef(1, 1, 0);
    if (flip)
      glScalef(-1, 1, 0);
    else
      glScalef(1, -1, 0);
    dragon(recursion - 1, true, !flip);
    glPopMatrix();
  }
  else {
    glBegin(GL_LINE_STRIP);
    const float c = 1 / (2 + M_SQRT2_F);
    glVertex3f(flip2 ? -c : c, 0, 0);
    glVertex3f(0, c, 0);
    glVertex3f(0, 1 - c, 0);
    glVertex3f(c, 1, 0);
    glVertex3f(1 - c, 1, 0);
    glEnd();
  }
}

// And then it will call this when stuff needs to be drawn:
void TestOp::draw_handle(ViewerContext* ctx)
{

  // You may want to do this if validate() calcuates anything you need to draw:
  // validate(false);

  // There are several "passes" and you should draw things during the correct
  // passes. There are a list of true/false tests on the ctx object to see if
  // something should be drawn during this pass.
  //
  // For 2D this will draw both a "shadow" line and a "real" line, but skip
  // all the other calls to draw_handle():
  if ( !ctx->draw_lines() )
    return;
  glColor(ctx->node_color());
  glPushMatrix();
  float w = r - x;
  float h = t - y;
  glTranslatef(x + w / 3, y + h / 5, 0);
  glScalef(w / 2, h * 3 / 5, 1);
  dragon(recursion, false, false);
  glPopMatrix();
}