switch(key)
    {
      case vl::Key_Up:    mViewDepth--;  break;
      case vl::Key_Down:  mViewDepth++;  break;
      case vl::Key_Left:  mTestNumber--; break;
      case vl::Key_Right: mTestNumber++; break;
      default:
        break;
    }

/**************************************************************************************/
/*                                                                                */
/*  Visualization Library                                                             */
/*  http://visualizationlibrary.org                                                   */
/*                                                                                */
/*  All rights reserved.                                                              */
/*                                                                                */
/*                                                                                */
/**************************************************************************************/

#include "BaseDemo.hpp"
#include <vlCore/Colors.hpp>
#include <vlGraphics/GeometryPrimitives.hpp>
#include <vlGraphics/Light.hpp>
#include <vlGraphics/Text.hpp>
#include <vlGraphics/FontManager.hpp>
#include <vlGraphics/SceneManagerActorKdTree.hpp>
#include <vlGraphics/SceneManagerActorTree.hpp>

class App_KdTreeView: public BaseDemo
{
public:
  virtual void initEvent()
  {
    vl::Log::notify(appletInfo());

    mSceneKdTree = new vl::SceneManagerActorKdTree;
    rendering()->as<vl::Rendering>()->sceneManagers()->push_back(mSceneKdTree.get());

    mText = new vl::Text;
    mText->setFont( vl::defFontManager()->acquireFont("/font/bitstream-vera/VeraMono.ttf", 10, false) );
    mText->setAlignment(vl::AlignHCenter | vl::AlignTop);
    mText->setViewportAlignment(vl::AlignHCenter | vl::AlignTop);
    mText->setColor(vl::white);
    mText->translate(0,-10,0);
    mTextActor = sceneManager()->tree()->addActor(mText.get(), new vl::Effect);
    mTextActor->effect()->shader()->enable(vl::EN_BLEND);

    mTestNumber = 0;
    mViewDepth  = -1;

    createScene();
    mText->setText( mText->text() + "\nPress left/right to change the test number\nPress up/down to change the visible tree level");
  }

  void createScene()
  {
    vl::ActorCollection actors;

    vl::ref<vl::Effect> effect = new vl::Effect;
    effect->shader()->enable(vl::EN_DEPTH_TEST);
    effect->shader()->enable(vl::EN_LIGHTING);
    effect->shader()->setRenderState( new vl::Light, 0 );

    vl::ref<vl::Geometry> ball = vl::makeUVSphere(vl::vec3(0,0,0),1,20,20);
    ball->computeNormals();

    vl::ref<vl::Geometry> pyramid = vl::makePyramid(vl::vec3(0,0,0),2,2);
    pyramid->computeNormals();

    switch(mTestNumber)
    {
      case 0:
      {
        for(int i=-3; i<+3; ++i)
        {
          vl::ref<vl::Actor> actor = new vl::Actor(ball.get(),effect.get(),new vl::Transform);
          actors.push_back(actor.get());
          vl::mat4 mat = vl::mat4::getTranslation(i*2.0f,0,0);
          actor->transform()->setLocalMatrix(mat);
          actor->transform()->computeWorldMatrix();
        }
        break;
      }
      case 1:
      {
        for(int i=-3; i<+3; ++i)
        {
          vl::ref<vl::Actor> actor = new vl::Actor(ball.get(),effect.get(),new vl::Transform);
          actors.push_back(actor.get());
          vl::mat4 mat = vl::mat4::getTranslation(0,i*2.0f,0);
          actor->transform()->setLocalMatrix(mat);
          actor->transform()->computeWorldMatrix();
        }
        break;
      }
      case 2:
      {
        for(int i=-3; i<+3; ++i)
        {
          vl::ref<vl::Actor> actor = new vl::Actor(ball.get(),effect.get(),new vl::Transform);
          actors.push_back(actor.get());
          vl::mat4 mat = vl::mat4::getTranslation(0,0,i*2.0f);
          actor->transform()->setLocalMatrix(mat);
          actor->transform()->computeWorldMatrix();
        }
        break;
      }
      case 3:
      {
        for(int x=-4; x<+4; ++x)
        {
          for(int y=-4; y<+4; ++y)
          {
            vl::ref<vl::Actor> actor = new vl::Actor(pyramid.get(),effect.get(),new vl::Transform);
            actors.push_back(actor.get());
            vl::mat4 mat = vl::mat4::getTranslation(x*2.0f,0,y*2.0f);
            actor->transform()->setLocalMatrix(mat);
            actor->transform()->computeWorldMatrix();
          }
        }
        break;
      }
      case 4:
      {
        for(int x=-4; x<+4; ++x)
        {
          for(int y=-4; y<+4; ++y)
          {
            vl::ref<vl::Actor> actor = new vl::Actor(pyramid.get(),effect.get(),new vl::Transform);
            actors.push_back(actor.get());
            vl::mat4 mat = vl::mat4::getTranslation(x*2.0f,y*2.0f,0);
            actor->transform()->setLocalMatrix(mat);
            actor->transform()->computeWorldMatrix();
          }
        }
        break;
      }
      case 5:
      {
        for(int x=-4; x<+4; ++x)
        {
          for(int y=-4; y<+4; ++y)
          {
            vl::ref<vl::Actor> actor = new vl::Actor(pyramid.get(),effect.get(),new vl::Transform);
            actors.push_back(actor.get());
            vl::mat4 mat = vl::mat4::getTranslation(0,x*2.0f,y*2.0f);
            actor->transform()->setLocalMatrix(mat);
            actor->transform()->computeWorldMatrix();
          }
        }
        break;
      }
      case 6:
      {
        for(int i=0; i<10; ++i)
        {
          vl::ref<vl::Actor> actor = new vl::Actor(pyramid.get(),effect.get(),new vl::Transform);
          actors.push_back(actor.get());
          vl::mat4 mat = vl::mat4::getTranslation(rand()%8-4.0f, rand()%8-4.0f, rand()%8-4.0f);
          actor->transform()->setLocalMatrix(mat);
          actor->transform()->computeWorldMatrix();
        }
        break;
      }
    }

    /* compile ActorKdTree */
    mSceneKdTree->tree()->buildKdTree(actors);

    updateKdTreeView();
  }

  void updateKdTreeView()
  {
    /* regenerate scene manager containing the text and the boxes representing the ActorKdTree nodes */
    sceneManager()->tree()->actors()->clear();

    sceneManager()->tree()->addActor( mTextActor.get() );
    mText->setText( vl::Say("Test #%n, Level #%n") << mTestNumber << mViewDepth );

    viewTreeNodes(mSceneKdTree->tree(),0);
  }

  void viewTreeNodes(vl::ActorTreeAbstract* tree, int level)
  {
    if (tree && !tree->aabb().isNull())
    {
      if (level == mViewDepth || mViewDepth == -1)
      {
        vl::ref<vl::Effect> effect = new vl::Effect;
        effect->shader()->gocPolygonMode()->set(vl::PM_LINE, vl::PM_LINE);
        effect->shader()->enable(vl::EN_DEPTH_TEST);
        effect->shader()->gocColor()->setValue(vl::gold);
        vl::ref<vl::Geometry> box = vl::makeBox(tree->aabb());
        sceneManager()->tree()->addActor(box.get(), effect.get());

        if (mViewDepth != -1)
          return;
      }

      for(int i=0; i<tree->childrenCount(); ++i)
        viewTreeNodes(tree->child(i), level+1);
    }
  }

  void keyPressEvent(unsigned short ch, vl::EKey key)
  {
    BaseDemo::keyPressEvent(ch,key);

    mViewDepth  = vl::clamp(mViewDepth,-1,10);
    mTestNumber = vl::clamp(mTestNumber,0,10);
    switch(key)
    {
      case vl::Key_Up:    updateKdTreeView(); break;
      case vl::Key_Down:  updateKdTreeView(); break;
      case vl::Key_Left:  createScene();      break;
      case vl::Key_Right: createScene();      break;
      default:
        break;
    }
  }

protected:
  vl::ref<vl::SceneManagerActorKdTree> mSceneKdTree;
  vl::ref<vl::Text> mText;
  vl::ref<vl::Actor> mTextActor;
  int mTestNumber;
  int mViewDepth;
};

// Have fun!

BaseDemo* Create_App_KdTreeView() { return new App_KdTreeView; }


static int32_t pthread_create_wrapper(wasm_exec_env_t exec_env,
                                      int32_t a,
                                      int32_t b,
                                      int32_t c,
                                      int32_t d)
{
    SPDLOG_DEBUG("S - pthread_create {} {} {} {}", a, b, c, d);
    return 0;
}

#include <wamr/native.h>
#include <wasm_export.h>

#include <faabric/util/logging.h>

namespace wasm {

// 14/04/21 - WAMR threading not implemented
// All of these functions are stubbed as threading with WAMR isn't yet
// implemented. Once it is, we will need to implement the function here,
// exepctially the locking which is used to manage thread-safe memory

static int32_t pthread_join_wrapper(wasm_exec_env_t exec_env,
                                    int32_t a,
                                    int32_t b)
{
    SPDLOG_DEBUG("S - pthread_join {} {}", a, b);
    return 0;
}

static int32_t pthread_once_wrapper(wasm_exec_env_t exec_env,
                                    int32_t a,
                                    int32_t b)
{
    SPDLOG_DEBUG("S - pthread_once {} {}", a, b);
    return 0;
}

static int32_t pthread_mutex_init_wrapper(wasm_exec_env_t exec_env,
                                          int32_t a,
                                          int32_t b)
{
    SPDLOG_DEBUG("S - pthread_mutex_init {} {}", a, b);
    return 0;
}

static int32_t pthread_mutex_lock_wrapper(wasm_exec_env_t exec_env, int32_t a)
{
    SPDLOG_TRACE("S - pthread_mutex_lock {}", a);
    return 0;
}

static int32_t pthread_mutex_unlock_wrapper(wasm_exec_env_t exec_env, int32_t a)
{
    SPDLOG_TRACE("S - pthread_mutex_unlock {}", a);
    return 0;
}

static int32_t pthread_mutex_destroy_wrapper(wasm_exec_env_t exec_env,
                                             int32_t a)
{
    SPDLOG_DEBUG("S - pthread_mutex_destroy {}", a);
    return 0;
}

static int32_t pthread_cond_init_wrapper(wasm_exec_env_t exec_env,
                                         int32_t a,
                                         int32_t b)
{
    SPDLOG_DEBUG("S - pthread_cond_init {}", a, b);
    return 0;
}

static int32_t pthread_cond_signal_wrapper(wasm_exec_env_t exec_env, int32_t a)
{
    SPDLOG_DEBUG("S - pthread_cond_signal {}", a);
    return 0;
}

static int32_t pthread_cond_wait_wrapper(wasm_exec_env_t exec_env,
                                         int32_t a,
                                         int32_t b)
{
    SPDLOG_DEBUG("S - pthread_cond_wait {}", a, b);
    return 0;
}

static int32_t pthread_cond_broadcast_wrapper(wasm_exec_env_t exec_env,
                                              int32_t a)
{
    SPDLOG_DEBUG("S - pthread_cond_broadcast {}", a);
    return 0;
}

static int32_t pthread_cond_destroy_wrapper(wasm_exec_env_t exec_env, int32_t a)
{
    SPDLOG_DEBUG("S - pthread_cond_destroy {}", a);
    return 0;
}

static int32_t pthread_mutexattr_init_wrapper(wasm_exec_env_t exec_env,
                                              int32_t a)
{
    SPDLOG_DEBUG("S - pthread_mutexattr_init {}", a);
    return 0;
}

static int32_t pthread_mutexattr_destroy_wrapper(wasm_exec_env_t exec_env,
                                                 int32_t a)
{
    SPDLOG_DEBUG("S - pthread_mutexattr_destroy {}", a);
    return 0;
}

static int32_t pthread_equal_wrapper(wasm_exec_env_t exec_env,
                                     int32_t a,
                                     int32_t b)
{
    SPDLOG_DEBUG("S - pthread_equal {} {}", a, b);
    return 0;
}

static NativeSymbol ns[] = {
    REG_NATIVE_FUNC(pthread_create, "(iiii)i"),
    REG_NATIVE_FUNC(pthread_join, "(ii)i"),
    REG_NATIVE_FUNC(pthread_once, "(ii)i"),
    REG_NATIVE_FUNC(pthread_mutex_init, "(ii)i"),
    REG_NATIVE_FUNC(pthread_mutex_lock, "(i)i"),
    REG_NATIVE_FUNC(pthread_mutex_unlock, "(i)i"),
    REG_NATIVE_FUNC(pthread_mutex_destroy, "(i)i"),
    REG_NATIVE_FUNC(pthread_cond_init, "(ii)i"),
    REG_NATIVE_FUNC(pthread_cond_signal, "(i)i"),
    REG_NATIVE_FUNC(pthread_cond_wait, "(ii)i"),
    REG_NATIVE_FUNC(pthread_cond_broadcast, "(i)i"),
    REG_NATIVE_FUNC(pthread_cond_destroy, "(i)i"),
    REG_NATIVE_FUNC(pthread_mutexattr_init, "(i)i"),
    REG_NATIVE_FUNC(pthread_mutexattr_destroy, "(i)i"),
    REG_NATIVE_FUNC(pthread_equal, "(ii)i"),
};

uint32_t getFaasmPthreadApi(NativeSymbol** nativeSymbols)
{
    *nativeSymbols = ns;
    return sizeof(ns) / sizeof(NativeSymbol);
}
}


// For now, just verify this doesn't crash.
namespace test0 {
  struct Obj {};

  struct Base {           virtual const Obj *foo() = 0; };
  struct Derived : Base { virtual       Obj *foo() { return new Obj(); } };

  void test(Derived *D) { D->foo(); }
}

namespace Test1 {
struct A {
  virtual void f();
};
void A::f() { }

}

namespace Test2 {

// This is a smoke test of the vtable dumper.
struct A {
  virtual void f();
  virtual void f() const;

  virtual A* g(int a);
  virtual ~A();
  virtual void h();
  virtual A& operator=(const A&);
};
void A::f() { }

// Another simple vtable dumper test.

struct B {
  virtual void f();
  virtual void g() = 0;
  virtual ~B() = 0;
};
void B::f() { }

}

namespace Test3 {

// If a function in a derived class overrides a function in a primary base,
// then the function should not have an entry in the derived class (unless the return
// value requires adjusting).

struct A {
  virtual void f();
};
void A::f() { }

struct B : A {
  virtual void f();
  virtual void g();
};
void B::f() { }

struct C : A {
  virtual void g();
  virtual void h();
};
void C::g() { }

struct D : B {
  virtual void f();
  virtual void g();
  virtual void h();
};

void D::f() { }
}

namespace Test4 {

// Test non-virtual result adjustments.

struct R1 { int r1; };
struct R2 { int r2; };
struct R3 : R1, R2 { int r3; };

struct A {
  virtual R2 *f();
};

struct B : A {
  virtual R3 *f();
};
R3 *B::f() { return 0; }

// Test virtual result adjustments.
struct V1 { int v1; };
struct V2 : virtual V1 { int v1; };

struct C {
  virtual V1 *f();
};

// CHECK-9-NEXT:       [return adjustment: 0 non-virtual, -24 vbase offset offset]
struct D : C {
  virtual V2 *f();
};
V2 *D::f() { return 0; };

// Virtual result adjustments with an additional non-virtual adjustment.
struct V3 : virtual R3 { int r3; };

// CHECK-10-NEXT:       [return adjustment: 4 non-virtual, -24 vbase offset offset]
struct E : A {
  virtual V3 *f();
};
V3 *E::f() { return 0;}

// Test that a pure virtual member doesn't get a thunk.

struct F : A {
  virtual void g();
  virtual R3 *f() = 0;
};
void F::g() { }

}

namespace Test5 {

// Simple secondary vtables without 'this' pointer adjustments.
struct A {
  virtual void f();
  virtual void g();
  int a;
};

struct B1 : A {
  virtual void f();
  int b1;
};

struct B2 : A {
  virtual void g();
  int b2;
};

struct C : B1, B2 {
  virtual void h();
};
void C::h() { }
}

namespace Test6 {

struct A1 {
  virtual void f();
  int a;
};

struct A2 {
  virtual void f();
  int a;
};

struct C : A1, A2 {
  virtual void f();
};
void C::f() { }

}

namespace Test7 {

// Test that the D::f overrider for A::f have different 'this' pointer
// adjustments in the two A base subobjects.

struct A {
  virtual void f();
  int a;
};

struct B1 : A { };
struct B2 : A { };

struct C { virtual void c(); };

struct D : C, B1, B2 {
  virtual void f();
};
void D::f() { }

}

namespace Test8 {

// Test that we don't try to layout vtables for classes that don't have
// virtual bases or virtual member functions.

struct A { };

struct B : A {
  virtual void f();
};
void B::f() { }

}

namespace Test9 {

// Simple test of vbase offsets.

struct A1 { int a1; };
struct A2 { int a2; };

struct B : virtual A1, virtual A2 {
  int b;

  virtual void f();
};

void B::f() { }

}

namespace Test10 {

// Test for a bug where we would not emit secondary vtables for bases
// of a primary base.
struct A1 { virtual void a1(); };
struct A2 { virtual void a2(); };

struct B : A1, A2 {
  int b;
};

struct C : B {
  virtual void f();
};
void C::f() { }

}

namespace Test11 {

// Very simple test of vtables for virtual bases.
struct A1 { int a; };
struct A2 { int b; };

struct B : A1, virtual A2 {
  int b;
};

struct C : virtual B {
  virtual void f();
};
void C::f() { }

}

namespace Test12 {

// Test that the right vcall offsets are generated in the right order.

// CHECK-19-NEXT:        [this adjustment: 0 non-virtual, -32 vcall offset offset]
struct A1 {
  virtual void a1();
  int a;
};

struct A2 {
  virtual void a2();
  int a;
};

struct A3 {
  virtual void a3();
  int a;
};

struct A : A1, A2, A3 {
  virtual void a();
  int i;
};

struct B : virtual A {
  virtual void f();

  virtual void a();
};
void B::f() { }

}

namespace Test13 {

// Test that we don't try to emit a vtable for 'A' twice.
struct A {
  virtual void f();
};

struct B : virtual A {
  virtual void f();
};

struct C : virtual B, virtual A {
  virtual void f();
};
void C::f() { }

}

namespace Test14 {

// Verify that we handle A being a non-virtual base of B, which is a virtual base.

struct A {
  virtual void f();
};

struct B : A { };

struct C : virtual B { };

struct D : C, virtual B {
 virtual void f();
};
void D::f() { }

}

namespace Test15 {

// Test that we don't emit an extra vtable for B since it's a primary base of C.
struct A { virtual void a(); };
struct B { virtual void b(); };

struct C : virtual B { };

struct D : A, virtual B, virtual C {
  virtual void f();
};
void D::f() { }

}

namespace Test16 {

// Test that destructors share vcall offsets.

struct A { virtual ~A(); };
struct B { virtual ~B(); };

struct C : A, B { virtual ~C(); };

// CHECK-23-NEXT:        [this adjustment: 0 non-virtual, -24 vcall offset offset]
// CHECK-23-NEXT:        [this adjustment: 0 non-virtual, -24 vcall offset offset]
// CHECK-23-NEXT:        [this adjustment: -8 non-virtual, -24 vcall offset offset]
// CHECK-23-NEXT:        [this adjustment: -8 non-virtual, -24 vcall offset offset]
struct D : virtual C {
  virtual void f();
};
void D::f() { }

}

namespace Test17 {

// Test that we don't mark E::f in the C-in-E vtable as unused.
struct A { virtual void f(); };
struct B : virtual A { virtual void f(); };
struct C : virtual A { virtual void f(); };
struct D : virtual B, virtual C { virtual void f(); };

// CHECK-24-NEXT:        [this adjustment: 0 non-virtual, -24 vcall offset offset]
class E : virtual D {
  virtual void f();
};
void E::f() {}

}

namespace Test18 {

// Test that we compute the right 'this' adjustment offsets.

struct A {
  virtual void f();
  virtual void g();
};

struct B : virtual A {
  virtual void f();
};

struct C : A, B {
  virtual void g();
};

// CHECK-25-NEXT:        [this adjustment: 0 non-virtual, -32 vcall offset offset]
// CHECK-25-NEXT:        [this adjustment: 0 non-virtual, -32 vcall offset offset]
// CHECK-25-NEXT:        [this adjustment: -8 non-virtual, -32 vcall offset offset]

// CHECK-25-NEXT:        [this adjustment: 0 non-virtual, -24 vcall offset offset]
// CHECK-25-NEXT:        [this adjustment: 0 non-virtual, -32 vcall offset offset]

// CHECK-25-NEXT:        [this adjustment: 0 non-virtual, -24 vcall offset offset]
struct D : virtual B, virtual C, virtual A
{
  virtual void f();
  virtual void h();
};
void D::f() {}

}

namespace Test19 {

struct A {
  int a;

  virtual void f();
};

struct B : A {
  int b;

  virtual void g();
};

struct C {
  virtual void c();
};

// CHECK-26-NEXT:        [this adjustment: 0 non-virtual, -24 vcall offset offset]
struct D : C, B, virtual A {
  virtual void f();
};
void D::f() { }

}

namespace Test20 {

// pure virtual member functions should never have 'this' adjustments.

struct A {
  virtual void f() = 0;
  virtual void g();
};

struct B : A { };

struct C : A, B {
  virtual void f() = 0;
  virtual void h();
};
void C::h() { }

}

namespace Test21 {

// Test that we get vbase offsets right in secondary vtables.
struct A {
  virtual void f();
};

struct B : virtual A { };
class C : virtual B { };
class D : virtual C { };

class E : virtual C { };

// CHECK-28:      Virtual base offset offsets for 'Test21::F' (5 entries).
class F : virtual D, virtual E {
  virtual void f();
};
void F::f() { }

}

namespace Test22 {

// Very simple construction vtable test.
struct V1 {
  int v1;
};

struct V2 : virtual V1 {
  int v2;
};

struct C : virtual V1, virtual V2 {
  int c;
  virtual void f();
};
void C::f() { }

}

namespace Test23 {

struct A {
  int a;
};

struct B : virtual A {
  int b;
};

struct C : A, virtual B {
  int c;
};

struct D : virtual A, virtual B, C {
  int d;

  void f();
};
void D::f() { }
  D d;
}

namespace Test24 {

// Another construction vtable test.

struct A {
  virtual void f();
};

struct B : virtual A { };
struct C : virtual A { };

struct D : B, C {
  virtual void f();
};
void D::f() { }

}

namespace Test25 {

// This mainly tests that we don't assert on this class hierarchy.

struct V {
  virtual void f();
};

struct A : virtual V { };
struct B : virtual V { };

struct C : A, virtual V, B {
  virtual void g();
};
void C::g() { }

}

namespace Test26 {

// Test that we generate the right number of entries in the C-in-D construction vtable, and that
// we don't mark A::a as unused.

struct A {
  virtual void a();
};

struct B {
  virtual void c();
};

struct C : virtual A {
  virtual void b();
};

class D : virtual B, virtual C {
  virtual void d();
};
void D::d() { }

}

namespace Test27 {

// Test that we don't generate a secondary vtable for C in the D-in-E vtable, since
// C doesn't have any virtual bases.

struct A {
  virtual void a();
};

struct B {
  virtual void b();
};

struct C {
  virtual void c();
};

struct D : A, virtual B, C {
  virtual void d();
};

struct E : D {
  virtual void e();
};
void E::e() { }

}

namespace Test28 {

// Check that we do include the vtable for B in the D-in-E construction vtable, since
// B is a base class of a virtual base (C).

struct A {
  virtual void a();
};

struct B {
  virtual void b();
};

struct C : A, B {
  virtual void c();
};

struct D : virtual C {
};

struct E : D {
  virtual void e();
};
void E::e() { }

}

namespace Test29 {

// Test that the covariant return thunk for B::f will have a virtual 'this' adjustment,
// matching gcc.

struct V1 { };
struct V2 : virtual V1 { };

struct A {
  virtual V1 *f();
};

// CHECK-36-NEXT:        [return adjustment: 0 non-virtual, -24 vbase offset offset]
// CHECK-36-NEXT:        [this adjustment: 0 non-virtual, -24 vcall offset offset]
struct B : virtual A {
  virtual V2 *f();
};
V2 *B::f() { return 0; }

}

namespace Test30 {

// Test that we don't assert when generating a vtable for F.
struct A { };

struct B : virtual A {
 int i;
};

struct C {
 virtual void f();
};

struct D : virtual C, B { };
struct E : virtual D { };

struct F : E {
 virtual void f();
};
void F::f() { }

}

namespace Test31 {

// Test that we don't add D::f twice to the primary vtable.
struct A {
  int a;
};

struct B {
  virtual void f();
};

struct C : A, virtual B {
  virtual void f();
};

// CHECK-37-NEXT:        [this adjustment: 0 non-virtual, -24 vcall offset offset]
struct D : virtual C {
  virtual void f();
};
void D::f() { }

}

namespace Test32 {

// Check that we correctly lay out the virtual bases of 'Test32::D'.

struct A {
  virtual void f();
};

struct B : virtual A { };
struct C : A, virtual B { };
struct D : virtual B { };

// CHECK-38:      Virtual base offset offsets for 'Test32::E' (3 entries).
struct E : C, virtual D {
  virtual void f();
};
void E::f() { }

}

namespace Test33 {

// Test that we don't emit too many vcall offsets in 'Test32::F'.

struct A {
  virtual void a();
};

struct B {
  virtual void b();
};

struct C : virtual A, virtual B {
  virtual void c();
};

struct D : virtual C { };

struct E : A, D {
  virtual void e();
};

struct F : virtual E, A {
  virtual void f();
};
void F::f() { }

}

namespace Test34 {

// Test that we lay out the construction vtable for 'Test34::E' in 'Test34::F' correctly.

struct A {
  virtual void a();
};
struct B : virtual A { };

struct C : B, A {
  virtual void c();
};

struct D : A, C { };

struct E : virtual D {
  virtual void e();
};

struct F : E {
  virtual void f();
};
void F::f() { }

}

namespace Test35 {

// Test that we lay out the virtual bases of 'Test35::H' in the correct order.

struct A {
 virtual void a();

 int i;
};

struct B : virtual A {
 virtual void b();
};

struct C {
 virtual void c();
};

struct D : C, virtual B {
 virtual void d();
};

struct E : D {
 virtual void e();

 bool b;
};

struct F : virtual D { };
struct G : virtual E { };

// CHECK-41:      Virtual base offset offsets for 'Test35::H' (4 entries).
struct H : F, G {
 virtual void h();
};
void H::h() { }

}

namespace Test36 {

// Test that we don't mark B::f as unused in the vtable for D.

struct A {
 virtual void f();
};

struct B : virtual A { };

struct C : virtual A {
 virtual void f();
};

// CHECK-42-NEXT:        [this adjustment: 0 non-virtual, -24 vcall offset offset]
struct D : virtual B, C {
 virtual void g();
};
void D::g() { }

}

namespace Test37 {

// Test that we give C::f the right vtable index. (PR9660).
struct A {
	virtual A* f() = 0;
};

struct B : virtual A {
  virtual B* f();
};

struct C : B {
  virtual C* f();
};

C* C::f() { return 0; }

}

namespace Test38 {
  struct A {
    virtual void *foo();
    virtual const void *foo() const;
  };

  class B : virtual public A {
    void *foo();
    const void *foo() const;
  };

  void *B::foo() { return 0; }
}

namespace Test39 {
  struct A {
    virtual void foo() = delete;
  };

  struct B: A {
    virtual void foo2();
  };

  void B::foo2() {
  }
}

namespace Test40 {
  struct A {
    virtual void foo() = 0;
  };

  struct B : public A {
    virtual void foo();
  };

  struct C: public B {
    virtual int f(int);
    virtual int f();
    virtual int g(int);
    virtual int g();
    virtual int h(int);
    virtual int h();
    virtual int i(int);
    virtual int i();
  };
  // Force C's vtable to be generated.


static void increaseCounters(Counters &Dst, const Counters &Src) {

  for (unsigned i = 0; i < 3; ++i)
    Dst.Array[i] = std::max(Dst.Array[i], Src.Array[i]);
}

//                     The LLVM Compiler Infrastructure
/// \file
/// \brief Insert wait instructions for memory reads and writes.
/// Memory reads and writes are issued asynchronously, so we need to insert
/// S_WAITCNT instructions when we want to access any of their results or
/// overwrite any register that's used asynchronously.

#include "AMDGPU.h"
#include "AMDGPUSubtarget.h"
#include "SIDefines.h"
#include "SIInstrInfo.h"
#include "SIMachineFunctionInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"

#define DEBUG_TYPE "si-insert-waits"

using namespace llvm;

namespace {

/// \brief One variable for each of the hardware counters
typedef union {
  struct {
    unsigned VM;
    unsigned EXP;
    unsigned LGKM;
  } Named;
  unsigned Array[3];

} Counters;

typedef enum {
  OTHER,
  SMEM,
  VMEM
} InstType;

typedef Counters RegCounters[512];
typedef std::pair<unsigned, unsigned> RegInterval;

class SIInsertWaits : public MachineFunctionPass {

private:
  const SIInstrInfo *TII;
  const SIRegisterInfo *TRI;
  const MachineRegisterInfo *MRI;

  /// \brief Constant hardware limits
  static const Counters WaitCounts;

  /// \brief Constant zero value
  static const Counters ZeroCounts;

  /// \brief Counter values we have already waited on.
  Counters WaitedOn;

  /// \brief Counter values for last instruction issued.
  Counters LastIssued;

  /// \brief Registers used by async instructions.
  RegCounters UsedRegs;

  /// \brief Registers defined by async instructions.
  RegCounters DefinedRegs;

  /// \brief Different export instruction types seen since last wait.
  unsigned ExpInstrTypesSeen;

  /// \brief Type of the last opcode.
  InstType LastOpcodeType;

  bool LastInstWritesM0;

  /// \brief Whether the machine function returns void
  bool ReturnsVoid;

  /// \brief Get increment/decrement amount for this instruction.
  Counters getHwCounts(MachineInstr &MI);

  /// \brief Is operand relevant for async execution?
  bool isOpRelevant(MachineOperand &Op);

  /// \brief Get register interval an operand affects.
  RegInterval getRegInterval(const TargetRegisterClass *RC,
                             const MachineOperand &Reg) const;

  /// \brief Handle instructions async components
  void pushInstruction(MachineBasicBlock &MBB,
                       MachineBasicBlock::iterator I);

  /// \brief Insert the actual wait instruction
  bool insertWait(MachineBasicBlock &MBB,
                  MachineBasicBlock::iterator I,
                  const Counters &Counts);

  /// \brief Do we need def2def checks?
  bool unorderedDefines(MachineInstr &MI);

  /// \brief Resolve all operand dependencies to counter requirements
  Counters handleOperands(MachineInstr &MI);

  /// \brief Insert S_NOP between an instruction writing M0 and S_SENDMSG.
  void handleSendMsg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I);

public:
  static char ID;

  SIInsertWaits() :
    MachineFunctionPass(ID),
    TII(nullptr),
    TRI(nullptr),
    ExpInstrTypesSeen(0) { }

  bool runOnMachineFunction(MachineFunction &MF) override;

  const char *getPassName() const override {
    return "SI insert wait instructions";
  }

  void getAnalysisUsage(AnalysisUsage &AU) const override {
    AU.setPreservesCFG();
    MachineFunctionPass::getAnalysisUsage(AU);
  }
};

} // End anonymous namespace

INITIALIZE_PASS_BEGIN(SIInsertWaits, DEBUG_TYPE,
                      "SI Insert Waits", false, false)
INITIALIZE_PASS_END(SIInsertWaits, DEBUG_TYPE,
                    "SI Insert Waits", false, false)

char SIInsertWaits::ID = 0;

char &llvm::SIInsertWaitsID = SIInsertWaits::ID;

FunctionPass *llvm::createSIInsertWaitsPass() {
  return new SIInsertWaits();
}

const Counters SIInsertWaits::WaitCounts = { { 15, 7, 15 } };
const Counters SIInsertWaits::ZeroCounts = { { 0, 0, 0 } };

Counters SIInsertWaits::getHwCounts(MachineInstr &MI) {
  uint64_t TSFlags = MI.getDesc().TSFlags;
  Counters Result = { { 0, 0, 0 } };

  Result.Named.VM = !!(TSFlags & SIInstrFlags::VM_CNT);

  // Only consider stores or EXP for EXP_CNT
  Result.Named.EXP = !!(TSFlags & SIInstrFlags::EXP_CNT &&
      (MI.getOpcode() == AMDGPU::EXP || MI.getDesc().mayStore()));

  // LGKM may uses larger values
  if (TSFlags & SIInstrFlags::LGKM_CNT) {

    if (TII->isSMRD(MI)) {

      if (MI.getNumOperands() != 0) {
        assert(MI.getOperand(0).isReg() &&
               "First LGKM operand must be a register!");

        // XXX - What if this is a write into a super register?
        const TargetRegisterClass *RC = TII->getOpRegClass(MI, 0);
        unsigned Size = RC->getSize();
        Result.Named.LGKM = Size > 4 ? 2 : 1;
      } else {
        // s_dcache_inv etc. do not have a a destination register. Assume we
        // want a wait on these.
        // XXX - What is the right value?
        Result.Named.LGKM = 1;
      }
    } else {
      // DS
      Result.Named.LGKM = 1;
    }

  } else {
    Result.Named.LGKM = 0;
  }

  return Result;
}

bool SIInsertWaits::isOpRelevant(MachineOperand &Op) {
  // Constants are always irrelevant
  if (!Op.isReg() || !TRI->isInAllocatableClass(Op.getReg()))
    return false;

  // Defines are always relevant
  if (Op.isDef())
    return true;

  // For exports all registers are relevant
  MachineInstr &MI = *Op.getParent();
  if (MI.getOpcode() == AMDGPU::EXP)
    return true;

  // For stores the stored value is also relevant
  if (!MI.getDesc().mayStore())
    return false;

  // Check if this operand is the value being stored.
  // Special case for DS instructions, since the address
  // operand comes before the value operand and it may have
  // multiple data operands.

  if (TII->isDS(MI)) {
    MachineOperand *Data = TII->getNamedOperand(MI, AMDGPU::OpName::data);
    if (Data && Op.isIdenticalTo(*Data))
      return true;

    MachineOperand *Data0 = TII->getNamedOperand(MI, AMDGPU::OpName::data0);
    if (Data0 && Op.isIdenticalTo(*Data0))
      return true;

    MachineOperand *Data1 = TII->getNamedOperand(MI, AMDGPU::OpName::data1);
    if (Data1 && Op.isIdenticalTo(*Data1))
      return true;

    return false;
  }

  // NOTE: This assumes that the value operand is before the
  // address operand, and that there is only one value operand.
  for (MachineInstr::mop_iterator I = MI.operands_begin(),
       E = MI.operands_end(); I != E; ++I) {

    if (I->isReg() && I->isUse())
      return Op.isIdenticalTo(*I);
  }

  return false;
}

RegInterval SIInsertWaits::getRegInterval(const TargetRegisterClass *RC,
                                          const MachineOperand &Reg) const {
  unsigned Size = RC->getSize();
  assert(Size >= 4);

  RegInterval Result;
  Result.first = TRI->getEncodingValue(Reg.getReg());
  Result.second = Result.first + Size / 4;

  return Result;
}

void SIInsertWaits::pushInstruction(MachineBasicBlock &MBB,
                                    MachineBasicBlock::iterator I) {

  // Get the hardware counter increments and sum them up
  Counters Increment = getHwCounts(*I);
  Counters Limit = ZeroCounts;
  unsigned Sum = 0;

  for (unsigned i = 0; i < 3; ++i) {
    LastIssued.Array[i] += Increment.Array[i];
    if (Increment.Array[i])
      Limit.Array[i] = LastIssued.Array[i];
    Sum += Increment.Array[i];
  }

  // If we don't increase anything then that's it
  if (Sum == 0) {
    LastOpcodeType = OTHER;
    return;
  }

  if (MBB.getParent()->getSubtarget<AMDGPUSubtarget>().getGeneration() >=
      AMDGPUSubtarget::VOLCANIC_ISLANDS) {
    // Any occurrence of consecutive VMEM or SMEM instructions forms a VMEM
    // or SMEM clause, respectively.
    // The temporary workaround is to break the clauses with S_NOP.
    // The proper solution would be to allocate registers such that all source
    // and destination registers don't overlap, e.g. this is illegal:
    //   r0 = load r2
    //   r2 = load r0
    if ((LastOpcodeType == SMEM && TII->isSMRD(*I)) ||
        (LastOpcodeType == VMEM && Increment.Named.VM)) {
      // Insert a NOP to break the clause.
      BuildMI(MBB, I, DebugLoc(), TII->get(AMDGPU::S_NOP))
          .addImm(0);
      LastInstWritesM0 = false;
    }

    if (TII->isSMRD(*I))
      LastOpcodeType = SMEM;
    else if (Increment.Named.VM)
      LastOpcodeType = VMEM;
  }

  // Remember which export instructions we have seen
  if (Increment.Named.EXP) {
    ExpInstrTypesSeen |= I->getOpcode() == AMDGPU::EXP ? 1 : 2;
  }

  for (unsigned i = 0, e = I->getNumOperands(); i != e; ++i) {
    MachineOperand &Op = I->getOperand(i);
    if (!isOpRelevant(Op))
      continue;

    const TargetRegisterClass *RC = TII->getOpRegClass(*I, i);
    RegInterval Interval = getRegInterval(RC, Op);
    for (unsigned j = Interval.first; j < Interval.second; ++j) {

      // Remember which registers we define
      if (Op.isDef())
        DefinedRegs[j] = Limit;

      // and which one we are using
      if (Op.isUse())
        UsedRegs[j] = Limit;
    }
  }
}

bool SIInsertWaits::insertWait(MachineBasicBlock &MBB,
                               MachineBasicBlock::iterator I,
                               const Counters &Required) {

  // End of program? No need to wait on anything
  // A function not returning void needs to wait, because other bytecode will
  // be appended after it and we don't know what it will be.
  if (I != MBB.end() && I->getOpcode() == AMDGPU::S_ENDPGM && ReturnsVoid)
    return false;

  // Figure out if the async instructions execute in order
  bool Ordered[3];

  // VM_CNT is always ordered
  Ordered[0] = true;

  // EXP_CNT is unordered if we have both EXP & VM-writes
  Ordered[1] = ExpInstrTypesSeen == 3;

  // LGKM_CNT is handled as always unordered. TODO: Handle LDS and GDS
  Ordered[2] = false;

  // The values we are going to put into the S_WAITCNT instruction
  Counters Counts = WaitCounts;

  // Do we really need to wait?
  bool NeedWait = false;

  for (unsigned i = 0; i < 3; ++i) {

    if (Required.Array[i] <= WaitedOn.Array[i])
      continue;

    NeedWait = true;

    if (Ordered[i]) {
      unsigned Value = LastIssued.Array[i] - Required.Array[i];

      // Adjust the value to the real hardware possibilities.
      Counts.Array[i] = std::min(Value, WaitCounts.Array[i]);

    } else
      Counts.Array[i] = 0;

    // Remember on what we have waited on.
    WaitedOn.Array[i] = LastIssued.Array[i] - Counts.Array[i];
  }

  if (!NeedWait)
    return false;

  // Reset EXP_CNT instruction types
  if (Counts.Named.EXP == 0)
    ExpInstrTypesSeen = 0;

  // Build the wait instruction
  BuildMI(MBB, I, DebugLoc(), TII->get(AMDGPU::S_WAITCNT))
          .addImm((Counts.Named.VM & 0xF) |
                  ((Counts.Named.EXP & 0x7) << 4) |
                  ((Counts.Named.LGKM & 0xF) << 8));

  LastOpcodeType = OTHER;
  LastInstWritesM0 = false;
  return true;
}

/// \brief helper function for handleOperands

Counters SIInsertWaits::handleOperands(MachineInstr &MI) {

  Counters Result = ZeroCounts;

  // S_SENDMSG implicitly waits for all outstanding LGKM transfers to finish,
  // but we also want to wait for any other outstanding transfers before
  // signalling other hardware blocks
  if (MI.getOpcode() == AMDGPU::S_SENDMSG)
    return LastIssued;

  // For each register affected by this instruction increase the result
  // TODO: We could probably just look at explicit operands if we removed VCC /
  // EXEC from SMRD dest reg classes.
  for (unsigned i = 0, e = MI.getNumOperands(); i != e; ++i) {
    MachineOperand &Op = MI.getOperand(i);
    if (!Op.isReg() || !TRI->isInAllocatableClass(Op.getReg()))
      continue;

    const TargetRegisterClass *RC = TII->getOpRegClass(MI, i);
    RegInterval Interval = getRegInterval(RC, Op);
    for (unsigned j = Interval.first; j < Interval.second; ++j) {

      if (Op.isDef()) {
        increaseCounters(Result, UsedRegs[j]);
        increaseCounters(Result, DefinedRegs[j]);
      }

      if (Op.isUse())
        increaseCounters(Result, DefinedRegs[j]);
    }
  }

  return Result;
}

void SIInsertWaits::handleSendMsg(MachineBasicBlock &MBB,
                                  MachineBasicBlock::iterator I) {
  if (MBB.getParent()->getSubtarget<AMDGPUSubtarget>().getGeneration() <
      AMDGPUSubtarget::VOLCANIC_ISLANDS)
    return;

  // There must be "S_NOP 0" between an instruction writing M0 and S_SENDMSG.
  if (LastInstWritesM0 && I->getOpcode() == AMDGPU::S_SENDMSG) {
    BuildMI(MBB, I, DebugLoc(), TII->get(AMDGPU::S_NOP)).addImm(0);
    LastInstWritesM0 = false;
    return;
  }

  // Set whether this instruction sets M0
  LastInstWritesM0 = false;

  unsigned NumOperands = I->getNumOperands();
  for (unsigned i = 0; i < NumOperands; i++) {
    const MachineOperand &Op = I->getOperand(i);

    if (Op.isReg() && Op.isDef() && Op.getReg() == AMDGPU::M0)
      LastInstWritesM0 = true;
  }
}

// FIXME: Insert waits listed in Table 4.2 "Required User-Inserted Wait States"
// around other non-memory instructions.
bool SIInsertWaits::runOnMachineFunction(MachineFunction &MF) {
  bool Changes = false;

  TII = static_cast<const SIInstrInfo *>(MF.getSubtarget().getInstrInfo());
  TRI =
      static_cast<const SIRegisterInfo *>(MF.getSubtarget().getRegisterInfo());
  const AMDGPUSubtarget &ST = MF.getSubtarget<AMDGPUSubtarget>();
  MRI = &MF.getRegInfo();

  WaitedOn = ZeroCounts;
  LastIssued = ZeroCounts;
  LastOpcodeType = OTHER;
  LastInstWritesM0 = false;
  ReturnsVoid = MF.getInfo<SIMachineFunctionInfo>()->returnsVoid();

  memset(&UsedRegs, 0, sizeof(UsedRegs));
  memset(&DefinedRegs, 0, sizeof(DefinedRegs));

  for (MachineFunction::iterator BI = MF.begin(), BE = MF.end();
       BI != BE; ++BI) {

    MachineBasicBlock &MBB = *BI;
    for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end();
         I != E; ++I) {

      // Insert required wait states for SMRD reading an SGPR written by a VALU
      if (ST.getGeneration() <= AMDGPUSubtarget::SOUTHERN_ISLANDS &&
          I->getOpcode() == AMDGPU::V_READFIRSTLANE_B32)
        TII->insertWaitStates(std::next(I), 4);

      // Wait for everything before a barrier.
      if (I->getOpcode() == AMDGPU::S_BARRIER)
        Changes |= insertWait(MBB, I, LastIssued);
      else
        Changes |= insertWait(MBB, I, handleOperands(*I));

      pushInstruction(MBB, I);
      handleSendMsg(MBB, I);
    }

    // Wait for everything at the end of the MBB
    Changes |= insertWait(MBB, MBB.getFirstTerminator(), LastIssued);
  }

  return Changes;
}


MachineIntAbstractDomain make_top_machine_int_var_pack_dbm() {
  return MachineIntAbstractDomain(
      RuntimeMachineIntDomain(RuntimeNumericDomain::top()));
}

/*******************************************************************************
 *
 * \file
 * \brief Implement make_(top|bottom)_machine_int_var_pack_dbm
 *
 * Author: Maxime Arthaud
 *
 * Contact: example@nnthink.com
 *
 * Notices:
 *
 * All Rights Reserved.
 *
 * Disclaimers:
 *
 * THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN
 * ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS,
 * RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS
 * RESULTING FROM USE OF THE SUBJECT SOFTWARE.  FURTHER, GOVERNMENT AGENCY
 * DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING THIRD-PARTY SOFTWARE,
 * IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS."
 *
 * Waiver and Indemnity:  RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST
 * THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL
 * AS ANY PRIOR RECIPIENT.  IF RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS
 * IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM SUCH
 * USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM,
 * RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD
 * HARMLESS THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS,
 * RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE IMMEDIATE,
 * UNILATERAL TERMINATION OF THIS AGREEMENT.
 *
 ******************************************************************************/

#include <ikos/core/domain/machine_int/numeric_domain_adapter.hpp>
#include <ikos/core/domain/numeric/var_packing_dbm.hpp>

#include <ikos/analyzer/analysis/value/machine_int_domain.hpp>

namespace ikos {
namespace analyzer {
namespace value {

namespace {

using RuntimeNumericDomain = core::numeric::VarPackingDBM< ZNumber, Variable* >;
using RuntimeMachineIntDomain =
    core::machine_int::NumericDomainAdapter< Variable*, RuntimeNumericDomain >;

} // end anonymous namespace

MachineIntAbstractDomain make_bottom_machine_int_var_pack_dbm() {
  return MachineIntAbstractDomain(
      RuntimeMachineIntDomain(RuntimeNumericDomain::bottom()));
}

} // end namespace value
} // end namespace analyzer
} // end namespace ikos


void objfree(OBJECT *k)									// Frees The Object (Releasing The Memory)
{
	free(k->points);									// Frees Points
}

/*
 *		This Code Was Created By Pet & Commented/Cleaned Up By Jeff Molofee
 *		If You've Found This Code Useful, Please Let Me Know.
 *		Visit NeHe Productions At http://nehe.gamedev.net
 *		Conversion to Visual Studio.NET done by GRANT JAMES(ZEUS)
 */

#include <windows.h>									// Header File For Windows
#include <math.h>										// Math Library Header File
#include <stdio.h>										// Header File For Standard Input/Output
#include <gl\gl.h>										// Header File For The OpenGL32 Library
#include <gl\glu.h>										// Header File For The GLu32 Library

HDC			hDC=NULL;									// Device Context Handle
HGLRC		hRC=NULL;									// Rendering Context Handle
HWND		hWnd=NULL;									// Window Handle
HINSTANCE	hInstance;									// Instance Handle

bool		keys[256];									// Key Array
bool		active=TRUE;								// Program's Active
bool		fullscreen=TRUE;							// Default Fullscreen To True

GLfloat		xrot,yrot,zrot,								// X, Y & Z Rotation
			xspeed,yspeed,zspeed,						// X, Y & Z Spin Speed
			cx,cy,cz=-15;								// X, Y & Z Position

int			key=1;										// Used To Make Sure Same Morph Key Is Not Pressed
int			step=0,steps=200;							// Step Counter And Maximum Number Of Steps
bool		morph=FALSE;								// Default morph To False (Not Morphing)

typedef struct											// Structure For 3D Points
{
	float	x, y, z;									// X, Y & Z Points
} VERTEX;												// Called VERTEX

typedef struct											// Structure For An Object
{
 int		verts;										// Number Of Vertices For The Object
 VERTEX		*points;									// One Vertice (Vertex x,y & z)
} OBJECT;												// Called OBJECT

int			maxver;										// Will Eventually Hold The Maximum Number Of Vertices
OBJECT		morph1,morph2,morph3,morph4,				// Our 4 Morphable Objects (morph1,2,3 & 4)
			helper,*sour,*dest;							// Helper Object, Source Object, Destination Object

LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);	// Declaration

void objallocate(OBJECT *k,int n)						// Allocate Memory For Each Object
{														// And Defines points
	k->points=(VERTEX*)malloc(sizeof(VERTEX)*n);		// Sets points Equal To VERTEX * Number Of Vertices
}														// (3 Points For Each Vertice)

void readstr(FILE *f,char *string)						// Reads A String From File (f)
{
	do													// Do This
	{
		fgets(string, 255, f);							// Gets A String Of 255 Chars Max From f (File)
	} while ((string[0] == '/') || (string[0] == '\n'));// Until End Of Line Is Reached
	return;												// Return
}

void objload(char *name,OBJECT *k)						// Loads Object From File (name)
{
	int		ver;										// Will Hold Vertice Count
	float	rx,ry,rz;									// Hold Vertex X, Y & Z Position
	FILE	*filein;									// Filename To Open
	char	oneline[255];								// Holds One Line Of Text (255 Chars Max)

	filein = fopen(name, "rt");							// Opens The File For Reading Text In Translated Mode
														// CTRL Z Symbolizes End Of File In Translated Mode
	readstr(filein,oneline);							// Jumps To Code That Reads One Line Of Text From The File
	sscanf(oneline, "Vertices: %d\n", &ver);			// Scans Text For "Vertices: ".  Number After Is Stored In ver
	k->verts=ver;										// Sets Objects verts Variable To Equal The Value Of ver
	objallocate(k,ver);									// Jumps To Code That Allocates Ram To Hold The Object

	for (int i=0;i<ver;i++)								// Loops Through The Vertices
	{
		readstr(filein,oneline);						// Reads In The Next Line Of Text
		sscanf(oneline, "%f %f %f", &rx, &ry, &rz);		// Searches For 3 Floating Point Numbers, Store In rx,ry & rz
		k->points[i].x = rx;							// Sets Objects (k) points.x Value To rx
		k->points[i].y = ry;							// Sets Objects (k) points.y Value To ry
		k->points[i].z = rz;							// Sets Objects (k) points.z Value To rz
	}
	fclose(filein);										// Close The File

	if(ver>maxver) maxver=ver;							// If ver Is Greater Than maxver Set maxver Equal To ver
}														// Keeps Track Of Highest Number Of Vertices Used In Any Of The
														// Objects
VERTEX calculate(int i)									// Calculates Movement Of Points During Morphing
{
	VERTEX a;											// Temporary Vertex Called a
	a.x=(sour->points[i].x-dest->points[i].x)/steps;	// a.x Value Equals Source x - Destination x Divided By Steps
	a.y=(sour->points[i].y-dest->points[i].y)/steps;	// a.y Value Equals Source y - Destination y Divided By Steps
	a.z=(sour->points[i].z-dest->points[i].z)/steps;	// a.z Value Equals Source z - Destination z Divided By Steps
	return a;											// Return The Results
}														// This Makes Points Move At A Speed So They All Get To Their
														// Destination At The Same Time
GLvoid ReSizeGLScene(GLsizei width, GLsizei height)		// Resize And Initialize The GL Window
{
	if (height==0)										// Prevent A Divide By Zero By
	{
		height=1;										// Making Height Equal One
	}

	glViewport(0,0,width,height);						// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix
}

int InitGL(GLvoid)										// All Setup For OpenGL Goes Here
{
	glBlendFunc(GL_SRC_ALPHA,GL_ONE);					// Set The Blending Function For Translucency
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);				// This Will Clear The Background Color To Black
	glClearDepth(1.0);									// Enables Clearing Of The Depth Buffer
	glDepthFunc(GL_LESS);								// The Type Of Depth Test To Do
	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glShadeModel(GL_SMOOTH);							// Enables Smooth Color Shading
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations

	maxver=0;											// Sets Max Vertices To 0 By Default
	objload("data/sphere.txt",&morph1);					// Load The First Object Into morph1 From File sphere.txt
	objload("data/torus.txt",&morph2);					// Load The Second Object Into morph2 From File torus.txt
	objload("data/tube.txt",&morph3);					// Load The Third Object Into morph3 From File tube.txt

	objallocate(&morph4,486);							// Manually Reserver Ram For A 4th 468 Vertice Object (morph4)
	for(int i=0;i<486;i++)								// Loop Through All 468 Vertices
	{
		morph4.points[i].x=((float)(rand()%14000)/1000)-7;	// morph4 x Point Becomes A Random Float Value From -7 to 7
		morph4.points[i].y=((float)(rand()%14000)/1000)-7;	// morph4 y Point Becomes A Random Float Value From -7 to 7
		morph4.points[i].z=((float)(rand()%14000)/1000)-7;	// morph4 z Point Becomes A Random Float Value From -7 to 7
	}

	objload("data/sphere.txt",&helper);					// Load sphere.txt Object Into Helper (Used As Starting Point)
	sour=dest=&morph1;									// Source & Destination Are Set To Equal First Object (morph1)

	return TRUE;										// Initialization Went OK
}

void DrawGLScene(GLvoid)								// Here's Where We Do All The Drawing
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer
	glLoadIdentity();									// Reset The View
	glTranslatef(cx,cy,cz);								// Translate The The Current Position To Start Drawing
	glRotatef(xrot,1,0,0);								// Rotate On The X Axis By xrot
	glRotatef(yrot,0,1,0);								// Rotate On The Y Axis By yrot
	glRotatef(zrot,0,0,1);								// Rotate On The Z Axis By zrot

	xrot+=xspeed; yrot+=yspeed; zrot+=zspeed;			// Increase xrot,yrot & zrot by xspeed, yspeed & zspeed

	GLfloat tx,ty,tz;									// Temp X, Y & Z Variables
	VERTEX q;											// Holds Returned Calculated Values For One Vertex

	glBegin(GL_POINTS);									// Begin Drawing Points
		for(int i=0;i<morph1.verts;i++)					// Loop Through All The Verts Of morph1 (All Objects Have
		{												// The Same Amount Of Verts For Simplicity, Could Use maxver Also)
			if(morph) q=calculate(i); else q.x=q.y=q.z=0;	// If morph Is True Calculate Movement Otherwise Movement=0
			helper.points[i].x-=q.x;					// Subtract q.x Units From helper.points[i].x (Move On X Axis)
			helper.points[i].y-=q.y;					// Subtract q.y Units From helper.points[i].y (Move On Y Axis)
			helper.points[i].z-=q.z;					// Subtract q.z Units From helper.points[i].z (Move On Z Axis)
			tx=helper.points[i].x;						// Make Temp X Variable Equal To Helper's X Variable
			ty=helper.points[i].y;						// Make Temp Y Variable Equal To Helper's Y Variable
			tz=helper.points[i].z;						// Make Temp Z Variable Equal To Helper's Z Variable

			glColor3f(0,1,1);							// Set Color To A Bright Shade Of Off Blue
			glVertex3f(tx,ty,tz);						// Draw A Point At The Current Temp Values (Vertex)
			glColor3f(0,0.5f,1);						// Darken Color A Bit
			tx-=2*q.x; ty-=2*q.y; ty-=2*q.y;			// Calculate Two Positions Ahead
			glVertex3f(tx,ty,tz);						// Draw A Second Point At The Newly Calculate Position
			glColor3f(0,0,1);							// Set Color To A Very Dark Blue
			tx-=2*q.x; ty-=2*q.y; ty-=2*q.y;			// Calculate Two More Positions Ahead
			glVertex3f(tx,ty,tz);						// Draw A Third Point At The Second New Position
		}												// This Creates A Ghostly Tail As Points Move
	glEnd();											// Done Drawing Points

	// If We're Morphing And We Haven't Gone Through All 200 Steps Increase Our Step Counter
	// Otherwise Set Morphing To False, Make Source=Destination And Set The Step Counter Back To Zero.
	if(morph && step<=steps)step++; else { morph=FALSE; sour=dest; step=0;}
}

GLvoid KillGLWindow(GLvoid)								// Properly Kill The Window
{
	objfree(&morph1);									// Jump To Code To Release morph1 Allocated Ram
	objfree(&morph2);									// Jump To Code To Release morph2 Allocated Ram
	objfree(&morph3);									// Jump To Code To Release morph3 Allocated Ram
	objfree(&morph4);									// Jump To Code To Release morph4 Allocated Ram
	objfree(&helper);									// Jump To Code To Release helper Allocated Ram

	if (fullscreen)										// Are We In Fullscreen Mode?
	{
		ChangeDisplaySettings(NULL,0);					// If So Switch Back To The Desktop
		ShowCursor(TRUE);								// Show Mouse Pointer
	}

	if (hRC)											// Do We Have A Rendering Context?
	{
		if (!wglMakeCurrent(NULL,NULL))					// Are We Able To Release The DC And RC Contexts?
		{
			MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}

		if (!wglDeleteContext(hRC))						// Are We Able To Delete The RC?
		{
			MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}
		hRC=NULL;										// Set RC To NULL
	}

	if (hDC && !ReleaseDC(hWnd,hDC))					// Are We Able To Release The DC
	{
		MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hDC=NULL;										// Set DC To NULL
	}

	if (hWnd && !DestroyWindow(hWnd))					// Are We Able To Destroy The Window?
	{
		MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hWnd=NULL;										// Set hWnd To NULL
	}

	if (!UnregisterClass("OpenGL",hInstance))			// Are We Able To Unregister Class
	{
		MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hInstance=NULL;									// Set hInstance To NULL
	}
}

/*	This Code Creates Our OpenGL Window.  Parameters Are:					*
 *	title			- Title To Appear At The Top Of The Window				*
 *	width			- Width Of The GL Window Or Fullscreen Mode				*
 *	height			- Height Of The GL Window Or Fullscreen Mode			*
 *	bits			- Number Of Bits To Use For Color (8/16/24/32)			*
 *	fullscreenflag	- Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE)	*/

BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
	GLuint		PixelFormat;			// Holds The Results After Searching For A Match
	WNDCLASS	wc;						// Windows Class Structure
	DWORD		dwExStyle;				// Window Extended Style
	DWORD		dwStyle;				// Window Style
	RECT		WindowRect;				// Grabs Rectangle Upper Left / Lower Right Values
	WindowRect.left=(long)0;			// Set Left Value To 0
	WindowRect.right=(long)width;		// Set Right Value To Requested Width
	WindowRect.top=(long)0;				// Set Top Value To 0
	WindowRect.bottom=(long)height;		// Set Bottom Value To Requested Height

	fullscreen=fullscreenflag;			// Set The Global Fullscreen Flag

	hInstance			= GetModuleHandle(NULL);				// Grab An Instance For Our Window
	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	// Redraw On Size, And Own DC For Window.
	wc.lpfnWndProc		= (WNDPROC) WndProc;					// WndProc Handles Messages
	wc.cbClsExtra		= 0;									// No Extra Window Data
	wc.cbWndExtra		= 0;									// No Extra Window Data
	wc.hInstance		= hInstance;							// Set The Instance
	wc.hIcon			= LoadIcon(NULL, IDI_WINLOGO);			// Load The Default Icon
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);			// Load The Arrow Pointer
	wc.hbrBackground	= NULL;									// No Background Required For GL
	wc.lpszMenuName		= NULL;									// We Don't Want A Menu
	wc.lpszClassName	= "OpenGL";								// Set The Class Name

	if (!RegisterClass(&wc))									// Attempt To Register The Window Class
	{
		MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	if (fullscreen)												// Attempt Fullscreen Mode?
	{
		DEVMODE dmScreenSettings;								// Device Mode
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));	// Makes Sure Memory's Cleared
		dmScreenSettings.dmSize=sizeof(dmScreenSettings);		// Size Of The Devmode Structure
		dmScreenSettings.dmPelsWidth	= width;				// Selected Screen Width
		dmScreenSettings.dmPelsHeight	= height;				// Selected Screen Height
		dmScreenSettings.dmBitsPerPel	= bits;					// Selected Bits Per Pixel
		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

		// Try To Set Selected Mode And Get Results.  NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
		if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
		{
			// If The Mode Fails, Offer Two Options.  Quit Or Use Windowed Mode.
			if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
			{
				fullscreen=FALSE;								// Windowed Mode Selected.  Fullscreen = FALSE
			}
			else
			{
				// Pop Up A Message Box Letting User Know The Program Is Closing.
				MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
				return FALSE;									// Return FALSE
			}
		}
	}

	if (fullscreen)												// Are We Still In Fullscreen Mode?
	{
		dwExStyle=WS_EX_APPWINDOW;								// Window Extended Style
		dwStyle=WS_POPUP;										// Windows Style
		ShowCursor(FALSE);										// Hide Mouse Pointer
	}
	else
	{
		dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;			// Window Extended Style
		dwStyle=WS_OVERLAPPEDWINDOW;							// Windows Style
	}

	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);	// Adjust Window To True Requested Size

	// Create The Window
	if (!(hWnd=CreateWindowEx(	dwExStyle,						// Extended Style For The Window
								"OpenGL",						// Class Name
								title,							// Window Title
								dwStyle |						// Defined Window Style
								WS_CLIPSIBLINGS |				// Required Window Style
								WS_CLIPCHILDREN,				// Required Window Style
								0, 0,							// Window Position
								WindowRect.right-WindowRect.left,	// Calculate Window Width
								WindowRect.bottom-WindowRect.top,	// Calculate Window Height
								NULL,							// No Parent Window
								NULL,							// No Menu
								hInstance,						// Instance
								NULL)))							// Dont Pass Anything To WM_CREATE
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	static	PIXELFORMATDESCRIPTOR pfd=							// pfd Tells Windows How We Want Things To Be
	{
		sizeof(PIXELFORMATDESCRIPTOR),							// Size Of This Pixel Format Descriptor
		1,														// Version Number
		PFD_DRAW_TO_WINDOW |									// Format Must Support Window
		PFD_SUPPORT_OPENGL |									// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,										// Must Support Double Buffering
		PFD_TYPE_RGBA,											// Request An RGBA Format
		bits,													// Select Our Color Depth
		0, 0, 0, 0, 0, 0,										// Color Bits Ignored
		0,														// No Alpha Buffer
		0,														// Shift Bit Ignored
		0,														// No Accumulation Buffer
		0, 0, 0, 0,												// Accumulation Bits Ignored
		16,														// 16Bit Z-Buffer (Depth Buffer)
		0,														// No Stencil Buffer
		0,														// No Auxiliary Buffer
		PFD_MAIN_PLANE,											// Main Drawing Layer
		0,														// Reserved
		0, 0, 0													// Layer Masks Ignored
	};

	if (!(hDC=GetDC(hWnd)))										// Did We Get A Device Context?
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))				// Did Windows Find A Matching Pixel Format?
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	if(!SetPixelFormat(hDC,PixelFormat,&pfd))					// Are We Able To Set The Pixel Format?
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	if (!(hRC=wglCreateContext(hDC)))							// Are We Able To Get A Rendering Context?
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	if(!wglMakeCurrent(hDC,hRC))								// Try To Activate The Rendering Context
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	ShowWindow(hWnd,SW_SHOW);									// Show The Window
	SetForegroundWindow(hWnd);									// Slightly Higher Priority
	SetFocus(hWnd);												// Sets Keyboard Focus To The Window
	ReSizeGLScene(width, height);								// Set Up Our Perspective GL Screen

	if (!InitGL())												// Initialize Our Newly Created GL Window
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	return TRUE;												// Success
}

LRESULT CALLBACK WndProc(	HWND	hWnd,						// Handle For This Window
							UINT	uMsg,						// Message For This Window
							WPARAM	wParam,						// Additional Message Information
							LPARAM	lParam)						// Additional Message Information
{
	switch (uMsg)												// Check For Windows Messages
	{
		case WM_ACTIVATE:										// Watch For Window Activate Message
		{
			if (!HIWORD(wParam))								// Check Minimization State
			{
				active=TRUE;									// Program Is Active
			}
			else												// Otherwise
			{
				active=FALSE;									// Program Is No Longer Active
			}

			return 0;											// Return To The Message Loop
		}

		case WM_SYSCOMMAND:										// Intercept System Commands
		{
			switch (wParam)										// Check System Calls
			{
				case SC_SCREENSAVE:								// Screensaver Trying To Start?
				case SC_MONITORPOWER:							// Monitor Trying To Enter Powersave?
				return 0;										// Prevent From Happening
			}
			break;												// Exit
		}

		case WM_CLOSE:											// Did We Receive A Close Message?
		{
			PostQuitMessage(0);									// Send A Quit Message
			return 0;											// Jump Back
		}

		case WM_KEYDOWN:										// Is A Key Being Held Down?
		{
			keys[wParam] = TRUE;								// If So, Mark It As TRUE
			return 0;											// Jump Back
		}

		case WM_KEYUP:											// Has A Key Been Released?
		{
			keys[wParam] = FALSE;								// If So, Mark It As FALSE
			return 0;											// Jump Back
		}

		case WM_SIZE:											// Resize The OpenGL Window
		{
			ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));		// LoWord=Width, HiWord=Height
			return 0;											// Jump Back
		}
	}

	// Pass All Unhandled Messages To DefWindowProc
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

int WINAPI WinMain(	HINSTANCE	hInstance,						// Instance
					HINSTANCE	hPrevInstance,					// Previous Instance
					LPSTR		lpCmdLine,						// Command Line Parameters
					int			nCmdShow)						// Window Show State
{
	MSG		msg;												// Windows Message Structure
	BOOL	done=FALSE;											// Bool Variable To Exit Loop

	// Ask The User Which Screen Mode They Prefer
	if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
	{
		fullscreen=FALSE;										// Windowed Mode
	}

	// Create Our OpenGL Window
	if (!CreateGLWindow("Piotr Cieslak & NeHe's Morphing Points Tutorial",640,480,16,fullscreen))
	{
		return 0;												// Quit If Window Was Not Created
	}

	while(!done)												// Loop That Runs While done=FALSE
	{
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))				// Is There A Message Waiting?
		{
			if (msg.message==WM_QUIT)							// Have We Received A Quit Message?
			{
				done=TRUE;										// If So done=TRUE
			}
			else												// If Not, Deal With Window Messages
			{
				TranslateMessage(&msg);							// Translate The Message
				DispatchMessage(&msg);							// Dispatch The Message
			}
		}
		else													// If There Are No Messages
		{
			// Draw The Scene.  Watch For ESC Key And Quit Messages From DrawGLScene()
			if (active && keys[VK_ESCAPE])						// Active?  Was There A Quit Received?
			{
				done=TRUE;										// ESC or DrawGLScene Signalled A Quit
			}
			else												// Not Time To Quit, Update Screen
			{
				DrawGLScene();									// Draw The Scene (Don't Draw When Inactive 1% CPU Use)
				SwapBuffers(hDC);								// Swap Buffers (Double Buffering)

				if(keys[VK_PRIOR])								// Is Page Up Being Pressed?
					zspeed+=0.01f;								// Increase zspeed

				if(keys[VK_NEXT])								// Is Page Down Being Pressed?
					zspeed-=0.01f;								// Decrease zspeed

				if(keys[VK_DOWN])								// Is Page Up Being Pressed?
					xspeed+=0.01f;								// Increase xspeed

				if(keys[VK_UP])									// Is Page Up Being Pressed?
					xspeed-=0.01f;								// Decrease xspeed

				if(keys[VK_RIGHT])								// Is Page Up Being Pressed?
					yspeed+=0.01f;								// Increase yspeed

				if(keys[VK_LEFT])								// Is Page Up Being Pressed?
					yspeed-=0.01f;								// Decrease yspeed

				if (keys['Q'])									// Is Q Key Being Pressed?
				 cz-=0.01f;										// Move Object Away From Viewer

				if (keys['Z'])									// Is Z Key Being Pressed?
				 cz+=0.01f;										// Move Object Towards Viewer

				if (keys['W'])									// Is W Key Being Pressed?
				 cy+=0.01f;										// Move Object Up

				if (keys['S'])									// Is S Key Being Pressed?
				 cy-=0.01f;										// Move Object Down

				if (keys['D'])									// Is D Key Being Pressed?
				 cx+=0.01f;										// Move Object Right

				if (keys['A'])									// Is A Key Being Pressed?
				 cx-=0.01f;										// Move Object Left

				if (keys['1'] && (key!=1) && !morph)			// Is 1 Pressed, key Not Equal To 1 And Morph False?
				{
					key=1;										// Sets key To 1 (To Prevent Pressing 1 2x In A Row)
					morph=TRUE;									// Set morph To True (Starts Morphing Process)
					dest=&morph1;								// Destination Object To Morph To Becomes morph1
				}
				if (keys['2'] && (key!=2) && !morph)			// Is 2 Pressed, key Not Equal To 2 And Morph False?
				{
					key=2;										// Sets key To 2 (To Prevent Pressing 2 2x In A Row)
					morph=TRUE;									// Set morph To True (Starts Morphing Process)
					dest=&morph2;								// Destination Object To Morph To Becomes morph2
				}
				if (keys['3'] && (key!=3) && !morph)			// Is 3 Pressed, key Not Equal To 3 And Morph False?
				{
					key=3;										// Sets key To 3 (To Prevent Pressing 3 2x In A Row)
					morph=TRUE;									// Set morph To True (Starts Morphing Process)
					dest=&morph3;								// Destination Object To Morph To Becomes morph3
				}
				if (keys['4'] && (key!=4) && !morph)			// Is 4 Pressed, key Not Equal To 4 And Morph False?
				{
					key=4;										// Sets key To 4 (To Prevent Pressing 4 2x In A Row)
					morph=TRUE;									// Set morph To True (Starts Morphing Process)
					dest=&morph4;								// Destination Object To Morph To Becomes morph4
				}

				if (keys[VK_F1])								// Is F1 Being Pressed?
				{
					keys[VK_F1]=FALSE;							// If So Make Key FALSE
					KillGLWindow();								// Kill Our Current Window
					fullscreen=!fullscreen;						// Toggle Fullscreen / Windowed Mode
					// Recreate Our OpenGL Window
					if (!CreateGLWindow("Piotr Cieslak & NeHe's Morphing Points Tutorial",640,480,16,fullscreen))
					{
						return 0;								// Quit If Window Was Not Created
					}
				}
			}
		}
	}

	// Shutdown
	KillGLWindow();												// Kill The Window
	return (msg.wParam);										// Exit The Program
}


switch (wParam)										// Check System Calls
			{
				case SC_SCREENSAVE:								// Screensaver Trying To Start?
				case SC_MONITORPOWER:							// Monitor Trying To Enter Powersave?
				return 0;										// Prevent From Happening
			}

/*
 *		This Code Was Created By Pet & Commented/Cleaned Up By Jeff Molofee
 *		If You've Found This Code Useful, Please Let Me Know.
 *		Visit NeHe Productions At http://nehe.gamedev.net
 *		Conversion to Visual Studio.NET done by GRANT JAMES(ZEUS)
 */

#include <windows.h>									// Header File For Windows
#include <math.h>										// Math Library Header File
#include <stdio.h>										// Header File For Standard Input/Output
#include <gl\gl.h>										// Header File For The OpenGL32 Library
#include <gl\glu.h>										// Header File For The GLu32 Library

HDC			hDC=NULL;									// Device Context Handle
HGLRC		hRC=NULL;									// Rendering Context Handle
HWND		hWnd=NULL;									// Window Handle
HINSTANCE	hInstance;									// Instance Handle

bool		keys[256];									// Key Array
bool		active=TRUE;								// Program's Active
bool		fullscreen=TRUE;							// Default Fullscreen To True

GLfloat		xrot,yrot,zrot,								// X, Y & Z Rotation
			xspeed,yspeed,zspeed,						// X, Y & Z Spin Speed
			cx,cy,cz=-15;								// X, Y & Z Position

int			key=1;										// Used To Make Sure Same Morph Key Is Not Pressed
int			step=0,steps=200;							// Step Counter And Maximum Number Of Steps
bool		morph=FALSE;								// Default morph To False (Not Morphing)

typedef struct											// Structure For 3D Points
{
	float	x, y, z;									// X, Y & Z Points
} VERTEX;												// Called VERTEX

typedef struct											// Structure For An Object
{
 int		verts;										// Number Of Vertices For The Object
 VERTEX		*points;									// One Vertice (Vertex x,y & z)
} OBJECT;												// Called OBJECT

int			maxver;										// Will Eventually Hold The Maximum Number Of Vertices
OBJECT		morph1,morph2,morph3,morph4,				// Our 4 Morphable Objects (morph1,2,3 & 4)
			helper,*sour,*dest;							// Helper Object, Source Object, Destination Object

LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);	// Declaration

void objallocate(OBJECT *k,int n)						// Allocate Memory For Each Object
{														// And Defines points
	k->points=(VERTEX*)malloc(sizeof(VERTEX)*n);		// Sets points Equal To VERTEX * Number Of Vertices
}														// (3 Points For Each Vertice)

void objfree(OBJECT *k)									// Frees The Object (Releasing The Memory)
{
	free(k->points);									// Frees Points
}

void readstr(FILE *f,char *string)						// Reads A String From File (f)
{
	do													// Do This
	{
		fgets(string, 255, f);							// Gets A String Of 255 Chars Max From f (File)
	} while ((string[0] == '/') || (string[0] == '\n'));// Until End Of Line Is Reached
	return;												// Return
}

void objload(char *name,OBJECT *k)						// Loads Object From File (name)
{
	int		ver;										// Will Hold Vertice Count
	float	rx,ry,rz;									// Hold Vertex X, Y & Z Position
	FILE	*filein;									// Filename To Open
	char	oneline[255];								// Holds One Line Of Text (255 Chars Max)

	filein = fopen(name, "rt");							// Opens The File For Reading Text In Translated Mode
														// CTRL Z Symbolizes End Of File In Translated Mode
	readstr(filein,oneline);							// Jumps To Code That Reads One Line Of Text From The File
	sscanf(oneline, "Vertices: %d\n", &ver);			// Scans Text For "Vertices: ".  Number After Is Stored In ver
	k->verts=ver;										// Sets Objects verts Variable To Equal The Value Of ver
	objallocate(k,ver);									// Jumps To Code That Allocates Ram To Hold The Object

	for (int i=0;i<ver;i++)								// Loops Through The Vertices
	{
		readstr(filein,oneline);						// Reads In The Next Line Of Text
		sscanf(oneline, "%f %f %f", &rx, &ry, &rz);		// Searches For 3 Floating Point Numbers, Store In rx,ry & rz
		k->points[i].x = rx;							// Sets Objects (k) points.x Value To rx
		k->points[i].y = ry;							// Sets Objects (k) points.y Value To ry
		k->points[i].z = rz;							// Sets Objects (k) points.z Value To rz
	}
	fclose(filein);										// Close The File

	if(ver>maxver) maxver=ver;							// If ver Is Greater Than maxver Set maxver Equal To ver
}														// Keeps Track Of Highest Number Of Vertices Used In Any Of The
														// Objects
VERTEX calculate(int i)									// Calculates Movement Of Points During Morphing
{
	VERTEX a;											// Temporary Vertex Called a
	a.x=(sour->points[i].x-dest->points[i].x)/steps;	// a.x Value Equals Source x - Destination x Divided By Steps
	a.y=(sour->points[i].y-dest->points[i].y)/steps;	// a.y Value Equals Source y - Destination y Divided By Steps
	a.z=(sour->points[i].z-dest->points[i].z)/steps;	// a.z Value Equals Source z - Destination z Divided By Steps
	return a;											// Return The Results
}														// This Makes Points Move At A Speed So They All Get To Their
														// Destination At The Same Time
GLvoid ReSizeGLScene(GLsizei width, GLsizei height)		// Resize And Initialize The GL Window
{
	if (height==0)										// Prevent A Divide By Zero By
	{
		height=1;										// Making Height Equal One
	}

	glViewport(0,0,width,height);						// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix
}

int InitGL(GLvoid)										// All Setup For OpenGL Goes Here
{
	glBlendFunc(GL_SRC_ALPHA,GL_ONE);					// Set The Blending Function For Translucency
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);				// This Will Clear The Background Color To Black
	glClearDepth(1.0);									// Enables Clearing Of The Depth Buffer
	glDepthFunc(GL_LESS);								// The Type Of Depth Test To Do
	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glShadeModel(GL_SMOOTH);							// Enables Smooth Color Shading
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations

	maxver=0;											// Sets Max Vertices To 0 By Default
	objload("data/sphere.txt",&morph1);					// Load The First Object Into morph1 From File sphere.txt
	objload("data/torus.txt",&morph2);					// Load The Second Object Into morph2 From File torus.txt
	objload("data/tube.txt",&morph3);					// Load The Third Object Into morph3 From File tube.txt

	objallocate(&morph4,486);							// Manually Reserver Ram For A 4th 468 Vertice Object (morph4)
	for(int i=0;i<486;i++)								// Loop Through All 468 Vertices
	{
		morph4.points[i].x=((float)(rand()%14000)/1000)-7;	// morph4 x Point Becomes A Random Float Value From -7 to 7
		morph4.points[i].y=((float)(rand()%14000)/1000)-7;	// morph4 y Point Becomes A Random Float Value From -7 to 7
		morph4.points[i].z=((float)(rand()%14000)/1000)-7;	// morph4 z Point Becomes A Random Float Value From -7 to 7
	}

	objload("data/sphere.txt",&helper);					// Load sphere.txt Object Into Helper (Used As Starting Point)
	sour=dest=&morph1;									// Source & Destination Are Set To Equal First Object (morph1)

	return TRUE;										// Initialization Went OK
}

void DrawGLScene(GLvoid)								// Here's Where We Do All The Drawing
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer
	glLoadIdentity();									// Reset The View
	glTranslatef(cx,cy,cz);								// Translate The The Current Position To Start Drawing
	glRotatef(xrot,1,0,0);								// Rotate On The X Axis By xrot
	glRotatef(yrot,0,1,0);								// Rotate On The Y Axis By yrot
	glRotatef(zrot,0,0,1);								// Rotate On The Z Axis By zrot

	xrot+=xspeed; yrot+=yspeed; zrot+=zspeed;			// Increase xrot,yrot & zrot by xspeed, yspeed & zspeed

	GLfloat tx,ty,tz;									// Temp X, Y & Z Variables
	VERTEX q;											// Holds Returned Calculated Values For One Vertex

	glBegin(GL_POINTS);									// Begin Drawing Points
		for(int i=0;i<morph1.verts;i++)					// Loop Through All The Verts Of morph1 (All Objects Have
		{												// The Same Amount Of Verts For Simplicity, Could Use maxver Also)
			if(morph) q=calculate(i); else q.x=q.y=q.z=0;	// If morph Is True Calculate Movement Otherwise Movement=0
			helper.points[i].x-=q.x;					// Subtract q.x Units From helper.points[i].x (Move On X Axis)
			helper.points[i].y-=q.y;					// Subtract q.y Units From helper.points[i].y (Move On Y Axis)
			helper.points[i].z-=q.z;					// Subtract q.z Units From helper.points[i].z (Move On Z Axis)
			tx=helper.points[i].x;						// Make Temp X Variable Equal To Helper's X Variable
			ty=helper.points[i].y;						// Make Temp Y Variable Equal To Helper's Y Variable
			tz=helper.points[i].z;						// Make Temp Z Variable Equal To Helper's Z Variable

			glColor3f(0,1,1);							// Set Color To A Bright Shade Of Off Blue
			glVertex3f(tx,ty,tz);						// Draw A Point At The Current Temp Values (Vertex)
			glColor3f(0,0.5f,1);						// Darken Color A Bit
			tx-=2*q.x; ty-=2*q.y; ty-=2*q.y;			// Calculate Two Positions Ahead
			glVertex3f(tx,ty,tz);						// Draw A Second Point At The Newly Calculate Position
			glColor3f(0,0,1);							// Set Color To A Very Dark Blue
			tx-=2*q.x; ty-=2*q.y; ty-=2*q.y;			// Calculate Two More Positions Ahead
			glVertex3f(tx,ty,tz);						// Draw A Third Point At The Second New Position
		}												// This Creates A Ghostly Tail As Points Move
	glEnd();											// Done Drawing Points

	// If We're Morphing And We Haven't Gone Through All 200 Steps Increase Our Step Counter
	// Otherwise Set Morphing To False, Make Source=Destination And Set The Step Counter Back To Zero.
	if(morph && step<=steps)step++; else { morph=FALSE; sour=dest; step=0;}
}

GLvoid KillGLWindow(GLvoid)								// Properly Kill The Window
{
	objfree(&morph1);									// Jump To Code To Release morph1 Allocated Ram
	objfree(&morph2);									// Jump To Code To Release morph2 Allocated Ram
	objfree(&morph3);									// Jump To Code To Release morph3 Allocated Ram
	objfree(&morph4);									// Jump To Code To Release morph4 Allocated Ram
	objfree(&helper);									// Jump To Code To Release helper Allocated Ram

	if (fullscreen)										// Are We In Fullscreen Mode?
	{
		ChangeDisplaySettings(NULL,0);					// If So Switch Back To The Desktop
		ShowCursor(TRUE);								// Show Mouse Pointer
	}

	if (hRC)											// Do We Have A Rendering Context?
	{
		if (!wglMakeCurrent(NULL,NULL))					// Are We Able To Release The DC And RC Contexts?
		{
			MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}

		if (!wglDeleteContext(hRC))						// Are We Able To Delete The RC?
		{
			MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}
		hRC=NULL;										// Set RC To NULL
	}

	if (hDC && !ReleaseDC(hWnd,hDC))					// Are We Able To Release The DC
	{
		MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hDC=NULL;										// Set DC To NULL
	}

	if (hWnd && !DestroyWindow(hWnd))					// Are We Able To Destroy The Window?
	{
		MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hWnd=NULL;										// Set hWnd To NULL
	}

	if (!UnregisterClass("OpenGL",hInstance))			// Are We Able To Unregister Class
	{
		MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hInstance=NULL;									// Set hInstance To NULL
	}
}

/*	This Code Creates Our OpenGL Window.  Parameters Are:					*
 *	title			- Title To Appear At The Top Of The Window				*
 *	width			- Width Of The GL Window Or Fullscreen Mode				*
 *	height			- Height Of The GL Window Or Fullscreen Mode			*
 *	bits			- Number Of Bits To Use For Color (8/16/24/32)			*
 *	fullscreenflag	- Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE)	*/

BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
	GLuint		PixelFormat;			// Holds The Results After Searching For A Match
	WNDCLASS	wc;						// Windows Class Structure
	DWORD		dwExStyle;				// Window Extended Style
	DWORD		dwStyle;				// Window Style
	RECT		WindowRect;				// Grabs Rectangle Upper Left / Lower Right Values
	WindowRect.left=(long)0;			// Set Left Value To 0
	WindowRect.right=(long)width;		// Set Right Value To Requested Width
	WindowRect.top=(long)0;				// Set Top Value To 0
	WindowRect.bottom=(long)height;		// Set Bottom Value To Requested Height

	fullscreen=fullscreenflag;			// Set The Global Fullscreen Flag

	hInstance			= GetModuleHandle(NULL);				// Grab An Instance For Our Window
	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	// Redraw On Size, And Own DC For Window.
	wc.lpfnWndProc		= (WNDPROC) WndProc;					// WndProc Handles Messages
	wc.cbClsExtra		= 0;									// No Extra Window Data
	wc.cbWndExtra		= 0;									// No Extra Window Data
	wc.hInstance		= hInstance;							// Set The Instance
	wc.hIcon			= LoadIcon(NULL, IDI_WINLOGO);			// Load The Default Icon
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);			// Load The Arrow Pointer
	wc.hbrBackground	= NULL;									// No Background Required For GL
	wc.lpszMenuName		= NULL;									// We Don't Want A Menu
	wc.lpszClassName	= "OpenGL";								// Set The Class Name

	if (!RegisterClass(&wc))									// Attempt To Register The Window Class
	{
		MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	if (fullscreen)												// Attempt Fullscreen Mode?
	{
		DEVMODE dmScreenSettings;								// Device Mode
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));	// Makes Sure Memory's Cleared
		dmScreenSettings.dmSize=sizeof(dmScreenSettings);		// Size Of The Devmode Structure
		dmScreenSettings.dmPelsWidth	= width;				// Selected Screen Width
		dmScreenSettings.dmPelsHeight	= height;				// Selected Screen Height
		dmScreenSettings.dmBitsPerPel	= bits;					// Selected Bits Per Pixel
		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

		// Try To Set Selected Mode And Get Results.  NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
		if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
		{
			// If The Mode Fails, Offer Two Options.  Quit Or Use Windowed Mode.
			if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
			{
				fullscreen=FALSE;								// Windowed Mode Selected.  Fullscreen = FALSE
			}
			else
			{
				// Pop Up A Message Box Letting User Know The Program Is Closing.
				MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
				return FALSE;									// Return FALSE
			}
		}
	}

	if (fullscreen)												// Are We Still In Fullscreen Mode?
	{
		dwExStyle=WS_EX_APPWINDOW;								// Window Extended Style
		dwStyle=WS_POPUP;										// Windows Style
		ShowCursor(FALSE);										// Hide Mouse Pointer
	}
	else
	{
		dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;			// Window Extended Style
		dwStyle=WS_OVERLAPPEDWINDOW;							// Windows Style
	}

	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);	// Adjust Window To True Requested Size

	// Create The Window
	if (!(hWnd=CreateWindowEx(	dwExStyle,						// Extended Style For The Window
								"OpenGL",						// Class Name
								title,							// Window Title
								dwStyle |						// Defined Window Style
								WS_CLIPSIBLINGS |				// Required Window Style
								WS_CLIPCHILDREN,				// Required Window Style
								0, 0,							// Window Position
								WindowRect.right-WindowRect.left,	// Calculate Window Width
								WindowRect.bottom-WindowRect.top,	// Calculate Window Height
								NULL,							// No Parent Window
								NULL,							// No Menu
								hInstance,						// Instance
								NULL)))							// Dont Pass Anything To WM_CREATE
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	static	PIXELFORMATDESCRIPTOR pfd=							// pfd Tells Windows How We Want Things To Be
	{
		sizeof(PIXELFORMATDESCRIPTOR),							// Size Of This Pixel Format Descriptor
		1,														// Version Number
		PFD_DRAW_TO_WINDOW |									// Format Must Support Window
		PFD_SUPPORT_OPENGL |									// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,										// Must Support Double Buffering
		PFD_TYPE_RGBA,											// Request An RGBA Format
		bits,													// Select Our Color Depth
		0, 0, 0, 0, 0, 0,										// Color Bits Ignored
		0,														// No Alpha Buffer
		0,														// Shift Bit Ignored
		0,														// No Accumulation Buffer
		0, 0, 0, 0,												// Accumulation Bits Ignored
		16,														// 16Bit Z-Buffer (Depth Buffer)
		0,														// No Stencil Buffer
		0,														// No Auxiliary Buffer
		PFD_MAIN_PLANE,											// Main Drawing Layer
		0,														// Reserved
		0, 0, 0													// Layer Masks Ignored
	};

	if (!(hDC=GetDC(hWnd)))										// Did We Get A Device Context?
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))				// Did Windows Find A Matching Pixel Format?
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	if(!SetPixelFormat(hDC,PixelFormat,&pfd))					// Are We Able To Set The Pixel Format?
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	if (!(hRC=wglCreateContext(hDC)))							// Are We Able To Get A Rendering Context?
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	if(!wglMakeCurrent(hDC,hRC))								// Try To Activate The Rendering Context
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	ShowWindow(hWnd,SW_SHOW);									// Show The Window
	SetForegroundWindow(hWnd);									// Slightly Higher Priority
	SetFocus(hWnd);												// Sets Keyboard Focus To The Window
	ReSizeGLScene(width, height);								// Set Up Our Perspective GL Screen

	if (!InitGL())												// Initialize Our Newly Created GL Window
	{
		KillGLWindow();											// Reset The Display
		MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;											// Return FALSE
	}

	return TRUE;												// Success
}

LRESULT CALLBACK WndProc(	HWND	hWnd,						// Handle For This Window
							UINT	uMsg,						// Message For This Window
							WPARAM	wParam,						// Additional Message Information
							LPARAM	lParam)						// Additional Message Information
{
	switch (uMsg)												// Check For Windows Messages
	{
		case WM_ACTIVATE:										// Watch For Window Activate Message
		{
			if (!HIWORD(wParam))								// Check Minimization State
			{
				active=TRUE;									// Program Is Active
			}
			else												// Otherwise
			{
				active=FALSE;									// Program Is No Longer Active
			}

			return 0;											// Return To The Message Loop
		}

		case WM_SYSCOMMAND:										// Intercept System Commands
		{

			break;												// Exit
		}

		case WM_CLOSE:											// Did We Receive A Close Message?
		{
			PostQuitMessage(0);									// Send A Quit Message
			return 0;											// Jump Back
		}

		case WM_KEYDOWN:										// Is A Key Being Held Down?
		{
			keys[wParam] = TRUE;								// If So, Mark It As TRUE
			return 0;											// Jump Back
		}

		case WM_KEYUP:											// Has A Key Been Released?
		{
			keys[wParam] = FALSE;								// If So, Mark It As FALSE
			return 0;											// Jump Back
		}

		case WM_SIZE:											// Resize The OpenGL Window
		{
			ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));		// LoWord=Width, HiWord=Height
			return 0;											// Jump Back
		}
	}

	// Pass All Unhandled Messages To DefWindowProc
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

int WINAPI WinMain(	HINSTANCE	hInstance,						// Instance
					HINSTANCE	hPrevInstance,					// Previous Instance
					LPSTR		lpCmdLine,						// Command Line Parameters
					int			nCmdShow)						// Window Show State
{
	MSG		msg;												// Windows Message Structure
	BOOL	done=FALSE;											// Bool Variable To Exit Loop

	// Ask The User Which Screen Mode They Prefer
	if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
	{
		fullscreen=FALSE;										// Windowed Mode
	}

	// Create Our OpenGL Window
	if (!CreateGLWindow("Piotr Cieslak & NeHe's Morphing Points Tutorial",640,480,16,fullscreen))
	{
		return 0;												// Quit If Window Was Not Created
	}

	while(!done)												// Loop That Runs While done=FALSE
	{
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))				// Is There A Message Waiting?
		{
			if (msg.message==WM_QUIT)							// Have We Received A Quit Message?
			{
				done=TRUE;										// If So done=TRUE
			}
			else												// If Not, Deal With Window Messages
			{
				TranslateMessage(&msg);							// Translate The Message
				DispatchMessage(&msg);							// Dispatch The Message
			}
		}
		else													// If There Are No Messages
		{
			// Draw The Scene.  Watch For ESC Key And Quit Messages From DrawGLScene()
			if (active && keys[VK_ESCAPE])						// Active?  Was There A Quit Received?
			{
				done=TRUE;										// ESC or DrawGLScene Signalled A Quit
			}
			else												// Not Time To Quit, Update Screen
			{
				DrawGLScene();									// Draw The Scene (Don't Draw When Inactive 1% CPU Use)
				SwapBuffers(hDC);								// Swap Buffers (Double Buffering)

				if(keys[VK_PRIOR])								// Is Page Up Being Pressed?
					zspeed+=0.01f;								// Increase zspeed

				if(keys[VK_NEXT])								// Is Page Down Being Pressed?
					zspeed-=0.01f;								// Decrease zspeed

				if(keys[VK_DOWN])								// Is Page Up Being Pressed?
					xspeed+=0.01f;								// Increase xspeed

				if(keys[VK_UP])									// Is Page Up Being Pressed?
					xspeed-=0.01f;								// Decrease xspeed

				if(keys[VK_RIGHT])								// Is Page Up Being Pressed?
					yspeed+=0.01f;								// Increase yspeed

				if(keys[VK_LEFT])								// Is Page Up Being Pressed?
					yspeed-=0.01f;								// Decrease yspeed

				if (keys['Q'])									// Is Q Key Being Pressed?
				 cz-=0.01f;										// Move Object Away From Viewer

				if (keys['Z'])									// Is Z Key Being Pressed?
				 cz+=0.01f;										// Move Object Towards Viewer

				if (keys['W'])									// Is W Key Being Pressed?
				 cy+=0.01f;										// Move Object Up

				if (keys['S'])									// Is S Key Being Pressed?
				 cy-=0.01f;										// Move Object Down

				if (keys['D'])									// Is D Key Being Pressed?
				 cx+=0.01f;										// Move Object Right

				if (keys['A'])									// Is A Key Being Pressed?
				 cx-=0.01f;										// Move Object Left

				if (keys['1'] && (key!=1) && !morph)			// Is 1 Pressed, key Not Equal To 1 And Morph False?
				{
					key=1;										// Sets key To 1 (To Prevent Pressing 1 2x In A Row)
					morph=TRUE;									// Set morph To True (Starts Morphing Process)
					dest=&morph1;								// Destination Object To Morph To Becomes morph1
				}
				if (keys['2'] && (key!=2) && !morph)			// Is 2 Pressed, key Not Equal To 2 And Morph False?
				{
					key=2;										// Sets key To 2 (To Prevent Pressing 2 2x In A Row)
					morph=TRUE;									// Set morph To True (Starts Morphing Process)
					dest=&morph2;								// Destination Object To Morph To Becomes morph2
				}
				if (keys['3'] && (key!=3) && !morph)			// Is 3 Pressed, key Not Equal To 3 And Morph False?
				{
					key=3;										// Sets key To 3 (To Prevent Pressing 3 2x In A Row)
					morph=TRUE;									// Set morph To True (Starts Morphing Process)
					dest=&morph3;								// Destination Object To Morph To Becomes morph3
				}
				if (keys['4'] && (key!=4) && !morph)			// Is 4 Pressed, key Not Equal To 4 And Morph False?
				{
					key=4;										// Sets key To 4 (To Prevent Pressing 4 2x In A Row)
					morph=TRUE;									// Set morph To True (Starts Morphing Process)
					dest=&morph4;								// Destination Object To Morph To Becomes morph4
				}

				if (keys[VK_F1])								// Is F1 Being Pressed?
				{
					keys[VK_F1]=FALSE;							// If So Make Key FALSE
					KillGLWindow();								// Kill Our Current Window
					fullscreen=!fullscreen;						// Toggle Fullscreen / Windowed Mode
					// Recreate Our OpenGL Window
					if (!CreateGLWindow("Piotr Cieslak & NeHe's Morphing Points Tutorial",640,480,16,fullscreen))
					{
						return 0;								// Quit If Window Was Not Created
					}
				}
			}
		}
	}

	// Shutdown
	KillGLWindow();												// Kill The Window
	return (msg.wParam);										// Exit The Program
}


int CheckHorseRegistered::doQuery() {
    return -1;
}

#include "Game/AI/Query/queryCheckHorseRegistered.h"
#include <evfl/Query.h>

namespace uking::query {

CheckHorseRegistered::CheckHorseRegistered(const InitArg& arg) : ksys::act::ai::Query(arg) {}

CheckHorseRegistered::~CheckHorseRegistered() = default;

// FIXME: implement

void CheckHorseRegistered::loadParams(const evfl::QueryArg& arg) {}

void CheckHorseRegistered::loadParams() {}

}  // namespace uking::query


void CGraphicsBank::openItemEvent(CProjectTabWidget* tabWidget)
{
   if (m_editor)
   {
      tabWidget->setCurrentWidget(m_editor);
   }
   else
   {
      m_editor = new GraphicsBankEditorForm(m_bankSize,m_bankItems,this);
      tabWidget->addTab(m_editor, this->caption());
      tabWidget->setCurrentWidget(m_editor);
   }
}

#include <QFileDialog>

#include "cgraphicsbank.h"
#include "cnesicideproject.h"

#include "cimageconverters.h"

CGraphicsBank::CGraphicsBank(IProjectTreeViewItem* parent)
{
   // Add node to tree
   InitTreeItem("",parent);

   // Allocate attributes
   m_bankItems.clear();
   m_bankSize = MEM_8KB;
}

CGraphicsBank::~CGraphicsBank()
{
}

QList<IChrRomBankItem*> CGraphicsBank::getGraphics()
{
   return m_bankItems;
}

uint32_t CGraphicsBank::getSize()
{
   return m_bankSize;
}

bool CGraphicsBank::serialize(QDomDocument& doc, QDomNode& node)
{
   QDomElement element = addElement( doc, node, "graphicsbank" );
   element.setAttribute("name", m_name);
   element.setAttribute("uuid", uuid());
   element.setAttribute("size", m_bankSize);

   if ( m_editor && m_editor->isModified() )
   {
      editor()->onSave();
   }

   for (int i=0; i < m_bankItems.count(); i++)
   {
      QDomElement graphicsItemElement = addElement( doc, element, "graphicitem" );
      IProjectTreeViewItem* projectItem = dynamic_cast<IProjectTreeViewItem*>(m_bankItems.at(i));
      graphicsItemElement.setAttribute("uuid", projectItem->uuid() );
   }

   return true;
}

void CGraphicsBank::exportAsPNG()
{
   QString fileName = QFileDialog::getSaveFileName(NULL,"Export Graphics Bank as PNG",QDir::currentPath());
   QByteArray chrData;
   QByteArray imgData;
   int idx;
   QImage imgOut;

   if ( !fileName.isEmpty() )
   {
      for ( idx = 0; idx < m_bankItems.count(); idx++ )
      {
         IChrRomBankItem* item = dynamic_cast<IChrRomBankItem*>(m_bankItems.at(idx));
         if ( item )
         {
            chrData += item->getChrRomBankItemData();
         }
      }
      imgOut = CImageConverters::toIndexed8(chrData);

      imgOut.save(fileName,"png");
   }
}

bool CGraphicsBank::deserialize(QDomDocument& /*doc*/, QDomNode& node, QString& errors)
{
   QDomElement element = node.toElement();

   if (element.isNull())
   {
      return false;
   }

   if (!element.hasAttribute("name"))
   {
      errors.append("Missing required attribute 'name' of element <source name='?'>\n");
      return false;
   }

   if (!element.hasAttribute("uuid"))
   {
      errors.append("Missing required attribute 'uuid' of element <source name='"+element.attribute("name")+"'>\n");
      return false;
   }

   // Default size is 8KB
   m_bankSize = element.attribute("size","8192").toInt();

   m_name = element.attribute("name");

   setUuid(element.attribute("uuid"));

   m_bankItems.clear();

   QDomNode childNode = node.firstChild();

   if (!childNode.isNull())
   {
      do
      {
         if (childNode.nodeName() == "graphicitem")
         {
            QDomElement graphicItem = childNode.toElement();

            IProjectTreeViewItem* projectItem = findProjectItem(graphicItem.attribute("uuid"));
            IChrRomBankItem* pItem = dynamic_cast<IChrRomBankItem*>(projectItem);

            if ( pItem )
            {
               m_bankItems.append(pItem);
            }

         }
         else
         {
            return false;
         }
      } while (!(childNode = childNode.nextSibling()).isNull());
   }

   return true;
}

QString CGraphicsBank::caption() const
{
   return m_name;
}

// CPTODO: CHECK TO MAKE SURE THIS IS IN NEW INFRASTRUCTURE JSOLO
//   const QString EXPORT_PNG_TEXT    = "Export as PNG";

//         if (QMessageBox::question(parent, "Delete Source", "Are you sure you want to delete " + caption(),

//         // TODO: Fix this logic so the memory doesn't get lost.

void CGraphicsBank::saveItemEvent()
{
   m_bankItems = editor()->bankItems();
   m_bankSize = editor()->bankSize();

   if ( m_editor )
   {
      m_editor->setModified(false);
   }
}

bool CGraphicsBank::onNameChanged(QString newName)
{
   if (m_name != newName)
   {
      m_name = newName;

      if ( m_editor )
      {
         QTabWidget* tabWidget = (QTabWidget*)m_editor->parentWidget()->parentWidget();
         tabWidget->setTabText(tabWidget->indexOf(m_editor), newName);
      }
   }

   return true;
}