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

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

            E_DRIVER_TYPE driverType = E_DRIVER_TYPE.EDT_DIRECT3D9;

            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");

            var keyInput = Console.ReadKey(true);

            switch (keyInput.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));

            if (device == null)
                return; // could not create selected driver.


            /*
            First, we add standard stuff to the scene: A nice irrlicht engine
            logo, a small help text, a user controlled camera, and we disable
            the mouse cursor.
            */

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

            driver.setTextureCreationFlag(E_TEXTURE_CREATION_FLAG.ETCF_ALWAYS_32_BIT, true);

            // add irrlicht logo
            env.addImage(driver.getTexture("../../media/irrlichtlogo2.png"),
                         new position2di(10, 10));

            //set other font
            env.getSkin().setFont(env.getFont("../../media/fontlucida.png"));

            // add some help text
            env.addStaticText(
                "Press 'W' to change wireframe mode\nPress 'D' to toggle detail map\nPress 'S' to toggle skybox/skydome",
                new recti(10, 421, 250, 475), true, true, null, -1, true);

            // add camera
            ICameraSceneNode camera =
                smgr.addCameraSceneNodeFPS(null, 100.0f, 1.2f);

            camera.setPosition(new vector3df(2700*2, 255*2, 2600*2));
            camera.setTarget(new vector3df(2397*2, 343*2, 2700*2));
            camera.setFarValue(42000.0f);

            // disable mouse cursor
            device.getCursorControl().setVisible(false);

            /*
            Here comes the terrain renderer scene node: We add it just like any
            other scene node to the scene using
            ISceneManager::addTerrainSceneNode(). The only parameter we use is a
            file name to the heightmap we use. A heightmap is simply a gray scale
            texture. The terrain renderer loads it and creates the 3D terrain from
            it.

            To make the terrain look more big, we change the scale factor of
            it to (40, 4.4, 40). Because we don't have any dynamic lights in the
            scene, we switch off the lighting, and we set the file
            terrain-texture.jpg as texture for the terrain and detailmap3.jpg as
            second texture, called detail map. At last, we set the scale values for
            the texture: The first texture will be repeated only one time over the
            whole terrain, and the second one (detail map) 20 times.
            */

            // add terrain scene node
            ITerrainSceneNode terrain = smgr.addTerrainSceneNode(
                "../../media/terrain-heightmap.bmp",
                null, // parent node
                -1, // node id
                new vector3df(0.0f, 0.0f, 0.0f), // position
                new vector3df(0.0f, 0.0f, 0.0f), // rotation
                new vector3df(40.0f, 4.4f, 40.0f), // scale
                new SColor(255, 255, 255, 255), // vertexColor
                5, // maxLOD
                E_TERRAIN_PATCH_SIZE.ETPS_17, // patchSize
                4 // smoothFactor
                );

            terrain.setMaterialFlag(E_MATERIAL_FLAG.EMF_LIGHTING, false);

            terrain.setMaterialTexture(0,
                                       driver.getTexture("../../media/terrain-texture.jpg"));
            terrain.setMaterialTexture(1,
                                       driver.getTexture("../../media/detailmap3.jpg"));

            terrain.setMaterialType(E_MATERIAL_TYPE.EMT_DETAIL_MAP);

            terrain.scaleTexture(1.0f, 20.0f);
            //terrain.setDebugDataVisible ( true );

            /*
            To be able to do collision with the terrain, we create a triangle selector.
            If you want to know what triangle selectors do, just take a look into the
            collision tutorial. The terrain triangle selector works together with the
            terrain. To demonstrate this, we create a collision response animator
            and attach it to the camera, so that the camera will not be able to fly
            through the terrain.
            */

            // create triangle selector for the terrain	
            ITriangleSelector selector
                = smgr.createTerrainTriangleSelector(terrain, 0);
            terrain.setTriangleSelector(selector);

            // create collision response animator and attach it to the camera
            ISceneNodeAnimator anim = smgr.createCollisionResponseAnimator(
                selector, camera, new vector3df(60, 100, 60),
                new vector3df(0, 0, 0),
                new vector3df(0, 50, 0));
            //selector.drop();
            camera.addAnimator(anim);
            //anim.drop();

            /* If you need access to the terrain data you can also do this directly via the following code fragment.
            */
            CDynamicMeshBuffer buffer = new CDynamicMeshBuffer(E_VERTEX_TYPE.EVT_2TCOORDS,
                                                               E_INDEX_TYPE.EIT_16BIT);
            terrain.getMeshBufferForLOD(buffer, 0);
            S3DVertex2TCoords data = buffer.getVertexBuffer().getDataS3DVertex2TCoords();
            // Work on data or get the IndexBuffer with a similar call.
            //buffer.drop(); // When done drop the buffer again.

            /*
            To make the user be able to switch between normal and wireframe mode,
            we create an instance of the event reciever from above and let Irrlicht
            know about it. In addition, we add the skybox which we already used in
            lots of Irrlicht examples and a skydome, which is shown mutually
            exclusive with the skybox by pressing 'S'.
            */

            // create skybox and skydome
            driver.setTextureCreationFlag(E_TEXTURE_CREATION_FLAG.ETCF_CREATE_MIP_MAPS, false);

            ISceneNode skybox = smgr.addSkyBoxSceneNode(
                driver.getTexture("../../media/irrlicht2_up.jpg"),
                driver.getTexture("../../media/irrlicht2_dn.jpg"),
                driver.getTexture("../../media/irrlicht2_lf.jpg"),
                driver.getTexture("../../media/irrlicht2_rt.jpg"),
                driver.getTexture("../../media/irrlicht2_ft.jpg"),
                driver.getTexture("../../media/irrlicht2_bk.jpg"));
            ISceneNode skydome = smgr.addSkyDomeSceneNode(driver.getTexture("../../media/skydome.jpg"), 16, 8, 0.95f,
                                                          2.0f);

            driver.setTextureCreationFlag(E_TEXTURE_CREATION_FLAG.ETCF_CREATE_MIP_MAPS, true);

            // create event receiver
            SEventReceiver receiver = new SEventReceiver(terrain, skybox, skydome);
            device.setEventReceiver(receiver);

            /*
            That's it, draw everything.
            */

            int lastFPS = -1;

            while (device.run())
                if (device.isWindowActive())
                {
                    driver.beginScene(true, true, new SColor());

                    smgr.drawAll();
                    env.drawAll();

                    driver.endScene();

                    // display frames per second in window title
                    int fps = driver.getFPS();
                    if (lastFPS != fps)
                    {
                        string str = "Terrain Renderer - Irrlicht Engine [";
                        str += driver.getName();
                        str += "] FPS:";
                        str += fps;
                        // Also print terrain height of current camera position
                        // We can use camera position because terrain is located at coordinate origin
                        str += " Height: ";
                        str += terrain.getHeight(camera.getAbsolutePosition().X,
                                                 camera.getAbsolutePosition().Z);

                        device.setWindowCaption(str);
                        lastFPS = fps;
                    }
                }

            //device.drop();

            return;
        }
    }

    class SEventReceiver : IEventReceiver
    {
        public SEventReceiver(ISceneNode terrain, ISceneNode skybox, ISceneNode skydome)
        {
            Terrain = terrain;
            Skybox = skybox;
            Skydome = skydome;
            showBox = true;

            Skybox.setVisible(true);
            Skydome.setVisible(false);
        }

        public override bool OnEvent(SEvent ev)
        {
            // check if user presses the key 'W' or 'D'
            if (ev.EventType == EEVENT_TYPE.EET_KEY_INPUT_EVENT && !ev.KeyInput.PressedDown)
            {
                switch (ev.KeyInput.Key)
                {
                    case EKEY_CODE.KEY_KEY_W: // switch wire frame mode
                        Terrain.setMaterialFlag(E_MATERIAL_FLAG.EMF_WIREFRAME,
                                !Terrain.getMaterial(0).Wireframe);
                        Terrain.setMaterialFlag(E_MATERIAL_FLAG.EMF_POINTCLOUD, false);
                        return true;
                    case EKEY_CODE.KEY_KEY_P: // switch wire frame mode
                        Terrain.setMaterialFlag(E_MATERIAL_FLAG.EMF_POINTCLOUD,
                                !Terrain.getMaterial(0).PointCloud);
                        Terrain.setMaterialFlag(E_MATERIAL_FLAG.EMF_WIREFRAME, false);
                        return true;
                    case EKEY_CODE.KEY_KEY_D: // toggle detail map
                        Terrain.setMaterialType(
                            Terrain.getMaterial(0).MaterialType == E_MATERIAL_TYPE.EMT_SOLID ?
                            E_MATERIAL_TYPE.EMT_DETAIL_MAP : E_MATERIAL_TYPE.EMT_SOLID);
                        return true;
                    case EKEY_CODE.KEY_KEY_S: // toggle skies
                        showBox = !showBox;
                        Skybox.setVisible(showBox);
                        Skydome.setVisible(!showBox);
                        return true;
                    default:
                        break;
                }
            }

            return false;
        }

        private ISceneNode Terrain;
        private ISceneNode Skybox;
        private ISceneNode Skydome;
        private bool showBox;
    }
}
