using System;
using AgkSharp;
using AGKCore;

namespace Shadows
{

    static class Program
    {
        /// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Core.CreateWin32Window("Shadows", 1024, 768, false);
            if (!Core.InitAGK())
                return;

            // Set some display properties
            Agk.SetVirtualResolution(1024, 768);
            Agk.SetClearColor(101, 120, 154);
            Agk.SetScissor(0, 0, 0, 0);
            Agk.SetGenerateMipmaps(1);
            Agk.SetCameraRange(1, 0.1f, 100);
            Agk.SetWindowAllowResize(1);

            Agk.UseNewDefaultFonts(1);
            Agk.SetPrintSize(20);
            Agk.SetPrintColor(255, 0, 0);

            int shadowMode = 3; // start with cascade shadow mapping which gives the best quality
            Agk.SetShadowMappingMode(shadowMode);
            Agk.SetShadowSmoothing(2); // random sampling
            Agk.SetShadowMapSize(1024, 1024);
            Agk.SetShadowRange(-1); // use the full camera range
            Agk.SetShadowBias(0.0012f); // offset shadows slightly to avoid shadow artifacts

            // show the sun, not necessary but improves the experience
            Agk.SetSkyBoxVisible(1);

            float dx = -0.3714f;
            float dy = -0.7428f;
            float dz = 0.5571f;
            Agk.SetSunDirection(dx, dy, dz);
            Agk.SetAmbientColor(30, 30, 30);

            // load a 3D object
            Agk.LoadImage(1, "media/Models/chapel_D.png");
            Agk.LoadObject(1, "media/Models/chapel.obj", 10);
            Agk.SetObjectImage(1, 1, 0);
            Agk.SetObjectTransparency(1, 0);
            Agk.SetObjectCastShadow(1, 1);

            // create a sphere object
            Agk.CreateObjectSphere(2, 1, 10, 10);
            Agk.SetObjectColor(2, 0, 255, 0, 128);
            Agk.SetObjectTransparency(2, 0);
            Agk.SetObjectCastShadow(2, 1);

            // make ground
            Agk.CreateObjectPlane(3, 100, 100);
            Agk.SetObjectRotation(3, 90, 0, 0);
            Agk.SetObjectPosition(3, 0, 0, 0);
            Agk.SetObjectCastShadow(3, 1); // this isn't necessary but improves the small low quality shadows
            Agk.LoadImage(2, "media/grass.png");
            Agk.SetImageWrapU(2, 1);
            Agk.SetImageWrapV(2, 1);
            Agk.SetObjectImage(3, 2, 0);
            Agk.SetObjectUVScale(3, 0, 10, 10);

            // make wall
            Agk.CreateObjectPlane(4, 20, 20);
            Agk.SetObjectPosition(4, 0, 9, -20);
            Agk.SetObjectCastShadow(4, 1);

            // optional bone animated character
            int useCharacter = 1;
            if (useCharacter == 1)
            {
                Agk.LoadObjectWithChildren(10, "media/Original_Soldier.X");
                Agk.SetObjectVisible(Agk.GetObjectChildID(10, 1), 0);
                Agk.SetObjectScalePermanent(10, 0.05f, 0.05f, 0.05f);
                Agk.SetObjectPosition(10, 6, 0, -6);
                Agk.SetObjectRotation(10, 0, 270, 0);
                Agk.LoadImage(3, "media/Soldier_body_1_D.png");
                Agk.SetObjectImage(10, 3, 0);
                Agk.SetObjectCastShadow(10, 1);

                Agk.LoadImage(4, "media/gun_D.png");
                Agk.SetObjectMeshImage(Agk.GetObjectChildID(10, 2), 1, 4, 0);
                Agk.SetObjectScalePermanent(Agk.GetObjectChildID(10, 2), 0.05f, 0.05f, 0.05f);
                Agk.SetObjectCastShadow((int)Agk.GetObjectChildID(10, 2), 1);

                Agk.SetObjectAnimationSpeed(10, 20);
                Agk.PlayObjectAnimation(10, "", 1000, 1282, 1, 0.5f);
            }

            // point camera towards the object
            Agk.SetCameraPosition(1, 15, 10, 0);
            Agk.SetCameraLookAt(1, 0, 5, 0, 0);

            // controls setup
            Agk.AddVirtualButton(1, 970, 715, 100);
            Agk.AddVirtualButton(2, 970, 610, 100);
            Agk.SetVirtualButtonText(2, "Forward");
            Agk.SetVirtualButtonText(1, "Backward");
            float startx = Agk.GetPointerX();
            float starty = Agk.GetPointerY();
            float time = 0;

            // light controls
            Agk.AddVirtualButton(3, 54, 715, 100);
            Agk.AddVirtualButton(4, 54, 610, 100);
            Agk.AddVirtualButton(5, 178, 715, 100);
            Agk.SetVirtualButtonText(4, "Light +");
            Agk.SetVirtualButtonText(3, "Light -");
            Agk.SetVirtualButtonText(5, "Mode");

            float angx = 0.0f, angy = 0.0f;
            while (Core.LoopAGK())
            {
                // move the camera with keys
                if (Agk.GetRawKeyState(87) != 0)
                    Agk.MoveCameraLocalZ(1, 0.2f);
                if (Agk.GetRawKeyState(83) != 0)
                    Agk.MoveCameraLocalZ(1, -0.2f);


                if (Agk.GetRawKeyState(65) != 0)
                    Agk.MoveCameraLocalX(1, -0.2f);
                if (Agk.GetRawKeyState(68) != 0)
                    Agk.MoveCameraLocalX(1, 0.2f);

                if (Agk.GetRawKeyState(81) != 0)
                    Agk.MoveCameraLocalY(1, -0.2f);
                if (Agk.GetRawKeyState(69) != 0)
                    Agk.MoveCameraLocalY(1, 0.2f);

                // move the cmaera with virtual buttons
                if (Agk.GetVirtualButtonState(1) == 1)
                    Agk.MoveCameraLocalZ(1, -0.2f);
                if (Agk.GetVirtualButtonState(2) == 1)
                    Agk.MoveCameraLocalZ(1, 0.2f);

                // rotate the camera
                if (Agk.GetPointerPressed() != 0)
                {
                    startx = Agk.GetPointerX();
                    starty = Agk.GetPointerY();
                    angx = Agk.GetCameraAngleX(1);
                    angy = Agk.GetCameraAngleY(1);
                }

                if (Agk.GetPointerState() == 1)
                {
                    float fDiffX = (Agk.GetPointerX() - startx) / 4.0f;
                    float fDiffY = (Agk.GetPointerY() - starty) / 4.0f;


                    float newX = angx + fDiffY;
                    if (newX > 89.0f)
                        newX = 89.0f;
                    if (newX < -89.0f)
                        newX = -89.0f;
                    Agk.SetCameraRotation(1, newX, angy + fDiffX, 0);
                }

                // change shadow mode
                if (Agk.GetRawKeyPressed(32) == 1 || Agk.GetVirtualButtonPressed(5) == 1)
                {
                    shadowMode++;

                    if (shadowMode > 3)
                        shadowMode = 0;

                    Agk.SetShadowMappingMode(shadowMode);
                }

                // move the green sphere
                time = time + Agk.GetFrameTime();
                float x = Agk.Sin(time * 70.0f) * 7.0f;
                float z = Agk.Cos(time * 70.0f) * 7.0f;
                Agk.SetObjectPosition(2, x, 2, z);

                // move directional light
                if (Agk.GetRawKeyState(90) != 0 || Agk.GetVirtualButtonState(3) != 0)
                    dx += 0.02f;
                if (Agk.GetRawKeyState(88) != 0 || Agk.GetVirtualButtonState(4) != 0)
                    dx -= 0.02f;
                Agk.SetSunDirection(dx, dy, dz);

                // display some useful information
                Agk.Print("Click and drag to rotate the camera, W, A, S, D, Q, E to move");
                Agk.Print("Press X or Z to move the sun, press Space to change shadow mapping mode");

                Agk.Print("FPS: " + Agk.ScreenFPS().ToString("N2"));

                Agk.PrintC("Shadow Mode: ");

                switch (shadowMode)
                {
                    case 0:
                        Agk.Print("None");
                        break;
                    case 1:
                        Agk.Print("Uniform shadow mapping");
                        break;
                    case 2:
                        Agk.Print("Light Space Perspective shadow mapping");
                        break;
                    case 3:
                        Agk.Print("Cascade shadow mapping");
                        break;
                }

                Agk.Sync();
            }

            Core.CleanUp();
        }
    }
}
