///////////////////////////////////////////////////////////////////////////////
// Name:        wx/private/webrequest.h
// Purpose:     wxWebRequest implementation classes
// Author:      Vadim Zeitlin
// Created:     2020-12-26
// Copyright:   (c) 2020 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

#ifndef _WX_PRIVATE_WEBREQUEST_H_
#define _WX_PRIVATE_WEBREQUEST_H_

#include "wx/ffile.h"
#include "wx/hashmap.h"
#include "wx/scopedptr.h"

#include "wx/private/refcountermt.h"

WX_DECLARE_STRING_HASH_MAP(wxString, wxWebRequestHeaderMap);

// Default buffer size when a fixed-size buffer must be used.
const int wxWEBREQUEST_BUFFER_SIZE = 64 * 1024;

// Trace mask used for the messages in wxWebRequest code.
#define wxTRACE_WEBREQUEST "webrequest"

// ----------------------------------------------------------------------------
// wxWebAuthChallengeImpl
// ----------------------------------------------------------------------------

class wxWebAuthChallengeImpl : public wxRefCounterMT
{
public:
    virtual ~wxWebAuthChallengeImpl() { }

    wxWebAuthChallenge::Source GetSource() const { return m_source; }

    virtual void SetCredentials(const wxWebCredentials& cred) = 0;

protected:
    explicit wxWebAuthChallengeImpl(wxWebAuthChallenge::Source source)
        : m_source(source) { }

private:
    const wxWebAuthChallenge::Source m_source;

    wxDECLARE_NO_COPY_CLASS(wxWebAuthChallengeImpl);
};

// ----------------------------------------------------------------------------
// wxWebRequestImpl
// ----------------------------------------------------------------------------

class wxWebRequestImpl : public wxRefCounterMT
{
public:
    virtual ~wxWebRequestImpl() { }

    void SetHeader(const wxString& name, const wxString& value)
    { m_headers[name] = value; }

    void SetMethod(const wxString& method) { m_method = method; }

    void SetData(const wxString& text, const wxString& contentType, const wxMBConv& conv = wxConvUTF8);

    bool SetData(wxScopedPtr<wxInputStream>& dataStream, const wxString& contentType, wxFileOffset dataSize = wxInvalidOffset);

    void SetStorage(wxWebRequest::Storage storage) { m_storage = storage; }

    wxWebRequest::Storage GetStorage() const { return m_storage; }

    // Precondition for this method checked by caller: current state is idle.
    virtual void Start() = 0;

    // Precondition for this method checked by caller: not idle and not already
    // cancelled.
    void Cancel();

    virtual wxWebResponseImplPtr GetResponse() const = 0;

    virtual wxWebAuthChallengeImplPtr GetAuthChallenge() const = 0;

    int GetId() const { return m_id; }

    wxWebSession& GetSession() const { return m_session; }

    wxWebRequest::State GetState() const { return m_state; }

    virtual wxFileOffset GetBytesSent() const = 0;

    virtual wxFileOffset GetBytesExpectedToSend() const = 0;

    virtual wxFileOffset GetBytesReceived() const;

    virtual wxFileOffset GetBytesExpectedToReceive() const;

    virtual wxWebRequestHandle GetNativeHandle() const = 0;

    void DisablePeerVerify(bool disable) { m_peerVerifyDisabled = disable; }

    bool IsPeerVerifyDisabled() const { return m_peerVerifyDisabled; }

    void SetState(wxWebRequest::State state, const wxString& failMsg = wxString());

    void ReportDataReceived(size_t sizeReceived);

    wxEvtHandler* GetHandler() const { return m_handler; }

    // Called to notify about the state change in the main thread by SetState()
    // (which can itself be called from a different one).
    //
    // It also releases a reference added when switching to the active state by
    // SetState() when leaving it.
    //
    // TODO-C++11: make private when we don't need StateEventProcessor any more.
    void ProcessStateEvent(wxWebRequest::State state, const wxString& failMsg);

protected:
    wxString m_method;
    wxWebRequest::Storage m_storage;
    wxWebRequestHeaderMap m_headers;
    wxFileOffset m_dataSize;
    wxScopedPtr<wxInputStream> m_dataStream;
    bool m_peerVerifyDisabled;

    wxWebRequestImpl(wxWebSession& session,
                     wxWebSessionImpl& sessionImpl,
                     wxEvtHandler* handler,
                     int id);

    bool WasCancelled() const { return m_cancelled; }

    // Call SetState() with either State_Failed or State_Completed appropriate
    // for the response status.
    void SetFinalStateFromStatus();

private:
    // Called from public Cancel() at most once per object.
    virtual void DoCancel() = 0;

    wxWebSession& m_session;
    wxEvtHandler* const m_handler;
    const int m_id;
    wxWebRequest::State m_state;
    wxFileOffset m_bytesReceived;
    wxCharBuffer m_dataText;

    // Initially false, set to true after the first call to Cancel().
    bool m_cancelled;

    wxDECLARE_NO_COPY_CLASS(wxWebRequestImpl);
};

// ----------------------------------------------------------------------------
// wxWebResponseImpl
// ----------------------------------------------------------------------------

class wxWebResponseImpl : public wxRefCounterMT
{
public:
    virtual ~wxWebResponseImpl();

    virtual wxFileOffset GetContentLength() const = 0;

    virtual wxString GetURL() const = 0;

    virtual wxString GetHeader(const wxString& name) const = 0;

    virtual wxString GetMimeType() const;

    virtual int GetStatus() const = 0;

    virtual wxString GetStatusText() const = 0;

    virtual wxInputStream* GetStream() const;

    virtual wxString GetSuggestedFileName() const;

    wxString AsString() const;

    virtual wxString GetDataFile() const;

protected:
    wxWebRequestImpl& m_request;
    size_t m_readSize;

    explicit wxWebResponseImpl(wxWebRequestImpl& request);

    // Called from derived class ctor to finish initialization which can't be
    // performed in ctor itself as it needs to use pure virtual method.
    void Init();

    void* GetDataBuffer(size_t sizeNeeded);

    void ReportDataReceived(size_t sizeReceived);

    // This function can optionally be called to preallocate the read buffer,
    // if the total amount of data to be downloaded is known in advance.
    void PreAllocBuffer(size_t sizeNeeded);

private:
    // Called by wxWebRequestImpl only.
    friend class wxWebRequestImpl;
    void Finalize();

    wxMemoryBuffer m_readBuffer;
    mutable wxFFile m_file;
    mutable wxScopedPtr<wxInputStream> m_stream;

    wxDECLARE_NO_COPY_CLASS(wxWebResponseImpl);
};

// ----------------------------------------------------------------------------
// wxWebSessionFactory
// ----------------------------------------------------------------------------

class wxWebSessionFactory
{
public:
    virtual wxWebSessionImpl* Create() = 0;

    virtual bool Initialize() { return true; }

    virtual ~wxWebSessionFactory() { }
};

// ----------------------------------------------------------------------------
// wxWebSessionImpl
// ----------------------------------------------------------------------------

class wxWebSessionImpl : public wxRefCounterMT
{
public:
    virtual ~wxWebSessionImpl() { }

    virtual wxWebRequestImplPtr
    CreateRequest(wxWebSession& session,
                  wxEvtHandler* handler,
                  const wxString& url,
                  int id) = 0;

    virtual wxVersionInfo GetLibraryVersionInfo() = 0;

    void AddCommonHeader(const wxString& name, const wxString& value)
        { m_headers[name] = value; }

    void SetTempDir(const wxString& dir) { m_tempDir = dir; }

    wxString GetTempDir() const;

    const wxWebRequestHeaderMap& GetHeaders() const { return m_headers; }

    virtual wxWebSessionHandle GetNativeHandle() const = 0;

protected:
    wxWebSessionImpl();

private:
    // Make it a friend to allow accessing our m_headers.
    friend class wxWebRequest;

    wxWebRequestHeaderMap m_headers;
    wxString m_tempDir;

    wxDECLARE_NO_COPY_CLASS(wxWebSessionImpl);
};

#endif // _WX_PRIVATE_WEBREQUEST_H_
