/*
    @brief Dullahan - a headless browser rendering engine
           based around the Chromium Embedded Framework
    @author Callum Prentice 2015

    LICENSE FILE TO GO HERE
*/

#ifndef _DULLAHAN
#define _DULLAHAN

#include <memory>
#include <functional>

class dullahan_impl;

class dullahan
{
    public:
        ////////// keyboard constants //////////
        typedef enum e_key_event
        {
            KE_KEY_DOWN,
            KE_KEY_REPEAT,
            KE_KEY_UP
        } EKeyEvent;

        typedef enum e_keyboard_modifier
        {
            KM_MODIFIER_NONE = 0x00,
            KM_MODIFIER_SHIFT = 0x01,
            KM_MODIFIER_CONTROL = 0x02,
            KM_MODIFIER_ALT = 0x04,
            KM_MODIFIER_META = 0x08
        } EKeyboardModifier;

        ////////// mouse constants //////////
        typedef enum e_mouse_event
        {
            ME_MOUSE_MOVE,
            ME_MOUSE_DOWN,
            ME_MOUSE_UP,
            ME_MOUSE_DOUBLE_CLICK
        } EMouseEvent;

        typedef enum e_mouse_button
        {
            MB_MOUSE_BUTTON_LEFT,
            MB_MOUSE_BUTTON_RIGHT,
            MB_MOUSE_BUTTON_MIDDLE
        } EMouseButton;

        ////////// cursor type //////////
        typedef enum e_cursor_type
        {
            CT_POINTER = 0,
            CT_CROSS,
            CT_HAND,
            CT_IBEAM,
            CT_WAIT,
            CT_HELP,
            CT_EASTRESIZE,
            CT_NORTHRESIZE,
            CT_NORTHEASTRESIZE,
            CT_NORTHWESTRESIZE,
            CT_SOUTHRESIZE,
            CT_SOUTHEASTRESIZE,
            CT_SOUTHWESTRESIZE,
            CT_WESTRESIZE,
            CT_NORTHSOUTHRESIZE,
            CT_EASTWESTRESIZE,
            CT_NORTHEASTSOUTHWESTRESIZE,
            CT_NORTHWESTSOUTHEASTRESIZE,
            CT_COLUMNRESIZE,
            CT_ROWRESIZE,
            CT_MIDDLEPANNING,
            CT_EASTPANNING,
            CT_NORTHPANNING,
            CT_NORTHEASTPANNING,
            CT_NORTHWESTPANNING,
            CT_SOUTHPANNING,
            CT_SOUTHEASTPANNING,
            CT_SOUTHWESTPANNING,
            CT_WESTPANNING,
            CT_MOVE,
            CT_VERTICALTEXT,
            CT_CELL,
            CT_CONTEXTMENU,
            CT_ALIAS,
            CT_PROGRESS,
            CT_NODROP,
            CT_COPY,
            CT_NONE,
            CT_NOTALLOWED,
            CT_ZOOMIN,
            CT_ZOOMOUT,
            CT_GRAB,
            CT_GRABBING,
            CT_CUSTOM,
        } ECursorType;

    public:
        //////////// initialization settings ////////////
        struct dullahan_settings
        {
            // initial dimensions of the browser window
            unsigned int initial_width = 512;
            unsigned int initial_height = 512;

            // substring inserted into existing user agent string
            // leave it blank by default otherwise "Chrome xx.x" part is removed
            std::string user_agent_substring = "";

            // default frame rate
            int frame_rate = 60;

            // enable/disable features - most obvious but listed for completeness
            bool java_enabled = false;              // java
            bool javascript_enabled = true;         // javascript
            bool webgl_enabled = true;              // webgl
            bool plugins_enabled = true;            // all plugins
            bool flash_enabled = true;              // system flash plugin
            bool media_stream_enabled = false;      // web cams etc. (caution)
            bool cookies_enabled = true;            // cookies
            bool cache_enabled = true;              // local cache
            bool begin_frame_scheduling = false;    // fixes issue when onPaint not called

            // path to browser cache
            std::string cache_path = "";

            // path to cookie store
            std::string cookie_store_path = "";

            // list of language locale codes used to configure the Accept-Language HTTP header value
            // and change the default language of the browser
            std::string accept_language_list = "en-us";

            // background color displayed before first page loaded (RRGGBB)
            unsigned int background_color = 0xffffff;
        };

    public:
        //////////// the API itself ////////////
        dullahan();
        ~dullahan();

        // initialize everything - call before anything else
        bool init(dullahan_settings& user_settings);

        // close down CEF - call just before you exit
        void shutdown();

        // indicate to CEF you want to exit - after you call this,
        // wait for onRequestExit() callback before calling shutdown()
        void requestExit();

        // accessors for size of virtual window
        void getSize(int& width, int& height);
        void setSize(int width, int height);
        int getDepth();

        // run CEF in it's own message loop - doesn't exit until requestExit()
        // and shutdown() calls triggered
        // Note: complimentary to update();
        void run();

        // do some work in CEF - call regularly in your own message loop
        // Note: complimentary to run();
        void update();

        // transport control
        bool canGoBack();
        void goBack();
        bool canGoForward();
        void goForward();
        bool isLoading();
        void reload(const bool ignore_cache);
        void stop();

        // versions of CEF, Chrome and this library and one with everything
        const std::string dullahan_cef_version();
        const std::string dullahan_chrome_version();
        const std::string dullahan_version();
        const std::string composite_version();

        // returns a user agent string based off of passed in string that
        // is "more" compatible with sites that look for a specific string
        std::string makeCompatibleUserAgentString(const std::string base);

        // mouse input
        void mouseButton(EMouseButton mouse_button,
                         EMouseEvent mouse_event,
                         int x, int y);
        void mouseMove(int x, int y);
        void mouseWheel(int delta_x, int delta_y);

        // keyboard input
        void nativeKeyboardEvent(uint32_t msg, uint32_t wparam, uint64_t lparam);

        // navigate to a URL
        void navigate(const std::string url);

        // give focus to virtual browser window
        void setFocus();

        // set the page zoom
        void setPageZoom(const double zoom_val);

        // show/hide the dev tools
        void showDevTools();
        void closeDevTools();

        // print page to PDF
        void printToPDF(const std::string path);

        // cookies
        bool setCookie(const std::string url,
                       const std::string name, const std::string value,
                       const std::string domain, const std::string path,
                       bool httponly, bool secure);
        void deleteAllCookies();

        // POST data to a URL
        void postData(const std::string url,
                      const std::string data,
                      const std::string headers);

        // javascript
        bool executeJavaScript(const std::string cmd);

        // display a message page in the browser - e.g. URL cannot be loaded
        void showBrowserMessage(const std::string msg);

        //////////// callback setters ////////////
        // URL changes - e.g. redirect
        void setOnAddressChangeCallback(std::function<void(const std::string url)> callback);

        // message appears in the JavaScript console
        void setOnConsoleMessageCallback(std::function<void(const std::string message,
                                         const std::string source, int line)> callback);

        // cursor changes - e.g. as passed over hyperlink or entered text field
        void setOnCursorChangedCallback(std::function<void(const ECursorType type,
                                        const unsigned int cursor)> callback);

        // custom URL scheme link is clicked (see setCustomSchemes(..))
        void setOnCustomSchemeURLCallback(std::function<void(const std::string url)> callback);

        // HTTP auth request triggered
        void setOnHTTPAuthCallback(std::function<bool(const std::string host,
                                   const std::string realm,
                                   std::string& username, std::string& password)> callback);

        // page finishes loading
        void setOnLoadEndCallback(std::function<void(int status)> callback);

        // page load error - e.g. 404
        void setOnLoadErrorCallback(
            std::function<void(int status, const std::string error_text)> callback);

        // page starts to load
        void setOnLoadStartCallback(std::function<void()> callback);

        // URL navigated
        void setOnNavigateURLCallback(std::function<void(const std::string url,
                                      const std::string target)> callback);

        // contents of the pages changes
        void setOnPageChangedCallback(std::function<void(const unsigned char* pixels,
                                      int x, int y,
                                      int width, int height,
                                      bool is_popup)> callback);

        // exit app requested
        void setOnRequestExitCallback(std::function<void()> callback);

        // browser status message changes
        void setOnStatusMessageCallback(std::function<void(const std::string message)> callback);

        // page title changes
        void setOnTitleChangeCallback(std::function<void(const std::string title)> callback);

        // a call to printToPDF completed
        void setOnPdfPrintFinishedCallback(std::function<void(const std::string path, bool ok)> callback);

        // file download requested
        void setOnFileDownloadCallback(std::function<void(const std::string filename)> callback);

        // file picker shown
        void setOnFileDialogCallback(std::function<const std::string()> callback);

    private:
        std::unique_ptr <dullahan_impl> mImpl;
};

#endif //  _DULLAHAN
