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

namespace Mouse_and_Joystick
{
    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 keyInfo = Console.ReadKey(true);

            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
            SEventReceiver receiver = new SEventReceiver();

            IrrlichtDevice device = IrrlichtNet.createDevice(driverType,
                    new dimension2di(640, 480), 16, false, false, false, receiver);

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


            arraySJoystickInfo joystickInfo = new arraySJoystickInfo();

            if (device.activateJoysticks(joystickInfo))
            {
                Console.WriteLine("Joystick support is enabled and " + joystickInfo.size() + " joystick(s) are present.");

                for (uint joystick = 0; joystick < joystickInfo.size(); ++joystick)
                {
                    Console.WriteLine("Joystick " + joystick + ":");
                    Console.WriteLine("\tName: '" + joystickInfo.getItem(joystick).Name + "'");
                    Console.WriteLine("\tAxes: " + joystickInfo.getItem(joystick).Axes);
                    Console.WriteLine("\tButtons: " + joystickInfo.getItem(joystick).Buttons);

                    Console.WriteLine("\tHat is: ");

                    switch (joystickInfo.getItem(joystick).PovHat)
                    {
                        case 0:
                            Console.WriteLine("present");
                            break;

                        case 1:
                            Console.WriteLine("absent");
                            break;

                        case 2:
                        default:
                            Console.WriteLine("unknown");
                            break;
                    }
                }
            }
            else
            {
                Console.WriteLine("Joystick support is not enabled.");
            }

            string tmp = "Irrlicht Joystick Example (";
            tmp += joystickInfo.size();
            tmp += " joysticks)";
            device.setWindowCaption(tmp);

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

            /*
            We'll create an arrow mesh and move it around either with the joystick axis/hat,
            or make it follow the mouse pointer. */
            ISceneNode node = smgr.addMeshSceneNode(smgr.addArrowMesh("Arrow", new SColor(255, 255, 0, 0), new SColor(255, 0, 255, 0)));
            node.setMaterialFlag(E_MATERIAL_FLAG.EMF_LIGHTING, false);

            ICameraSceneNode camera = smgr.addCameraSceneNode();
            camera.setPosition(new vector3df(0, 0, -10));

            // As in example 04, we'll use framerate independent movement.
            uint then = device.getTimer().getTime();
            float MOVEMENT_SPEED = 5.0f;

            while (device.run())
            {
                // Work out a frame delta time.
                uint now = device.getTimer().getTime();
                float frameDeltaTime = (float)(now - then) / 1000.0f; // Time in seconds
                then = now;

                bool movedWithJoystick = false;
                vector3df nodePosition = node.getPosition();

                if (joystickInfo.size() > 0)
                {
                    float moveHorizontal = 0.0f; // Range is -1.f for full left to +1.f for full right
                    float moveVertical = 0.0f; // -1.f for full down to +1.f for full up.

                    SJoystickEvent joystickData = receiver.GetJoystickState();

                    // We receive the full analog range of the axes, and so have to implement our
                    // own dead zone.  This is an empirical value, since some joysticks have more
                    // jitter or creep around the center point than others.  We'll use 5% of the
                    // range as the dead zone, but generally you would want to give the user the
                    // option to change this.
                    float DEAD_ZONE = 0.05f;

                    moveHorizontal =
                        (float)joystickData.GetAxis(SJoystickEvent.AXIS_X) / 32767.0f;
                    if (Math.Abs(moveHorizontal) < DEAD_ZONE)
                        moveHorizontal = 0.0f;

                    moveVertical =
                        (float)joystickData.GetAxis(SJoystickEvent.AXIS_Y) / -32767.0f;
                    if (Math.Abs(moveVertical) < DEAD_ZONE)
                        moveVertical = 0.0f;

                    // POV hat info is only currently supported on Windows, but the value is
                    // guaranteed to be 65535 if it's not supported, so we can check its range.
                    ushort povDegrees = (ushort)(joystickData.POV / 100);
                    if (povDegrees < 360)
                    {
                        if (povDegrees > 0 && povDegrees < 180)
                            moveHorizontal = 1.0f;
                        else if (povDegrees > 180)
                            moveHorizontal = -1.0f;

                        if (povDegrees > 90 && povDegrees < 270)
                            moveVertical = -1.0f;
                        else if (povDegrees > 270 || povDegrees < 90)
                            moveVertical = +1.0f;
                    }

                    if (!IrrlichtNet.equals(moveHorizontal, 0.0f) || !IrrlichtNet.equals(moveVertical, 0.0f))
                    {
                        nodePosition.X += MOVEMENT_SPEED * frameDeltaTime * moveHorizontal;
                        nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime * moveVertical;
                        movedWithJoystick = true;
                    }
                }

                // If the arrow node isn't being moved with the joystick, then have it follow the mouse cursor.
                if (!movedWithJoystick)
                {
                    // Create a ray through the mouse cursor.
                    line3df ray = smgr.getSceneCollisionManager().getRayFromScreenCoordinates(
                        receiver.GetMouseState().Position, camera);

                    // And intersect the ray with a plane around the node facing towards the camera.
                    plane3df plane = new plane3df(nodePosition, new vector3df(0, 0, -1));
                    vector3df mousePosition = new vector3df();
                    if (plane.getIntersectionWithLine(ray.start, ray.getVector(), mousePosition))
                    {
                        // We now have a mouse position in 3d space; move towards it.
                        vector3df toMousePosition = mousePosition - nodePosition;
                        float availableMovement = MOVEMENT_SPEED * frameDeltaTime;

                        if (toMousePosition.getLength() <= availableMovement)
                            nodePosition = mousePosition; // Jump to the final position
                        else
                            nodePosition += toMousePosition.normalize() * availableMovement; // Move towards it
                    }
                }

                node.setPosition(nodePosition);

                // Turn lighting on and off depending on whether the left mouse button is down.
                node.setMaterialFlag(E_MATERIAL_FLAG.EMF_LIGHTING, receiver.GetMouseState().LeftButtonDown);

                driver.beginScene(true, true, new SColor(255, 113, 113, 133));
                smgr.drawAll(); // draw the 3d scene
                driver.endScene();
            }

            /*
            In the end, delete the Irrlicht device.
            */
            //device.drop();

            return;
        }
    }

    /*
Just as we did in example 04.Movement, we'll store the latest state of the
mouse and the first joystick, updating them as we receive events.
*/
    class SEventReceiver : IEventReceiver
    {

        // We'll create a struct to record info on the mouse state
        public class SMouseState
        {
            public position2di Position = new position2di();
            public bool LeftButtonDown = false;
        }

        private SMouseState MouseState = new SMouseState();

        // This is the one method that we have to implement
        public override bool OnEvent(SEvent ev)
        {
            // Remember the mouse state
            if (ev.EventType == EEVENT_TYPE.EET_MOUSE_INPUT_EVENT)
            {
                switch (ev.MouseInput.Event)
                {
                    case EMOUSE_INPUT_EVENT.EMIE_LMOUSE_PRESSED_DOWN:
                        MouseState.LeftButtonDown = true;
                        break;

                    case EMOUSE_INPUT_EVENT.EMIE_LMOUSE_LEFT_UP:
                        MouseState.LeftButtonDown = false;
                        break;

                    case EMOUSE_INPUT_EVENT.EMIE_MOUSE_MOVED:
                        MouseState.Position.X = ev.MouseInput.X;
                        MouseState.Position.Y = ev.MouseInput.Y;
                        break;

                    default:
                        // We won't use the wheel
                        break;
                }
            }

            // The state of each connected joystick is sent to us
            // once every run() of the Irrlicht device.  Store the
            // state of the first joystick, ignoring other joysticks.
            // This is currently only supported on Windows and Linux.
            if (ev.EventType == EEVENT_TYPE.EET_JOYSTICK_INPUT_EVENT
                && ev.JoystickEvent.Joystick == 0)
            {
                JoystickState = ev.JoystickEvent;
            }

            return false;
        }

        internal SJoystickEvent GetJoystickState()
        {
            return JoystickState;
        }

        internal SMouseState GetMouseState()
        {
            return MouseState;
        }


        internal SEventReceiver()
        {
        }

        private SJoystickEvent JoystickState;
    }


}
