//  SuperTuxKart - a fun racing game with go-kart
//  Copyright (C) 2011-2015 Joerg Henrichs
//                2013 Glenn De Jonghe
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 3
//  of the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

#ifndef HEADER_HTTP_REQUEST_HPP
#define HEADER_HTTP_REQUEST_HPP

#include "online/request.hpp"
#include "utils/cpp2011.hpp"
#include "utils/string_utils.hpp"

#include <cassert>
#include <cstdint>
#include <atomic>
#include <string>

namespace Online
{
    bool globalHTTPRequestInit();
    void globalHTTPRequestCleanup();

    class API
    {
    public:
        static const std::string USER_PATH;
        static const std::string SERVER_PATH;
    };

    /** A http request.
     */
    class HTTPRequest : public Request
    {
    private:
        /** The progress indicator. 0 until it is started and the first
         *  packet is downloaded. Guaranteed to be <1 while the download
         *  is in progress, it will be set to either -1 (error) or 1
         *  (everything ok) at the end. */
        std::atomic<float> m_progress;

        std::atomic<double> m_total_size;

        /** The url to download. */
        std::string m_url;

        /** The POST parameters that will be sent with the request. */
        std::string m_parameters;

        /** Result code from request implementation, 0 if succeeded. */
        int64_t m_result_code;

        /** String to store the received data in. */
        std::string m_string_buffer;

#ifdef APPLE_NETWORK_LIBRARIES
        std::string m_error_string;
#endif

        std::string urlEncode(const std::string& value);

        void logMessage();
    protected:
        /** Contains a filename if the data should be saved into a file
         *  instead of being kept in memory. Otherwise this is "". */
        std::string m_filename;

        bool m_disable_sending_log;

        /* If true, it will not send POST parameters so
         * it's just a GET request. */
        bool m_download_assets_request;

        virtual void operation() OVERRIDE;

        void init();
    public:
        HTTPRequest(int priority = 1);
        HTTPRequest(const std::string &filename, int priority = 1);
        HTTPRequest(const char * const filename, int priority = 1);
        virtual bool       isAllowedToAdd() const OVERRIDE;
        void               setApiURL(const std::string& url, const std::string &action);
        void               setAddonsURL(const std::string& path);

        // ------------------------------------------------------------------------
        /** Returns true if there was an error downloading the file. */
        virtual bool hadDownloadError() const        { return m_result_code != 0; }
        // ------------------------------------------------------------------------
        void setDownloadAssetsRequest(bool val)
                                               { m_download_assets_request = val; }
        // ------------------------------------------------------------------------
        /** Returns the error message if an error has occurred.
         */
        const char* getDownloadErrorMessage() const;
        // ------------------------------------------------------------------------
        /** Returns the downloaded string.
         *  \pre request has to be done
         *  \return get the result string from the request reply
         */
        const std::string & getData() const
        {
            assert(hasBeenExecuted());
            return m_string_buffer;
        }   // getData

        // --------------------------------------------------------------------
        /** Sets a parameter to 'value' (std::string). */
        void addParameter(const std::string & name, const std::string &value)
        {
            // Call the template, so that the strings are escaped properly
            addParameter(name, value.c_str());
        }   // addParameter

        // --------------------------------------------------------------------
        /** Sets a parameter to 'value' (stringw). */
        void addParameter(const std::string &name,
                          const irr::core::stringw &value)
        {
            std::string s = StringUtils::wideToUtf8(value);

            // Call the template to escape strings properly
            addParameter(name, s.c_str());
        }   // addParameter

        // --------------------------------------------------------------------
        /** Sets a parameter to 'value' (arbitrary types). */
        template <typename T>
        void addParameter(const std::string &name, const T& value)
        {
            assert(isPreparing());
            std::string s = StringUtils::toString(value);
            std::string s1 = urlEncode(name);
            std::string s2 = urlEncode(s);
            m_parameters.append(s1 + "=" + s2 + "&");
        }

        // --------------------------------------------------------------------
        /** Returns the current progress. */
        float getProgress() const { return m_progress.load(); }

        // --------------------------------------------------------------------
        /** Sets the current progress. */
        void setProgress(float f) { m_progress.store(f); }

        // --------------------------------------------------------------------
        const std::string & getURL() const { assert(isBusy()); return m_url;}

        // --------------------------------------------------------------------
        /** Sets the URL for this request. */
        void setURL(const std::string & url)
        {
            assert(isPreparing());
            m_url = url;
        }   // setURL
        // --------------------------------------------------------------------
        const std::string& getFileName() const           { return m_filename; }
        // --------------------------------------------------------------------
        double getTotalSize() const             { return m_total_size.load(); }
        // --------------------------------------------------------------------
        void setTotalSize(double d)                  { m_total_size.store(d); }
    };   // class HTTPRequest
} //namespace Online
#endif // HEADER_HTTP_REQUEST_HPP
