/// <summary>
        /// Returns an archive download link that returns a game archive for the specified OperatingSystem, game version, bitness and client type.
        /// </summary>
        /// <exception cref="ArgumentOutOfRangeException">The value of spec.OperatingSystem wasn't expected.</exception>
        public Uri GetGameArchiveDownloadUri(GameArchiveSpec spec)
        {
            /* uri format: factorio.com/get-download/{version}/{build}/{platform}
             *
             * version: 0.0.0
             * build: alpha, demo, headless (only linux64 platform)
             * platform:
             * - win64: .exe file, win64-manual: .zip file, win32, .exe file, win32-manual: .zip file
             * - osx: .dmg file
             * - linux64: .tar.gz file, linux32: .tar.gz file
             *
             */

            string platform;

            switch (spec.OperatingSystem)
            {
            case OperatingSystem.Windows:
                platform = "win64-manual";
                break;

            case OperatingSystem.Mac:
                platform = "osx";
                break;

            case OperatingSystem.Linux:
                platform = "linux64";
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            var uriTail = string.Format("{0}/{1}/{2}", spec.Version, spec.BuildConfiguration, platform);

            return(new Uri(_gameArchiveDownloadsUriBase, uriTail));
        }
        /// <summary>
        /// Note: the extension component of the destination path in the GameArchive result may be different based on the archive format received.
        /// </summary>
        /// <exception cref="SecurityException">The caller does not have the required permission to access the file.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="spec"/> or <paramref name="destinationPath"/> is <see langword="null" />.</exception>
        /// <exception cref="FileNotFoundException"></exception>
        /// <exception cref="HttpRequestException">Recieved an error status code that could not be recovered from.</exception>
        public async Task <GameArchive> GetGameAsArchive(GameArchiveSpec spec, string destinationPath)
        {
            if (spec == null)
            {
                throw new ArgumentNullException("spec");
            }
            if (destinationPath == null)
            {
                throw new ArgumentNullException("destinationPath");
            }

            // Append the proper extension if it doesn't have it already
            // So that apps like 7z can properly extract the file in the future
            var extension = spec.GetFileExtension();

            if (!destinationPath.EndsWith(extension))
            {
                destinationPath = destinationPath + extension;
            }

            try
            {
                using (var outStream = new FileStream(destinationPath, FileMode.OpenOrCreate))
                    using (var inStream = await GetGameAsArchiveStream(spec))
                    {
                        await inStream.CopyToAsync(outStream);
                    }
            }

            catch (DirectoryNotFoundException ex)
            {
                throw new FileNotFoundException(ex.Message, ex);
            }

            return(new GameArchive(destinationPath, spec));
        }
        // ReSharper disable ExceptionNotThrown
        /// <exception cref="WebException">Request failed due to a bad connection (e.g. no internet).</exception>
        /// <exception cref="HttpRequestException">Recieved an error status code that could not be recovered from.</exception>
        // ReSharper restore ExceptionNotThrown
        public async Task <Stream> GetGameAsArchiveStream(GameArchiveSpec spec)
        {
            var uri = _homepageUriFactory.GetGameArchiveDownloadUri(spec);

            return(await _client.GetStreamAsync(uri));
        }