/*
 * Copyright (C) 2004-2020 ZNC, see the NOTICE file for details.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ZNC_WEBMODULES_H
#define ZNC_WEBMODULES_H

#include <znc/zncconfig.h>
#include <znc/Template.h>
#include <znc/HTTPSock.h>
#include <znc/Utils.h>
#include <znc/Translation.h>

class CAuthBase;
class CUser;
class CWebSock;
class CModule;
class CWebSubPage;

typedef std::shared_ptr<CWebSubPage> TWebSubPage;
typedef std::vector<TWebSubPage> VWebSubPages;

class CZNCTagHandler : public CTemplateTagHandler {
  public:
    CZNCTagHandler(CWebSock& pWebSock);
    virtual ~CZNCTagHandler() {}

    bool HandleTag(CTemplate& Tmpl, const CString& sName, const CString& sArgs,
                   CString& sOutput) override;

  private:
    CWebSock& m_WebSock;
};

class CWebSession {
  public:
    CWebSession(const CString& sId, const CString& sIP);
    ~CWebSession();

    CWebSession(const CWebSession&) = delete;
    CWebSession& operator=(const CWebSession&) = delete;

    const CString& GetId() const { return m_sId; }
    const CString& GetIP() const { return m_sIP; }
    CUser* GetUser() const { return m_pUser; }
    time_t GetLastActive() const { return m_tmLastActive; }
    bool IsLoggedIn() const { return m_pUser != nullptr; }
    bool IsAdmin() const;
    void UpdateLastActive();

    CUser* SetUser(CUser* p) {
        m_pUser = p;
        return m_pUser;
    }

    void ClearMessageLoops();
    void FillMessageLoops(CTemplate& Tmpl);
    size_t AddError(const CString& sMessage);
    size_t AddSuccess(const CString& sMessage);

  private:
    CString m_sId;
    CString m_sIP;
    CUser* m_pUser;
    VCString m_vsErrorMsgs;
    VCString m_vsSuccessMsgs;
    time_t m_tmLastActive;
};

class CWebSubPage {
  public:
    CWebSubPage(const CString& sName, const CString& sTitle = "",
                unsigned int uFlags = 0)
        : m_uFlags(uFlags), m_sName(sName), m_Title(sTitle), m_vParams() {}

    CWebSubPage(const CString& sName, const COptionalTranslation& Title,
                const VPair& vParams, unsigned int uFlags = 0)
        : m_uFlags(uFlags),
          m_sName(sName),
          m_Title(Title),
          m_vParams(vParams) {}

    virtual ~CWebSubPage() {}

    enum { F_ADMIN = 1 };

    void SetName(const CString& s) { m_sName = s; }
    void SetTitle(const COptionalTranslation& s) {
        m_Title = s;
    }
    void AddParam(const CString& sName, const CString& sValue) {
        m_vParams.push_back(make_pair(sName, sValue));
    }

    bool RequiresAdmin() const { return m_uFlags & F_ADMIN; }

    const CString& GetName() const { return m_sName; }
    CString GetTitle() const { return m_Title.Resolve(); }
    const VPair& GetParams() const { return m_vParams; }

  private:
    unsigned int m_uFlags;
    CString m_sName;
    COptionalTranslation m_Title;
    VPair m_vParams;
};

class CWebSessionMap : public TCacheMap<CString, std::shared_ptr<CWebSession>> {
  public:
    CWebSessionMap(unsigned int uTTL = 5000)
        : TCacheMap<CString, std::shared_ptr<CWebSession>>(uTTL) {}
    void FinishUserSessions(const CUser& User);
};

class CWebSock : public CHTTPSock {
  public:
    enum EPageReqResult {
        PAGE_NOTFOUND,  // print 404 and Close()
        PAGE_PRINT,     // print page contents and Close()
        PAGE_DEFERRED,  // async processing, Close() will be called from a
                        // different place
        PAGE_DONE       // all stuff has been done
    };

    CWebSock(const CString& sURIPrefix);
    virtual ~CWebSock();

    bool ForceLogin() override;
    bool OnLogin(const CString& sUser, const CString& sPass,
                 bool bBasic) override;
    void OnPageRequest(const CString& sURI) override;

    EPageReqResult PrintTemplate(const CString& sPageName, CString& sPageRet,
                                 CModule* pModule = nullptr);
    EPageReqResult PrintStaticFile(const CString& sPath, CString& sPageRet,
                                   CModule* pModule = nullptr);

    CString FindTmpl(CModule* pModule, const CString& sName);

    void PrintErrorPage(const CString& sMessage);

    std::shared_ptr<CWebSession> GetSession();

    Csock* GetSockObj(const CString& sHost, unsigned short uPort) override;
    static CString GetSkinPath(const CString& sSkinName);
    void GetAvailSkins(VCString& vRet) const;
    CString GetSkinName();

    CString GetRequestCookie(const CString& sKey);
    bool SendCookie(const CString& sKey, const CString& sValue);

    static void FinishUserSessions(const CUser& User);

    CString GetCSRFCheck();
    bool ValidateCSRFCheck(const CString& sURI);

  protected:
    using CHTTPSock::PrintErrorPage;

    bool AddModLoop(const CString& sLoopName, CModule& Module,
                    CTemplate* pTemplate = nullptr);
    VCString GetDirs(CModule* pModule, bool bIsTemplate);
    void SetPaths(CModule* pModule, bool bIsTemplate = false);
    void SetVars();

  private:
    EPageReqResult OnPageRequestInternal(const CString& sURI,
                                         CString& sPageRet);

    bool m_bPathsSet;
    CTemplate m_Template;
    std::shared_ptr<CAuthBase> m_spAuth;
    CString m_sModName;
    CString m_sPath;
    CString m_sPage;
    std::shared_ptr<CWebSession> m_spSession;

    static const unsigned int m_uiMaxSessions;
};

#endif  // !ZNC_WEBMODULES_H
