using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.ComponentModel;
using System.Net;
using System.IO;
using System.Globalization;

namespace Hover
{
    // Coding straight for a long time, large parts of this code is very sloppy hackish. please don't judge, wanted to get a lot in the game.
    [Flags]
    enum artiHelper
    { 
        none = 0, 
        one = 1, 
        two = 2, 
        three = 4, 
        four = 8, 
        five = 16, 
        six = 32, 
        seven = 64, 
        eight = 128,
        nine = 256, 
        ten = 512,
        eleven = 1024,
        twelse = 2048, 
        thirteen = 4096, 
    }

    class Engine : Microsoft.Xna.Framework.Game
    {
        public static Engine S;

        public const int TileSize = 32;

        public const bool PixelClampPositions = false;

        public static GraphicsDeviceManager graphics;
        public static SpriteBatch sb;

        public static Point2 GameRes = new Point2(512, 260);
        public static Vector2 Res { get { return new Vector2(graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height); } }
        public static Vector2 Scale { get { return Res / GameRes; } }
        public static Vector2 CamPos;
        public static Random Rnd = new Random();
        public static int Score;
        public static GameTime gt;

        #region Data

        public static Texture2D Pixel;
        public static Texture2D Spritesheet, Tile1, Logo, bars;
        public static SpriteFont font1, font2;
        SoundEffectInstance music;

        #endregion

        bool mute;
        public float sfxVolume = 0.2f;
        List<Sprite> actors = new List<Sprite>();
        Level level;
        Vector2 camTarg;
        BackgroundWorker bw;
        string highscore = "", highscore2 = "", highscoreper = "";
        string[] hsStats = new string[7];
        float camInertia;
        float camInertiaBuildup;
        const float camInertiaTarget = 0.07f;
        const float camInertiaBuildupStart = 0.00001f;
        bool camStart = false;
        bool camDown = false;
        int lastScoreRow;
        public static DateTime gameStartTime;
        public static TimeSpan lastGameLength;
        bool showHelp = false;
        int showStartup = 350;
        BloomComponent bloom;
        RenderTarget2D target;
        public bool friend = true;
        public Vector2 friendPos = new Vector2(340, 167);

        #region Artifacts

        public static Rectangle[] artifactRects = new Rectangle[]
        {
            new Rectangle(80 + 0 * 16, 192, 16, 16),
            new Rectangle(80 + 1 * 16, 192, 16, 16),
            new Rectangle(80 + 2 * 16, 192, 16, 16),
            new Rectangle(80 + 3 * 16, 192, 16, 16),
            new Rectangle(80 + 4 * 16, 192, 16, 16),
            new Rectangle(80 + 0 * 16, 192 + 16, 16, 16),
            new Rectangle(80 + 1 * 16, 192 + 16, 16, 16),
            new Rectangle(80 + 2 * 16, 192 + 16, 16, 16),
            new Rectangle(80 + 3 * 16, 192 + 16, 16, 16),
            new Rectangle(80 + 4 * 16, 192 + 16, 16, 16),
            new Rectangle(80 + 5 * 16, 192, 16, 16), // cake
            new Rectangle(80 + 5 * 16, 192 + 16, 16, 16), // heart
            new Rectangle(80 + 6 * 16, 192 + 16, 16, 16), // heart
        };
        public static string[] artifactNames = new string[]
        {
            "Alien Skeleton",
            "Neil Armstrongs Flag",
            "UFO",
            "Alien Head",
            "Lunar Lander",
            "Holy Grail",
            "Achilles Heel",
            "Schrodingers Box",
            "Scepter",
            "Top Secret Gravity Weapon",
            "The Cake",
            "Heartless Bastard",
            "Jennys Loveletter",
        };
        public static List<string>[] artifactComments = new List<string>[]
        {
            new List<string>() { "This is proof of alien life!", "I can't believe nobody has found this before", "Looks like a monkey... wait.." },
            new List<string>() { "The original stale flag, and here I thought it was a hoax", "To think they actually did go to the moon", "It's still hard", "Can't.. bend..." },
            new List<string>() { "I wonder what technology lies inside", "I guess aliens are small", "I wonder if this is remote control" },
            new List<string>() { "It's still warm!", "Who said aliens are green?", "Where did the body go?" },
            new List<string>() { "I've always wanted one of these", "The battery is dead", "I'm taking this home", },
            new List<string>() { "Ah so this is where it was! No wonder nobody ever found it before", "Now to find some water", "Lets see if this works.. now where are some spikes", },
            new List<string>() { "How did this even get here?", "A heel? Seriously how could this save the world?" },
            new List<string>() { "I wonder whats inside", "I always wanted a cat, or did I?", "Do you think it's still alive?", },
            new List<string>() { "Seems to lead to a secret place, dropped by Anubis. wait what?", "Controls life and death", "Need to keep the robot locked to bring the plasma to city of gold", },
            new List<string>() { "This secret weapon seems out of this world! It may very well be humanities last hope!", "I should really try the PFG game", "Looks a lot like a portal gun", },
            new List<string>() { "It wasn't a lie after all", "I don't even like whipped cream", "Did you think it was a lie?", },
            new List<string>() { "He got what was coming to him", "Shut up", "I know the rest..", "Rest in pieces", "Die already" },
            new List<string>() { "I promise I won't read it", "I didn't know we had a pen onboard", "Who is Jenny?", "It's not for me?" },
        };
        public static bool[] artifactsFound = new bool[artifactRects.Length];
        public static int[] artifactDepths = new int[]
        {
            15, 25, 40, 65, 85, 100, 140, 200, 300, 400, 2000, 99999, 10010,
        };

        public static void UnlockArtifact(int id)
        {
            if (id < 0 || id >= artifactNames.Length || artifactsFound[id]) return;

            Engine.artifactsFound[id] = true;
            Engine.PlaySound("artifact");
            Engine.commentText = Engine.artifactComments[id][Rnd.Next(Engine.artifactComments[id].Count)];
            Engine.commentTime = 200;
            Engine.artifactTitle = Engine.artifactNames[id];
            Engine.artifactTime = 300;
            if (id <= 9)
                Score += (id+1) * 10;
        }

        #endregion

        public static string[] helpTexts = new string[]
        {
            "help...",
            "what happened?",
            "it.. it must have something to do with that hole... ",
            "you must go deep down and get the artifacts",
            "I fear it's all lost already",
            "we were earths last hope",
            "so... cold...",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            "",
            "*grumble*",
            "",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            //"",
            "I'm still alive",
            "Have you found anything to eat?",
            //"",
            //"",
            //"",
            //"",
            //"",
            "Tell Jenny I...",
            "that I...",
            "I...",

        };
        int chatTime = 20;
        public int chatIndex = 0;

        public static string artifactTitle;
        public static int artifactTime;
        public static int commentTime;
        public static string commentText;
        

        List<Particle2DSystem> particles = new List<Particle2DSystem>();

        Particle2DSystem pSmoke, pExplosion;
        public Emitter2D eSmoke1, eSmoke2, eExplosion, eShipExplosion;
        public int explodeTime = 0;
        System.Windows.Forms.Form form;

        public Engine()
        {
            S = this;
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            //graphics.PreferredBackBufferWidth = (int)GameRes.X;
            //graphics.PreferredBackBufferHeight = (int)GameRes.Y;


            this.Window.AllowUserResizing = true;
            this.IsMouseVisible = true;

            IntPtr ptr = this.Window.Handle;
            form = (System.Windows.Forms.Form)System.Windows.Forms.Control.FromHandle(ptr);
            //form.FormBorderStyle = FormBorderStyle.Sizable;
            //form.MaximizeBox = true;
            //form.WindowState = FormWindowState.Maximized;
            ToggleFullscreen();
            
            // Worker for highscores
            bw = new BackgroundWorker();
            bw.DoWork += new DoWorkEventHandler(updHighscores);
            bw.RunWorkerAsync();

            bloom = new BloomComponent(this);
            bloom.sceneRenderTarget = target;
            Components.Add(bloom);
            bloom.Settings = new BloomSettings(null, 0.5f, 2, 1, 1, 1, 1);
        }

        protected override void Initialize()
        {
            sb = new SpriteBatch(GraphicsDevice);
            //Components.Add(new Ward.SpritebatchComponent(this, sb, graphics.GraphicsDevice, "font1", "Software/jsmars/Hover"));
            base.Initialize();
        }

        protected override void LoadContent()
        {
            target = new RenderTarget2D(graphics.GraphicsDevice, GameRes.X, GameRes.Y);
            bloom.sceneRenderTarget = target;

            Data.LoadAll(Content);
            Spritesheet = Content.Load<Texture2D>("sprites");
            Tile1 = Content.Load<Texture2D>("tile1");
            Logo = Content.Load<Texture2D>("logo");
            bars = Content.Load<Texture2D>("bars");
            Pixel = new Texture2D(graphics.GraphicsDevice, 1, 1);
            Pixel.SetData<Color>(new Color[] { Color.White });
            font1 = Data.Font["font1"];
            font2 = Data.Font["font2"];

            #region Particles

            // SMOKE FROM SHIP
            pSmoke = new Particle2DSystem("sprites");
            particles.Add(pSmoke);
            pSmoke.OnInitializeParticle += (p) =>
                {
                    p.TexSource = new Rectangle(Engine.Rnd.Next(4) * 13, 51, 13, 13);
                    p.Color = Color.White;
                };
            pSmoke.OnUpdateParticle += (p) =>
                {
                    byte c = p.Color.R;
                    if (c >= 1)
                        c -= 1;
                    p.Color = new Color(c, c, c, c);
                };
            eSmoke1 = new Emitter2D();
            eSmoke2 = new Emitter2D();
            pSmoke.Emitters.Add(eSmoke1);
            pSmoke.Emitters.Add(eSmoke2);
            eSmoke1.EmitterPosition = new Vector2(447, 144);
            eSmoke2.EmitterPosition = new Vector2(463, 129);
            eSmoke1.LifeTimeMin = eSmoke2.LifeTimeMin = 3;
            eSmoke1.LifeTimeMax = eSmoke2.LifeTimeMax = 5;
            eSmoke1.VelocityMin = eSmoke2.VelocityMin = new Vector2(-0.2f, -0.6f);
            eSmoke1.VelocityMax = eSmoke2.VelocityMax = new Vector2(0.2f, -0.1f);
            eSmoke1.SpawnPerSecond = eSmoke2.SpawnPerSecond = 50;


            pExplosion = new Particle2DSystem("sprites");
            particles.Add(pExplosion);
            pExplosion.OnInitializeParticle += (p) =>
                {
                    p.Position = new Vector2(p.Position.X + (float)Rnd.NextDouble() * 10 - 5, p.Position.Y + (float)Rnd.NextDouble() * 10 - 5);
                    p.TexSource = new Rectangle(176, 192, 16, 16);
                    var f = Engine.Rnd.NextDouble();
                    if (p.Emitter == eShipExplosion)
                        eShipExplosion.SpawnPerSecond = (float)Rnd.NextDouble() * 3f + 0.2f;
                };
            pExplosion.OnUpdateParticle += (p) =>
                {
                    if (p.Life < 0.1f)
                        p.TexSource = new Rectangle(176, 192, 16, 16);
                    else if (p.Life < 0.2f)
                        p.TexSource = new Rectangle(176 + 16, 192, 16, 16);
                    else
                        p.TexSource = new Rectangle(176 + 32, 192, 16, 16);
                };
            eExplosion = new Emitter2D();
            pExplosion.Emitters.Add(eExplosion);
            eExplosion.LifeTimeMin = eExplosion.LifeTimeMax = 0.3f;
            eExplosion.SpawnPerSecond = 25;
            eExplosion.SpawnEnable = false;
            eExplosion.VelocityMin = new Vector2(-.1f, -.1f);
            eExplosion.VelocityMax = new Vector2(.1f, .1f);

            eShipExplosion = new Emitter2D();
            pExplosion.Emitters.Add(eShipExplosion);
            eShipExplosion.EmitterPosition = new Vector2(442, 141);
            eShipExplosion.LifeTimeMin = eShipExplosion.LifeTimeMax = 0.3f;
            eShipExplosion.SpawnEnable = true;
            eShipExplosion.SpawnPerSecond = 1.5f;
            eShipExplosion.VelocityMin = eExplosion.VelocityMin;
            eShipExplosion.VelocityMax = eExplosion.VelocityMax;
            
            #endregion

            foreach (var item in particles)
                item.LoadContent(Content);

            for (int i = 0; i < 200; i++)
                foreach (var item in particles)
                    item.Update(0.016f);

            actors.Add(new Player());

            music = Data.Sfx["gravity"].CreateInstance();
            music.Play();

            StartLevel();



            
            base.LoadContent();
        }

        public void StartLevel(int seed = 0)
        {
            if (seed == 0)
                seed = Rnd.Next();

            level = new Level(seed);
            Player.S.Start();
            Score = 0;
            camInertia = 0;
            camInertiaBuildup = camInertiaBuildupStart;
            CamPos = new Vector2(0, -50);
            camStart = false;
            camDown = false;
            chatIndex = 0;
            chatTime = 20;
            lastScoreRow = Level.startingRow;
            gameStartTime = DateTime.Now;
        }

        protected override void Update(GameTime gameTime)
        {
            if (Input.KeyPressed(Microsoft.Xna.Framework.Input.Keys.F1))
            {
                showHelp = !showHelp;
                showStartup = 0;
            }
            if (Input.Jump || Input.Right || Input.Left)
                showStartup = 0;
            if (Input.KeyPressed(Keys.F11))
                ToggleFullscreen();

            #region Music

            if (Input.KeyPressed(Microsoft.Xna.Framework.Input.Keys.M))
                mute = !mute;

            if (mute && music.State == SoundState.Playing)
                music.Stop();
            if (!mute && music.State != SoundState.Playing)
            {
                music = Data.Sfx["gravity"].CreateInstance();
                music.Play();
            }

            #endregion

            gt = gameTime;

            var mouse = new Vector2(Input.ms.X / Scale.X + CamPos.X, Input.ms.Y / Scale.Y + CamPos.Y);

            Input.Update();
            level.Update();
            foreach (var item in particles)
                item.Update(0.016f);

            if (Input.KeyPressed(Microsoft.Xna.Framework.Input.Keys.E) || Input.ps.Buttons.Y == Microsoft.Xna.Framework.Input.ButtonState.Pressed ||
                (!Player.S.Alive && Input.Jump && Player.S.DeadTime >= 0))
                StartLevel();

            foreach (var item in actors)
                item.Update();


            if (Input.KeyPressed(Microsoft.Xna.Framework.Input.Keys.X))
                foreach (var item in level.Rows)
                    item.CalcTiles();

            if (Input.KeyPressed(Microsoft.Xna.Framework.Input.Keys.C))
                System.GC.Collect();

            if (Input.Jump || Input.Left || Input.Right)
                camStart = true;
            if (camStart && camInertia < camInertiaTarget)
            {
                camInertiaBuildup *= 1.1f;
                camInertia = Math.Min(camInertia + camInertiaBuildup, camInertiaTarget);
            }

            if (Level.S.Rows.Count > 2 && Player.S.Position.Y > Level.S.Rows[2].PosY)
                camDown = true;

            if (camDown)
                camTarg = new Vector2(0, Player.S.Position.Y - 100);
            else
                camTarg = new Vector2(0, Player.S.Position.Y - 200);

            CamPos = Inertia(CamPos, camTarg, camInertia);

            int scorerow = (int)((Player.S.Position.Y - Level.S.ScoreStart) / Engine.TileSize);
            if (lastScoreRow < scorerow && Player.S.Alive)
            {
                Score += (scorerow - lastScoreRow);
                lastScoreRow = scorerow;
            }

            if (Player.S.Alive)
                Score = (int)MathHelper.Max(Score, (int)((Player.S.Position.Y - Level.S.ScoreStart) / Engine.TileSize));

            if (explodeTime > 0)
            {
                explodeTime--;
                if (explodeTime == 0)
                    eExplosion.SpawnEnable = false;
            }

            Ward.Watch("Mouse-Player.Dist", Vector2.Distance(Player.S.Position, mouse));
            Ward.Watch("Player Pos", Player.S.Position);
            Ward.SetLine("d", Player.S.Position, mouse, Color.Yellow);
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            //bloom.BeginDraw();

            #region Render Target

            graphics.GraphicsDevice.SetRenderTarget(target);

            GraphicsDevice.Clear(Color.Black);

            //var scaleBox = new Vector3(Math.Min(Scale.X, Scale.Y), Math.Min(Scale.X, Scale.Y), 0);
            //var excess = Math.Max(Scale.X, Scale.Y) - Math.Min(Scale.X, Scale.Y) * GameRes.Y;

            //var camP = CamPos;
            //if (Scale.X < Scale.Y)
            //    camP.Y += excess / 2;
            //else
            //    camP.X += excess / 2;



            //var scale = Matrix.CreateScale(Scale);
            var cam = Matrix.CreateTranslation(new Vector3(-CamPos, 0));// *scale;

            Ward.Set2DCamera(cam);

            sb.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.Default, RasterizerState.CullNone, null, cam);

            level.Draw(sb);

            #region Surface items

            foreach (var item in particles)
                item.Draw(sb);

            // DRAW SURFACE ITEMS
            sb.Draw(Spritesheet, new Vector2(0, 128 + 32), new Rectangle(96, 96, 32, 32), Color.White);
            sb.Draw(Spritesheet, new Vector2(GameRes.X - 128, 128), new Rectangle(128, 64, 128, 64), Color.White);

            if (friend)
                sb.Draw(Spritesheet, friendPos, Player.S.recDead, Color.White);

            sb.DrawString(font2, "THE CAKE IS A LIE", new Vector2(30, -580), new Color(30, 30, 30), 0, Vector2.Zero, 4, SpriteEffects.None, 1);


            if (chatIndex < helpTexts.Length && chatTime > 15 && friend)
            {
                var size = font1.MeasureString(helpTexts[chatIndex]);
                chatText(helpTexts[chatIndex], new Vector2(350 - size.X / 2, 150), Color.Yellow);
            }
            if (Player.S.Position.X > 270 && Player.S.Position.Y < Level.S.ScoreStart || chatIndex > 0)
                chatTime--;
            if (chatTime <= 0 && chatIndex < helpTexts.Length && friend)
            {
                chatIndex++;
                chatTime = 140;
                if (Player.S.Position.X > 270 && Player.S.Position.Y < Level.S.ScoreStart) // Close
                {
                    if (chatIndex + 1 == helpTexts.Length && !artifactsFound[12])
                        UnlockArtifact(12);
                    if (chatIndex < helpTexts.Length && helpTexts[chatIndex].Length > 0 && Player.S.Position.X > 270 && Player.S.Position.Y < Level.S.ScoreStart)
                        PlaySound("talk");
                }
            }

            if (Player.S.Position.Y < -390)
                UnlockArtifact(10);

            if (Player.S.Position.Y < Level.S.ScoreStart)
            {
                if (Player.S.Position.X > 400)
                {
                    commentTime = 180;
                    commentText = "The last thing I remember was a strange\ndisturbance in the moons gravity field";
                }

                if (Player.S.Position.X < 10)
                {
                    commentTime = 10000;
                    commentText = "Nothing over here..";
                }
            }

            #endregion

            #region Player Comments

            if (commentTime > 0)
            {
                commentTime--;
                var meassure = font1.MeasureString(commentText);
                int offset = (int)(meassure.X / 2);
                Vector2 pos = new Vector2(Player.S.Position.X + Player.S.Size.X/2 - offset, Player.S.Position.Y - meassure.Y - 5);
                if (pos.X + offset * 2 > GameRes.X)
                    pos.X = GameRes.X - offset * 2;
                if (pos.X < 0)
                    pos.X = 0;
                chatText(commentText, pos, Color.Orange);
            }

            foreach (var item in actors)
                item.Draw(sb);

            #endregion

            #region Logo

            float f = (camInertiaTarget - camInertia) / camInertiaTarget;
            Color c = new Color(f, f, f, f);
            if (!Player.S.Alive)
                f = 1;
            if (!showHelp)
            {
                sb.Draw(Logo, Vector2.Zero, null, c, 0, Vector2.Zero, 1, SpriteEffects.None, 1);

                if (showStartup > 0)
                {
                    showStartup--;
                    textShadowed("Press F1 for help", new Vector2(GameRes.X / 2, 150), new Color(0, 0, 0, (byte)MathHelper.Clamp(showStartup * 3, 0, 255)), 1, true);
                }
            }

            #endregion

            sb.End();

            #region Menu Texts

            sb.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.Default, RasterizerState.CullNone);

            if (showHelp)
            {
                sb.Draw(Pixel, new Rectangle(0, 0, (int)GameRes.X, (int)GameRes.Y), new Color(0, 0, 0, 200));

                sb.Draw(Logo, Vector2.Zero, null, Color.White, 0, Vector2.Zero, 1, SpriteEffects.None, 1);

                textShadowed("Controls:\n" +
                             "   Gamepad:\n" +
                             "     Move: Left Stick\n" +
                             "     Jump/Jetpack: A\n" +
                             "     Bomb: B\n" +
                             "     Speed: Left Trigger\n" +
                             "   Keyboard:\n" +
                             "     Move: Arrows or WASD\n" +
                             "     Jump/Jetpack: Space or Up\n" +
                             "     Bomb: Control or Z" +
                             "     Speed: Shift or 0\n\n" +
                             "     Mute: M\n" + 
                             "     Toggle Fullscreen: F11", new Vector2(40, 100), Color.Black);

                textShadowed("Tips:\n" +
                             "  * Hit stalagnites from the side to destroy them\n" +
                             "  * Dab the jetpack, don't hold it in.\n" +
                             "  * Have fun!\n\n\n\n" +
                             "Game created in 48h for Ludum Dare 29\n" +
                             "Thanks for playing!\n" +
                             "www.jsmars.com\n" +
                             "jsmars@gmail.com", new Vector2(280, 100), Color.Black);
                             
                             
            }
            else
            {
                if (f == 0 && Player.S.Alive)
                {
                    // FUEL BAR
                    sb.Draw(Engine.Pixel, new Vector2(Engine.GameRes.X / 2 + 1, Engine.GameRes.Y - 20 + 1), null, Color.Black, 0, new Vector2(0.5f, 0.5f), new Vector2((Engine.GameRes.X - 40) * (Player.S.fuel / (float)Player.fuelMax), 4), SpriteEffects.None, 1);
                    sb.Draw(Engine.Pixel, new Vector2(Engine.GameRes.X / 2, Engine.GameRes.Y - 20), null, Player.S.fuelColor, 0, new Vector2(0.5f, 0.5f), new Vector2((Engine.GameRes.X - 40) * (Player.S.fuel / (float)Player.fuelMax), 4), SpriteEffects.None, 1);

                    textShadowed("SCORE: " + Score + "\nBOMBS: " + Player.S.Bombs, new Vector2(20, 20), Color.Black, 1, false, font2);

                    if (artifactTime > 0)
                    {
                        artifactTime--;
                        int center = (int)(GameRes.X / 2);
                        textShadowed("ARTIFACT FOUND!", new Vector2(center, 210), new Color(166, 131, 47), 1, true, font2);
                        textShadowed(artifactTitle, new Vector2(center, 245), Color.Black, 1, true);
                    }
                }

                if (!Player.S.Alive)
                {
                    sb.Draw(Pixel, new Rectangle(0, 0, (int)GameRes.X, (int)GameRes.Y), new Color(0, 0, 0, 160));

                    int center = (int)(GameRes.X / 2);
                    textShadowed("YOU DIED", new Vector2(center, 20), Color.DarkRed, 4, true, font2);
                    //textShadowed("HUMANITY IS DOOMED!", new Vector2(center, 80), Color.Black, 1, true);
                    textShadowed("SCORE: " + Score, new Vector2(center, 80), Color.DarkRed, 2, true, font2);
                    textShadowed("PERSONAL BEST: " + highscoreper, new Vector2(center, 112), Color.DarkBlue, 1, true);


                    textShadowed("ARTIFACTS", new Vector2(center, 125), new Color(166, 131, 47), 1, true);

                    for (int i = 0; i < artifactRects.Length; i++)
                    {
                        Point2 pos = new Point2((int)(center - (artifactRects.Length / 2f) * 24 + 3 + i * 24), 140);
                        sb.Draw(Pixel, new Rectangle(pos.X, pos.Y, 18, 18), artifactsFound[i] ? Color.LightGreen : new Color(94, 4, 4));
                        sb.Draw(Spritesheet, new Vector2(pos.X + 1, pos.Y + 1), artifactRects[i], artifactsFound[i] ? Color.White : Color.Black);
                    }

                    textShadowed(fetchScore ? "-downlaoding highscores-" : "GLOBAL HIGHSCORE", new Vector2(center, 168), Color.DarkBlue, 1, true, font2);
                    textShadowed(highscore, new Vector2(65, 185), Color.Black);
                    textShadowed(highscore2, new Vector2(300, 185), Color.Black);

                    if (hsStats.Length >= 7)
                    {
                        int x = 10;
                        int y = 76;
                        textShadowed("YOUR TOTAL TIME: " + timeString(hsStats[1]), new Vector2(x, y), Color.Black);
                        textShadowed("YOUR TOTAL SCORE: " + hsStats[2], new Vector2(x, y + 10), Color.Black);
                        textShadowed("YOUR TOTAL PLAYES: " + hsStats[3], new Vector2(x, y + 20), Color.Black);
                        textShadowed("TOTAL TIME ALL: " + timeString(hsStats[4]), new Vector2(x, y + 30), Color.Black);
                        textShadowed("TOTAL SCORE ALL: " + hsStats[5], new Vector2(x, y + 40), Color.Black);
                        textShadowed("TOTAL PLAYS ALL: " + hsStats[6], new Vector2(x, y + 50), Color.Black);
                    }

                    textShadowed("Press Jump", new Vector2(center, GameRes.Y - 25), Color.Black, 1, true, font2);
                }

                sb.Draw(bars, new Rectangle(0, 0, GameRes.X, bars.Height), new Color(25, 25, 25, 25));
            }

            sb.End();

            #endregion

            graphics.GraphicsDevice.SetRenderTarget(null);

            #endregion

            base.Draw(gameTime);
        }

        private string timeString(string str)
        {
            float f;
            if (float.TryParse(str, out f))
            {
                var sec = f % 100;
                f -= sec;
                var min = f % 10000;
                f -= min;
                min /= 100;
                var hour = f / 10000;

                return hour + "h " + min + "m " + sec + "s";
            }
            return str;
        }

        private void textShadowed(string str, Vector2 pos, Color color, float scale = 1, bool center = false, SpriteFont font = null)
        {
            if (font == null)
                font = font1;

            Vector2 origin = Vector2.Zero;
            if (center)
                origin = new Vector2(font.MeasureString(str).X / 2, 0);

            Color col = new Color(190, 190, 190);

            sb.DrawString(font, str, pos + new Vector2(-scale, -scale), col, 0, origin, scale, SpriteEffects.None, 1);
            sb.DrawString(font, str, pos + new Vector2(scale, -scale), col, 0, origin, scale, SpriteEffects.None, 1);
            sb.DrawString(font, str, pos + new Vector2(-scale, scale), col, 0, origin, scale, SpriteEffects.None, 1);
            sb.DrawString(font, str, pos + new Vector2(scale, scale), col, 0, origin, scale, SpriteEffects.None, 1);
            sb.DrawString(font, str, pos + new Vector2(-scale, 0), col, 0, origin, scale, SpriteEffects.None, 1);
            sb.DrawString(font, str, pos + new Vector2(scale, 0), col, 0, origin, scale, SpriteEffects.None, 1);
            sb.DrawString(font, str, pos + new Vector2(0, -scale), col, 0, origin, scale, SpriteEffects.None, 1);
            sb.DrawString(font, str, pos + new Vector2(0, scale), col, 0, origin, scale, SpriteEffects.None, 1);
            sb.DrawString(font, str, pos, color, 0, origin, scale, SpriteEffects.None, 1);
        }

        private void chatText(string str, Vector2 pos, Color color)
        {
            sb.DrawString(font1, str, pos + new Vector2(1, 1), Color.Black);
            sb.DrawString(font1, str, pos, color);
        }

        public Vector2 Inertia(Vector2 source, Vector2 targ, float inertia)
        {
            return new Vector2(source.X + (targ.X - source.X) * inertia,
                               source.Y + (targ.Y - source.Y) * inertia);
        }

        public void ToggleFullscreen()
        {
            int sx = System.Windows.Forms.SystemInformation.PrimaryMonitorSize.Width;
            int sy = System.Windows.Forms.SystemInformation.PrimaryMonitorSize.Height;
            if (form.FormBorderStyle != System.Windows.Forms.FormBorderStyle.None)
            {
                form.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
                form.Location = new System.Drawing.Point(0, 0);
                graphics.PreferredBackBufferWidth = sx;
                graphics.PreferredBackBufferHeight = sy;
                graphics.ApplyChanges();
            }
            else
            {
                form.Location = new System.Drawing.Point(sx / 2 - GameRes.X / 2, sy / 2 - GameRes.Y / 2);
                form.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable;
                graphics.PreferredBackBufferWidth = GameRes.X;
                graphics.PreferredBackBufferHeight = GameRes.Y;
                graphics.ApplyChanges();
            }
        }

        public static void PlaySound(string name)
        {
            SoundEffect s;
            if (Data.Sfx.TryGetValue(name, out s))
            {
                var i = s.CreateInstance();
                i.Volume = S.sfxVolume;
                i.Play();
            }
        }


        public static void Explode(Vector2 position)
        {
            S.eExplosion.EmitterPosition = position;
            S.eExplosion.SpawnEnable = true;
            S.explodeTime = 20;

            //if (f < 0.33f)
            //    Engine.PlaySound("bomb1");
            //else if (f < 0.66f)
            //    Engine.PlaySound("bomb2");
            //else
            //    Engine.PlaySound("bomb3");
            Engine.PlaySound("bomb4");
        }

        public static bool submitScore;
        public static bool fetchScore = true;
        public static string subName, subScore, subTime, subCustom;
        const string highscoreUrl = "http://eriksson25.mine.nu/jsmars/highscores/";
        const string gamename = "hover";

        void updHighscores(object sender, DoWorkEventArgs e)
        {
            while (true)
            {
                if (submitScore)
                {
                    submitScore = false;
                    var url = highscoreUrl +
                                "index.php?" +
                                "insertscore=" + gamename +
                                "&version=" + 1 +
                                "&mode=" + 0 +
                                "&name=" + subName +
                                "&score=" + subScore +
                                "&custom=" + subCustom +
                                "&time=" + subTime;
                    GetWebResponse(url);
                    fetchScore = true;
                }

                if (fetchScore)
                {
                    var url = highscoreUrl + "index.php?showscores=" + gamename + "&getlist&limit=10&max";
                    var hs = GetWebResponse(url);
                    string[] lines = hs.Split('\n');
                    highscore = ""; highscore2 = "";
                    int i = 1;
                    foreach (var line in lines)
                    {
                        string[] word = line.Split('\t');
                        if (word.Length > 5)
                        {
                            var str = string.Concat(i++, ": ", word[0], "  ", word[1], "\n");
                            if (i <= 5)
                                highscore += str;
                            else
                                highscore2 += str;
                        }
                    }

                    highscoreper = "";
                    url = highscoreUrl + "index.php?showscores=" + gamename + "&getlist&best=" + System.Environment.MachineName;
                    hs = GetWebResponse(url);
                    lines = hs.Split('\n');
                    foreach (var line in lines)
                    {
                        string[] word = line.Split('\t');
                        if (word.Length > 5)
                        {
                            highscoreper += string.Concat(word[1], "   ");
                            break;
                        }
                    }

                    fetchScore = false;

                    url = highscoreUrl + "index.php?stats=" + gamename + "&name=" + System.Environment.MachineName;
                    hs = GetWebResponse(url);
                    lines = hs.Split(new string[] { "<br>" }, StringSplitOptions.RemoveEmptyEntries);
                    for (int x = 0; x < lines.Length; x++)
                        lines[x] = lines[x].Replace("\t", "");

                    hsStats = lines;
                }


                System.Threading.Thread.Sleep(150);
            }
        }

        public string GetWebResponse(string url)
        {
            WebRequest request = WebRequest.Create(url);
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream dataStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(dataStream);

            string responseFromServer = reader.ReadToEnd();

            reader.Close();
            dataStream.Close();
            response.Close();

            return responseFromServer;
        }
    }

    #region Entry

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main(string[] args)
        {
            using (Engine game = new Engine())
            {
                game.Run();
            }
        }
    }

    #endregion
}
