public static void Main(string[] args)
        {
            // First, check wether the user needs assistance:
            if (args.Length == 0 || args [0] == "--help")
            {
                PrintHelp();
                return;
            }

            if (args[0] == "--paths")
            {
                bool generatePNGs   = false;
                bool generateCSV    = false;
                bool writePathsToDB = true;

                switch (args[1])
                {
                case "--png":
                    generatePNGs = true;
                    break;

                case "--nopng":
                    generatePNGs = false;
                    break;

                default:
                    generatePNGs = false;
                    break;
                }

                switch (args[2])
                {
                case "--csv":
                    generateCSV = true;
                    break;

                case "--nocsv":
                    generateCSV = false;
                    break;

                default:
                    generateCSV = false;
                    break;
                }

                for (int i = 3; i < args.Length; i++)
                {
                    using (var fileStream = File.OpenRead(args[i]))
                    {
                        using (var parser = new DemoParser(fileStream))
                        {
                            PathGenerator.GeneratePath(parser, generatePNGs, generateCSV, writePathsToDB, Path.GetFileName(fileStream.Name));
                        }
                    }
                }
                return;
            }

            if (args[0] == "--analyze")
            {
                using (var fileStream = File.OpenRead(args[1]))
                {
                    PathAnalyzer.AnalyzePaths(Path.GetFileName(fileStream.Name), fileStream);
                }
                return;
            }

            if (args[0] == "--stratmeta")
            {
                StratMetaGenerator.GenerateStratMeta(int.Parse(args[1]));
                return;
            }

            if (args [0] == "--scoreboard")
            {
                using (var fileStream = File.OpenRead(args[1])) {
                    using (var parser = new DemoParser(fileStream)) {
                        ScoreboardGenerator.GenerateScoreboards(parser);
                    }
                }
                return;
            }

            if (args [0] == "--frags")
            {
                using (var fileStream = File.OpenRead(args[1])) {
                    using (var parser = new DemoParser(fileStream)) {
                        FragGenerator.GenerateFrags(parser);
                    }
                }
                return;
            }

            // Every argument is a file, so let's iterate over all the arguments
            // So you can call this program like
            // > StatisticsGenerator.exe hello.dem bye.dem
            // It'll generate the statistics.
            foreach (var fileName in args)
            {
                // Okay, first we need to initalize a demo-parser
                // It takes a stream, so we simply open with a filestream
                using (var fileStream = File.OpenRead(fileName)) {
                    // By using "using" we make sure that the fileStream is properly disposed
                    // the same goes for the DemoParser which NEEDS to be disposed (else it'll
                    // leak memory and kittens will die.

                    Console.WriteLine("Parsing demo " + fileName);

                    using (var parser = new DemoParser(fileStream)) {
                        // So now we've initialized a demo-parser.
                        // let's parse the head of the demo-file to get which map the match is on!
                        // this is always the first step you need to do.
                        parser.ParseHeader();

                        // and now, do some magic: grab the match!
                        string map = parser.Map;

                        // And now, generate the filename of the resulting file
                        string outputFileName = fileName + "." + map + ".csv";
                        // and open it.
                        var outputStream = new StreamWriter(outputFileName);

                        //And write a header so you know what is what in the resulting file
                        outputStream.WriteLine(GenerateCSVHeader());

                        // Cool! Now let's get started generating the analysis-data.

                        //Let's just declare some stuff we need to remember

                        // Here we'll save how far a player has travelled each round.
                        // Here we remember wheter the match has started yet.
                        bool hasMatchStarted = false;

                        int ctStartroundMoney = 0, tStartroundMoney = 0, ctEquipValue = 0, tEquipValue = 0, ctSaveAmount = 0, tSaveAmount = 0;

                        float ctWay = 0, tWay = 0;

                        int defuses = 0;
                        int plants  = 0;


                        Dictionary <Player, int> killsThisRound = new Dictionary <Player, int> ();

                        List <Player> ingame = new List <Player> ();

                        // Since most of the parsing is done via "Events" in CS:GO, we need to use them.
                        // So you bind to events in C# as well.

                        // AFTER we have bound the events, we start the parser!


                        parser.MatchStarted += (sender, e) => {
                            hasMatchStarted = true;
                            //Okay let's output who's really in this game!

                            Console.WriteLine("Participants: ");
                            Console.WriteLine("  Terrorits \"{0}\": ", parser.CTClanName);

                            foreach (var player in parser.PlayingParticipants.Where(a => a.Team == Team.Terrorist))
                            {
                                Console.WriteLine("    {0} {1} (Steamid: {2})", player.AdditionaInformations.Clantag, player.Name, player.SteamID);
                            }

                            Console.WriteLine("  Counter-Terrorits \"{0}\": ", parser.TClanName);
                            foreach (var player in parser.PlayingParticipants.Where(a => a.Team == Team.CounterTerrorist))
                            {
                                Console.WriteLine("    {0} {1} (Steamid: {2})", player.AdditionaInformations.Clantag, player.Name, player.SteamID);
                            }

                            // Okay, problem: At the end of the demo
                            // a player might have already left the game,
                            // so we need to store some information
                            // about the players before they left :)
                            ingame.AddRange(parser.PlayingParticipants);
                        };

                        parser.PlayerKilled += (object sender, PlayerKilledEventArgs e) => {
                            //the killer is null if you're killed by the world - eg. by falling
                            if (e.Killer != null)
                            {
                                if (!killsThisRound.ContainsKey(e.Killer))
                                {
                                    killsThisRound[e.Killer] = 0;
                                }

                                //Remember how many kills each player made this rounds
                                killsThisRound[e.Killer]++;
                            }
                        };

                        parser.RoundStart += (sender, e) => {
                            if (!hasMatchStarted)
                            {
                                return;
                            }

                            //How much money had each team at the start of the round?
                            ctStartroundMoney = parser.Participants.Where(a => a.Team == Team.CounterTerrorist).Sum(a => a.Money);
                            tStartroundMoney  = parser.Participants.Where(a => a.Team == Team.Terrorist).Sum(a => a.Money);

                            //And how much they did they save from the last round?
                            ctSaveAmount = parser.Participants.Where(a => a.Team == Team.CounterTerrorist && a.IsAlive).Sum(a => a.CurrentEquipmentValue);
                            tSaveAmount  = parser.Participants.Where(a => a.Team == Team.Terrorist && a.IsAlive).Sum(a => a.CurrentEquipmentValue);

                            //And let's reset those statistics
                            ctWay  = 0; tWay = 0;
                            plants = 0; defuses = 0;

                            killsThisRound.Clear();
                        };

                        parser.FreezetimeEnded += (sender, e) => {
                            if (!hasMatchStarted)
                            {
                                return;
                            }

                            // At the end of the freezetime (when players can start walking)
                            // calculate the equipment value of each team!
                            ctEquipValue = parser.Participants.Where(a => a.Team == Team.CounterTerrorist).Sum(a => a.CurrentEquipmentValue);
                            tEquipValue  = parser.Participants.Where(a => a.Team == Team.Terrorist).Sum(a => a.CurrentEquipmentValue);
                        };

                        parser.BombPlanted += (sender, e) => {
                            if (!hasMatchStarted)
                            {
                                return;
                            }

                            plants++;
                        };

                        parser.BombDefused += (sender, e) => {
                            if (!hasMatchStarted)
                            {
                                return;
                            }

                            defuses++;
                        };

                        parser.TickDone += (sender, e) => {
                            if (!hasMatchStarted)
                            {
                                return;
                            }

                            // Okay, let's measure how far each team travelled.
                            // As you might know from school the amount walked
                            // by a player is the sum of it's velocities

                            foreach (var player in parser.PlayingParticipants)
                            {
                                // We multiply it by the time of one tick
                                // Since the velocity is given in
                                // ingame-units per second
                                float currentWay = (float)(player.Velocity.Absolute * parser.TickTime);

                                // This is just an example of what kind of stuff you can do
                                // with this parser.
                                // Of course you could find out who makes the most footsteps, and find out
                                // which player ninjas the most - just to give you an example

                                if (player.Team == Team.CounterTerrorist)
                                {
                                    ctWay += currentWay;
                                }
                                else if (player.Team == Team.Terrorist)
                                {
                                    tWay += currentWay;
                                }
                            }
                        };

                        //So now lets do some fancy output
                        parser.RoundEnd += (sender, e) => {
                            if (!hasMatchStarted)
                            {
                                return;
                            }

                            // We do this in a method-call since we'd else need to duplicate code
                            // The much parameters are there because I simply extracted a method
                            // Sorry for this - you should be able to read it anywys :)
                            PrintRoundResults(parser, outputStream, ctStartroundMoney, tStartroundMoney, ctEquipValue, tEquipValue, ctSaveAmount, tSaveAmount, ctWay, tWay, defuses, plants, killsThisRound);
                        };

                        //Now let's parse the demo!
                        parser.ParseToEnd();

                        //And output the result of the last round again.
                        PrintRoundResults(parser, outputStream, ctStartroundMoney, tStartroundMoney, ctEquipValue, tEquipValue, ctSaveAmount, tSaveAmount, ctWay, tWay, defuses, plants, killsThisRound);



                        //Lets just display an end-game-scoreboard!

                        Console.WriteLine("Finished! Results: ");
                        Console.WriteLine("  Terrorits \"{0}\": ", parser.CTClanName);

                        foreach (var player in ingame.Where(a => a.Team == Team.Terrorist))
                        {
                            Console.WriteLine(
                                "    {0} {1} (Steamid: {2}): K: {3}, D: {4}, A: {5}",
                                player.AdditionaInformations.Clantag,
                                player.Name, player.SteamID,
                                player.AdditionaInformations.Kills,
                                player.AdditionaInformations.Deaths,
                                player.AdditionaInformations.Assists
                                );
                        }

                        Console.WriteLine("  Counter-Terrorits \"{0}\": ", parser.TClanName);
                        foreach (var player in ingame.Where(a => a.Team == Team.CounterTerrorist))
                        {
                            Console.WriteLine(
                                "    {0} {1} (Steamid: {2}): K: {3}, D: {4}, A: {5}",
                                player.AdditionaInformations.Clantag,
                                player.Name, player.SteamID,
                                player.AdditionaInformations.Kills,
                                player.AdditionaInformations.Deaths,
                                player.AdditionaInformations.Assists
                                );
                        }

                        outputStream.Close();
                    }
                }
            }
        }
        static public void AnalyzePaths(string filename, FileStream fileStream)
        {
            long                   matchID                 = 0;
            string                 mapName                 = "";
            List <ingameRound>     allRoundsList           = new List <ingameRound>();
            List <ingameRound>     matchRoundsList         = new List <ingameRound>();
            List <ingameRound>     existingStratRoundsList = new List <ingameRound>();
            List <ingamePathpoint> allPathpointsList       = new List <ingamePathpoint>();

            MySqlConnection sqlConnection = null;
            MySqlDataReader sqlReader     = null;


            //Setup connection with the MySQL Server//////////////////////////////////////
            try
            {
                string connectionString = "SERVER=localhost;DATABASE=demostatistics;UID=demostatistics;PASSWORD=1ax4X7M4;";
                sqlConnection = new MySqlConnection(connectionString);
                sqlConnection.Open();
                Console.WriteLine(getTimeStamp() + "Connected to MySQL Server");
            }
            catch (MySqlException er)
            {
                Console.WriteLine(getTimeStamp() + "Error: {0}", er.ToString());
            }
            //////////////////////////////////////////////////////////////////////////////



            //Preloading info for analysis.///////////////////////////////////////////////
            //Get match based on the demo filename
            string       matchSelectQuery      = "SELECT id,mapName FROM `match` WHERE filename='" + filename + "'";
            MySqlCommand matchSelectionCommand = new MySqlCommand(matchSelectQuery, sqlConnection);

            sqlReader = matchSelectionCommand.ExecuteReader();

            while (sqlReader.Read())
            {
                //For each match
                matchID = sqlReader.GetInt64("id");
                mapName = sqlReader.GetString("mapName");
            }
            sqlReader.Close();
            //If match is not found in database launch PathGenerator
            if (matchID == 0)
            {
                Console.WriteLine(getTimeStamp() + "Match not found in DB... Running PathGenerator...");
                Console.WriteLine(); Console.WriteLine(); Console.WriteLine();

                using (var parser = new DemoParser(fileStream))
                {
                    PathGenerator.GeneratePath(parser, false, false, true, filename);
                }

                sqlReader = matchSelectionCommand.ExecuteReader();
                while (sqlReader.Read())
                {
                    //For each match
                    matchID = sqlReader.GetInt64("id");
                    mapName = sqlReader.GetString("mapName");
                }
                sqlReader.Close();
                //System.Environment.Exit(1);
            }
            Console.WriteLine(getTimeStamp() + filename + " is being analyzed");

            //Prepare statement to get all viable rounds from database
            Console.Write(getTimeStamp() + "Loading all rounds in DB... ");
            string       allRoundsSelectionQuery   = "SELECT id,roundType,stratFound FROM `round` WHERE bombPlanted=1 OR enemyTeamWiped=1 AND mapName='" + mapName + "'";
            MySqlCommand allRoundsSelectionCommand = new MySqlCommand(allRoundsSelectionQuery, sqlConnection);

            sqlReader = allRoundsSelectionCommand.ExecuteReader();

            //Load each round which meets the query
            while (sqlReader.Read())
            {
                allRoundsList.Add(new ingameRound(sqlReader.GetInt64("id"), sqlReader.GetString("roundType"), sqlReader.GetBoolean("stratFound")));
            }
            sqlReader.Close();
            Console.WriteLine(allRoundsList.Count + " entries - Done");

            //Prepare statement to get viable rounds from match
            Console.Write(getTimeStamp() + "Loading rounds from Demo-File... ");
            string       matchRoundSelectionQuery   = "SELECT id,roundType,stratFound FROM `round` WHERE matchID=" + matchID + " AND bombPlanted=1 OR enemyTeamWiped=1 AND stratFound=0";
            MySqlCommand matchRoundSelectionCommand = new MySqlCommand(matchRoundSelectionQuery, sqlConnection);

            sqlReader = matchRoundSelectionCommand.ExecuteReader();

            while (sqlReader.Read())
            {
                matchRoundsList.Add(new ingameRound(sqlReader.GetInt64("id"), sqlReader.GetString("roundType"), sqlReader.GetBoolean("stratFound")));
            }
            sqlReader.Close();
            Console.WriteLine(matchRoundsList.Count + " entries - Done");

            //Prepare statement to get viable rounds from existing strats
            Console.Write(getTimeStamp() + "Loading rounds from existing strats... ");
            string       existingStratsRoundSelectionQuery   = "SELECT stratID,roundID,roundType FROM `stratrounds`";
            MySqlCommand existingStratsRoundSelectionCommand = new MySqlCommand(existingStratsRoundSelectionQuery, sqlConnection);

            sqlReader = existingStratsRoundSelectionCommand.ExecuteReader();
            while (sqlReader.Read())
            {
                existingStratRoundsList.Add(new ingameRound(sqlReader.GetInt64("roundID"), sqlReader.GetString("roundType"), true));
                //existingStratRoundsList.Find(n => n.roundID == sqlReader.GetInt64("roundID")).stratID = sqlReader.GetInt64("stratID");

                foreach (var existingRound in existingStratRoundsList.Where(n => n.roundID == sqlReader.GetInt64("roundID")))
                {
                    existingRound.stratID = sqlReader.GetInt64("stratID");
                }
            }
            sqlReader.Close();
            Console.WriteLine(existingStratRoundsList.Count + " entries - Done");

            //Prepare statement to get all viable pathpoints from database
            Console.Write(getTimeStamp() + "Loading all pathpoints in DB... ");
            foreach (var round in allRoundsList)
            {
                string       pathpointsSelectionQuery   = "SELECT roundID,X,Y,Z FROM `pathpoints` WHERE roundID=" + round.roundID;
                MySqlCommand pathpointsSelectionCommand = new MySqlCommand(pathpointsSelectionQuery, sqlConnection);
                sqlReader = pathpointsSelectionCommand.ExecuteReader();

                while (sqlReader.Read())
                {
                    allPathpointsList.Add(new ingamePathpoint(sqlReader.GetInt64("roundID"), sqlReader.GetInt32("X"), sqlReader.GetInt32("Y"), sqlReader.GetInt32("Z")));
                }
                sqlReader.Close();
            }
            Console.WriteLine(allPathpointsList.Count + " entries - Done");
            /////////////////////////////////////////////////////////////////////////////



            //Analysis Loop - ITS HAPPENING!!!
            Console.WriteLine(); Console.WriteLine();
            Console.WriteLine(getTimeStamp() + "Starting analysis loop");
            foreach (var round in matchRoundsList)
            {
                //Load all pathpoints for this round in the demo file
                List <ingamePathpoint> demoPathpointList = new List <ingamePathpoint>();

                Console.Write(getTimeStamp() + "Loading pathpoints for round " + round.roundID + "... ");
                string       demoPathpointsSelectionQuery   = "SELECT roundID,X,Y,Z FROM `pathpoints` WHERE roundID=" + round.roundID;
                MySqlCommand demoPathpointsSelectionCommand = new MySqlCommand(demoPathpointsSelectionQuery, sqlConnection);
                sqlReader = demoPathpointsSelectionCommand.ExecuteReader();

                while (sqlReader.Read())
                {
                    demoPathpointList.Add(new ingamePathpoint(sqlReader.GetInt64("roundID"), sqlReader.GetInt32("X"), sqlReader.GetInt32("Y"), sqlReader.GetInt32("Z")));
                }
                sqlReader.Close();
                Console.WriteLine(demoPathpointList.Count + " entries - Done");

                bool matchingRoundInDB = false;
                //List which contains all rounds which are similar to this round from the Demo
                List <ingameRound> demoRoundSimilarRounds = new List <ingameRound>();
                //Add the round from the demo itself to the list
                demoRoundSimilarRounds.Add(round);

                Parallel.ForEach(allRoundsList, dbRound =>
                {
                    int counter        = 0;
                    int successCounter = 0;
                    foreach (var demoPathPoint in demoPathpointList)
                    {
                        ingamePathpoint roundedDemoPathPoint = new ingamePathpoint(demoPathPoint.roundID, (((int)Math.Round((int)demoPathPoint.X / 30.0)) * 30), (((int)Math.Round((int)demoPathPoint.Y / 30.0)) * 30), (((int)Math.Round((int)demoPathPoint.Z / 30.0)) * 30));
                        foreach (var dbPathPoint in allPathpointsList.Where(n => n.roundID != demoPathPoint.roundID && n.roundID == dbRound.roundID))
                        {
                            ingamePathpoint roundedDbPathPoint = new ingamePathpoint(dbPathPoint.roundID, (((int)Math.Round((int)dbPathPoint.X / 30.0)) * 30), (((int)Math.Round((int)dbPathPoint.Y / 30.0)) * 30), (((int)Math.Round((int)dbPathPoint.Z / 30.0)) * 30));

                            counter++;
                            double distance = Math.Sqrt(MyPow((roundedDbPathPoint.X - roundedDemoPathPoint.X), 2) + MyPow((roundedDbPathPoint.Y - roundedDemoPathPoint.Y), 2) + MyPow((roundedDbPathPoint.Z - roundedDemoPathPoint.Z), 2));


                            if (distance == 0)
                            {
                                successCounter++;
                            }
                        }
                    }

                    if (counter != 0)
                    {
                        //Calculate percentage of pathpoints from the DB which are close to the pathpoints from the demo
                        float successPercentage = ((float)successCounter / (float)counter) * 100;
                        if (successPercentage >= 1.7)
                        {
                            //Found a round which seems to be the same strat like the the round from the demo
                            Console.WriteLine("Success round " + dbRound.roundID + ": " + successPercentage.ToString(".0###") + "%");

                            //Add the round from the database which seems similar to the list.
                            demoRoundSimilarRounds.Add(dbRound);
                            matchingRoundInDB = true;
                        }
                    }
                });

                //If a match has been found
                if (matchingRoundInDB)
                {
                    long existingStratID = -1;
                    foreach (var similarRound in demoRoundSimilarRounds.Where(n => n.stratFound == true))
                    {
                        foreach (var stratRound in existingStratRoundsList.Where(n => n.roundID == similarRound.roundID))
                        {
                            Console.WriteLine("The matching round " + similarRound.roundID + " is already in the strat " + stratRound.stratID);
                            existingStratID = stratRound.stratID;
                        }
                    }

                    if (existingStratID != -1)
                    {
                        int    newRoundCounter = 0;
                        string addRoundToExistingStratQuery = "INSERT INTO `stratrounds` (stratID,roundID,roundType) VALUES ";
                        foreach (var similarRound in demoRoundSimilarRounds.Where(n => n.stratFound == false))
                        {
                            Console.WriteLine("Adding round " + similarRound.roundID + " to the existing strat " + existingStratID);
                            newRoundCounter++;

                            similarRound.stratFound = true;
                            similarRound.stratID    = existingStratID;
                            existingStratRoundsList.Add(similarRound);

                            foreach (var dbRound in allRoundsList.Where(n => n.roundID == similarRound.roundID))
                            {
                                dbRound.stratFound = true;
                                dbRound.stratID    = existingStratID;
                            }
                            foreach (var matchRound in matchRoundsList.Where(n => n.roundID == similarRound.roundID))
                            {
                                matchRound.stratFound = true;
                                matchRound.stratID    = existingStratID;
                            }

                            //Write stratFound into DB
                            MySqlCommand setStratFoundForRound = new MySqlCommand("UPDATE `round` SET stratFound=1 WHERE id=" + similarRound.roundID, sqlConnection);
                            setStratFoundForRound.ExecuteNonQuery();

                            addRoundToExistingStratQuery += "(" + existingStratID + "," + similarRound.roundID + ",'" + similarRound.roundType + "'),";
                        }

                        if (newRoundCounter >= 1)
                        {
                            addRoundToExistingStratQuery  = addRoundToExistingStratQuery.Remove(addRoundToExistingStratQuery.Length - 1);
                            addRoundToExistingStratQuery += ";";
                            MySqlCommand addRoundToExistingStrat = new MySqlCommand(addRoundToExistingStratQuery, sqlConnection);
                            addRoundToExistingStrat.ExecuteNonQuery();
                            Console.WriteLine("Added all rounds to the existing strat " + existingStratID);
                        }
                    }

                    else if (existingStratID == -1)
                    {
                        //Create new strat in DB
                        MySqlCommand createNewStratForRounds = new MySqlCommand("INSERT INTO `strats` (mapName,stratType) VALUES ('" + mapName + "','" + round.roundType + "')", sqlConnection);
                        createNewStratForRounds.ExecuteNonQuery();
                        Console.WriteLine("Created new strat " + createNewStratForRounds.LastInsertedId);

                        string addRoundToNewStratQuery = "INSERT INTO `stratrounds` (stratID,roundID,roundType) VALUES ";
                        string updateRoundsQuery       = "UPDATE `round` SET stratFound=1 WHERE";
                        foreach (var similarRound in demoRoundSimilarRounds)
                        {
                            Console.WriteLine("Adding round " + similarRound.roundID + " to the new strat " + createNewStratForRounds.LastInsertedId);
                            similarRound.stratFound = true;
                            similarRound.stratID    = createNewStratForRounds.LastInsertedId;
                            existingStratRoundsList.Add(similarRound);

                            foreach (var dbRound in allRoundsList.Where(n => n.roundID == similarRound.roundID))
                            {
                                dbRound.stratFound = true;
                                dbRound.stratID    = createNewStratForRounds.LastInsertedId;
                            }
                            foreach (var matchRound in matchRoundsList.Where(n => n.roundID == similarRound.roundID))
                            {
                                matchRound.stratFound = true;
                                matchRound.stratID    = createNewStratForRounds.LastInsertedId;
                            }

                            addRoundToNewStratQuery += "(" + createNewStratForRounds.LastInsertedId + "," + similarRound.roundID + ",'" + similarRound.roundType + "'),";
                            updateRoundsQuery       += " id=" + similarRound.roundID + " OR";
                        }
                        addRoundToNewStratQuery  = addRoundToNewStratQuery.Remove(addRoundToNewStratQuery.Length - 1);
                        addRoundToNewStratQuery += ";";
                        MySqlCommand insertRoundToStrat = new MySqlCommand(addRoundToNewStratQuery, sqlConnection);
                        insertRoundToStrat.ExecuteNonQuery();

                        updateRoundsQuery  = updateRoundsQuery.Remove(updateRoundsQuery.Length - 3);
                        updateRoundsQuery += ";";
                        MySqlCommand updateRounds = new MySqlCommand(updateRoundsQuery, sqlConnection);
                        updateRounds.ExecuteNonQuery();

                        Console.WriteLine("Added all rounds to the new strat " + createNewStratForRounds.LastInsertedId);
                    }
                }
            }
        }