using System;
using System.Collections.Generic;
using System.Text;
using IrrlichtNetSwig;

namespace CustomSceneNode
{
    class Program
    {
        static void Main(string[] args)
        {
            // let user select driver type

	        E_DRIVER_TYPE driverType;

	        System.Console.WriteLine(@"Please select the driver you want for this example:
(a) Direct3D 9.0c
(b) Direct3D 8.1
(c) OpenGL 1.5
(d) Software Renderer
(e) Burning's Software Renderer
(f) NullDevice
(otherKey) exit");

            ConsoleKeyInfo keyInfo = System.Console.ReadKey();
	        
	        switch(keyInfo.KeyChar)
	        {
		        case 'a': driverType = E_DRIVER_TYPE.EDT_DIRECT3D9;break;
		        case 'b': driverType = E_DRIVER_TYPE.EDT_DIRECT3D8;break;
		        case 'c': driverType = E_DRIVER_TYPE.EDT_OPENGL;   break;
		        case 'd': driverType = E_DRIVER_TYPE.EDT_SOFTWARE; break;
		        case 'e': driverType = E_DRIVER_TYPE.EDT_BURNINGSVIDEO;break;
		        case 'f': driverType = E_DRIVER_TYPE.EDT_NULL;     break;
		        default: return;
	        }

	        // create device

	        IrrlichtDevice device = IrrlichtNet.createDevice(driverType,
			        new dimension2di(640, 480), 16, false);
        		
	        if (device == null)
		        return; // could not create selected driver.

	        // create engine and camera

	        device.setWindowCaption("Custom Scene Node - Irrlicht Engine Demo");

	        IVideoDriver driver = device.getVideoDriver();
	        ISceneManager smgr = device.getSceneManager();

	        smgr.addCameraSceneNode(null, new vector3df(0,-40,0), new vector3df(0,0,0));

	        /*
	        Create our scene node. I don't check the result of calling new, as it
	        should throw an exception rather than returning 0 on failure. Because
	        the new node will create itself with a reference count of 1, and then
	        will have another reference added by its parent scene node when it is
	        added to the scene, I need to drop my reference to it. Best practice is
	        to drop it only *after* I have finished using it, regardless of what
	        the reference count of the object is after creation.
	        */
	        CSampleSceneNode myNode =
		        new CSampleSceneNode(smgr.getRootSceneNode(), smgr, 666);

	        /*
	        To animate something in this boring scene consisting only of one
	        tetraeder, and to show that you now can use your scene node like any
	        other scene node in the engine, we add an animator to the scene node,
	        which rotates the node a little bit.
	        irr::scene::ISceneManager::createRotationAnimator() could return 0, so
	        should be checked.
	        */
	        ISceneNodeAnimator anim =
		        smgr.createRotationAnimator(new vector3df(0.8f, 0, 0.8f));

	        if(anim!=null)
	        {
		        myNode.addAnimator(anim);
        		
		        /*
		        I'm done referring to anim, so must
		        irr::IReferenceCounted::drop() this reference now because it
		        was produced by a createFoo() function. As I shouldn't refer to
		        it again, ensure that I can't by setting to 0.
		        */
		        //anim->drop();
		        anim = null;
	        }

	        /*
	        I'm done with my CSampleSceneNode object, and so must drop my reference.
	        This won't delete the object, yet, because it is still attached to the
	        scene graph, which prevents the deletion until the graph is deleted or the
	        custom scene node is removed from it.
	        */
	        //myNode->drop();
	        myNode = null; // As I shouldn't refer to it again, ensure that I can't

	        /*
	        Now draw everything and finish.
	        */
	        uint frames=0;
	        while(device.run())
	        {
		        driver.beginScene(true, true, new SColor(0,100,100,100));

		        smgr.drawAll();

		        driver.endScene();
		        if (++frames==100)
		        {
			        string str = "Irrlicht Engine [";
			        str += driver.getName();
			        str += "] FPS: ";
		            str += driver.getFPS().ToString();

			        device.setWindowCaption(str);
			        frames=0;
		        }
	        }

	        //device->drop();
        	
	        return ;
        }
    }

    class CSampleSceneNode : ISceneNode_ForInheritance
    {

        /*
        First, we declare some member variables:
        The bounding box, 4 vertices, and the material of the tetraeder.
        */
        private aabbox3df Box = new aabbox3df();
        private SMaterial Material = new SMaterial();
        private ISceneManager sceneManager;
        S3DVertex[] s3DVertexArray = new S3DVertex[4];
        /*
        The parameters of the constructor specify the parent of the scene node,
        a pointer to the scene manager, and an id of the scene node.
        In the constructor we call the parent class' constructor,
        set some properties of the material, and
        create the 4 vertices of the tetraeder we will draw later.
        */

        public CSampleSceneNode(ISceneNode parent, ISceneManager mgr, int id)
            : base(parent, mgr, id)
        {
            sceneManager = mgr;
            Material.Wireframe = false;
            Material.Lighting = false;

            s3DVertexArray[0] = new S3DVertex(0, 0, 10, 1, 1, 0,
                                        new SColor(255, 0, 255, 255), 0, 1);
            s3DVertexArray[1] = new S3DVertex(10, 0, -10, 1, 0, 0,
                                        new SColor(255, 255, 0, 255), 1, 1);
            s3DVertexArray[2] = new S3DVertex(0, 20, 0, 0, 1, 1,
                                        new SColor(255, 255, 255, 0), 1, 0);
            s3DVertexArray[3] = new S3DVertex(-10, 0, -10, 0, 0, 1,
                                        new SColor(255, 0, 255, 0), 0, 0);

            /*
            The Irrlicht Engine needs to know the bounding box of a scene node.
            It will use it for automatic culling and other things. Hence, we
            need to create a bounding box from the 4 vertices we use.
            If you do not want the engine to use the box for automatic culling,
            and/or don't want to create the box, you could also call
            irr::scene::ISceneNode::setAutomaticCulling() with irr::scene::EAC_OFF.
            */
            Box.reset(s3DVertexArray[0].Pos);
            for (int i = 0; i < 4; i++)
            {
                Box.addInternalPoint(s3DVertexArray[i].Pos);
            }
        }

        /*
	    Before it is drawn, the irr::scene::ISceneNode::OnRegisterSceneNode()
	    method of every scene node in the scene is called by the scene manager.
	    If the scene node wishes to draw itself, it may register itself in the
	    scene manager to be drawn. This is necessary to tell the scene manager
	    when it should call irr::scene::ISceneNode::render(). For
	    example, normal scene nodes render their content one after another,
	    while stencil buffer shadows would like to be drawn after all other
	    scene nodes. And camera or light scene nodes need to be rendered before
	    all other scene nodes (if at all). So here we simply register the
	    scene node to render normally. If we would like to let it be rendered
	    like cameras or light, we would have to call
	    SceneManager->registerNodeForRendering(this, SNRT_LIGHT_AND_CAMERA);
	    After this, we call the actual
	    irr::scene::ISceneNode::OnRegisterSceneNode() method of the base class,
	    which simply lets also all the child scene nodes of this node register
	    themselves.
	    */
        public override void OnRegisterSceneNode()
        {
            if (isVisible())
                sceneManager.registerNodeForRendering(this);

            base.OnRegisterSceneNode();
        }

        /*
        In the render() method most of the interesting stuff happens: The
        Scene node renders itself. We override this method and draw the
        tetraeder.
        */
        public override void render()
        {
            ushort[] indices = { 0, 2, 3, 2, 1, 3, 1, 0, 3, 2, 0, 1 };
            IVideoDriver driver = sceneManager.getVideoDriver();

            driver.setMaterial(Material);
            driver.setTransform(E_TRANSFORMATION_STATE.ETS_WORLD, getAbsoluteTransformation());
            driver.drawIndexedTriangleList(s3DVertexArray, 4, indices, 4);
        }

        /*
        And finally we create three small additional methods.
        irr::scene::ISceneNode::getBoundingBox() returns the bounding box of
        this scene node, irr::scene::ISceneNode::getMaterialCount() returns the
        amount of materials in this scene node (our tetraeder only has one
        material), and irr::scene::ISceneNode::getMaterial() returns the
        material at an index. Because we have only one material here, we can
        return the only one material, assuming that no one ever calls
        getMaterial() with an index greater than 0.
        */
        public override aabbox3df getBoundingBox()
        {
            return Box;
        }

        public override uint getMaterialCount()
        {
            return 1;
        }

        public override SMaterial getMaterial(uint i)
        {
            return Material;
        }
    }
}
