/***************************************************************************
 *   crwx-ng                                                               *
 *   Copyright (C) 2007-2010,2012 Vadim Lopatin <coolreader.org@gmail.com> *
 *   Copyright (C) 2008 Torque <torque@mail.ru>                            *
 *   Copyright (C) 2018 Sergey Torokhov <torokhov-s-a@yandex.ru>           *
 *   Copyright (C) 2020-2024 Aleksey Chernov <valexlin@gmail.com>          *
 *                                                                         *
 *   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 2        *
 *   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., 51 Franklin Street, Fifth Floor, Boston,            *
 *   MA 02110-1301, USA.                                                   *
 ***************************************************************************/

#include "readerview.h"
#include "readerframe.h"
#include "utils.h"
#include "app-props.h"

#include <wx/scrolbar.h>
#include <wx/menu.h>
#include <wx/dcclient.h>
#include <wx/power.h>

#include <lvstreamutils.h>
#include <ldomdocument.h>

#define RENDER_TIMER_ID 123
#define CLOCK_TIMER_ID  124
#define CURSOR_TIMER_ID 125

BEGIN_EVENT_TABLE(ReaderView, wxPanel)
    EVT_PAINT(ReaderView::OnPaint)
    EVT_SIZE(ReaderView::OnSize)
    EVT_MOUSEWHEEL(ReaderView::OnMouseWheel)
    EVT_LEFT_DOWN(ReaderView::OnMouseLDown)
    EVT_RIGHT_DOWN(ReaderView::OnMouseRDown)
    EVT_MOTION(ReaderView::OnMouseMotion)
    EVT_MENU_RANGE(0, 0xFFFF, ReaderView::OnCommand)
    EVT_SET_FOCUS(ReaderView::OnSetFocus)
    EVT_TIMER(RENDER_TIMER_ID, ReaderView::OnTimer)
    EVT_TIMER(CLOCK_TIMER_ID, ReaderView::OnTimer)
    EVT_TIMER(CURSOR_TIMER_ID, ReaderView::OnTimer)
    EVT_INIT_DIALOG(ReaderView::OnInitDialog)
END_EVENT_TABLE()

wxColour ReaderView::getBackgroundColour() {
#if (COLOR_BACKBUFFER == 1)
    lUInt32 cl = getDocView()->getBackgroundColor();
#else
    lUInt32 cl = 0xFFFFFF;
#endif
    wxColour wxcl((cl >> 16) & 255, (cl >> 8) & 255, (cl >> 0) & 255);
    return wxcl;
}

void ReaderView::OnInitDialog(wxInitDialogEvent& event) {
    //SetBackgroundColour( getBackgroundColour() );
    _isFullscreen = _props->getBoolDef(PROP_APP_WINDOW_FULLSCREEN);
}

lString32 ReaderView::GetLastRecentFileName() {
    if (getDocView() && getDocView()->getHistory()->getRecords().length() > 0)
        return getDocView()->getHistory()->getRecords()[0]->getFilePathName();
    return lString32::empty_str;
}

ReaderView::ReaderView(CRPropRef props)
        : _normalCursor(wxCURSOR_ARROW)
        , _linkCursor(wxCURSOR_HAND)
        , _scrollbar(NULL)
        , _props(props)
        , _screen(300, 400)
        , _wm(&_screen) {
    _wm.activateWindow((_docwin = new CRDocViewWindow(&_wm)));
    CRPropRef defProps = getDocView()->propsGetCurrent();
    addProperties(_props, defProps);
    getDocView()->setCallback(this);
    getDocView()->setPageMargins(lvRect(14, 5, 14, 5));

    getDocView()->setMinFontSize(14);
    getDocView()->setMaxFontSize(72);

    //_docview->setBackgroundColor(0x000000);
    //_docview->setTextColor(0xFFFFFF);

    {
        LVStreamRef stream = LVOpenFileStream(GetHistoryFileName().c_str(), LVOM_READ);
        if (!stream.isNull()) {
            getDocView()->getHistory()->loadFromStream(stream);
            stream = NULL;
        }
    }

    _renderTimer = new wxTimer(this, RENDER_TIMER_ID);
    _clockTimer = new wxTimer(this, CLOCK_TIMER_ID);
    _cursorTimer = new wxTimer(this, CURSOR_TIMER_ID);

    InitDialog();
}

ReaderView::~ReaderView() {
    delete _renderTimer;
    delete _clockTimer;
    delete _cursorTimer;
}

void ReaderView::OnTimer(wxTimerEvent& event) {
    //printf("cr3view::OnTimer() \n");
    if (event.GetId() == RENDER_TIMER_ID) {
        int dx;
        int dy;
        GetClientSize(&dx, &dy);
        //if ( _docview->IsRendered() && dx == _docview->GetWidth()
        //        && dy == _docview->GetHeight() )
        //    return; // no resize
        if (dx < 50 || dy < 50 || dx > 10000 || dy > 10000) {
            return;
        }
        _wm.reconfigure(dx, dy, CR_ROTATE_ANGLE_0);
        getDocView()->checkRender();
        doPaint();
    } else if (event.GetId() == CURSOR_TIMER_ID) {
        SetCursor(wxCursor(wxCURSOR_BLANK));
    } else if (event.GetId() == CLOCK_TIMER_ID) {
        if (IsShownOnScreen()) {
            if (getDocView()->IsRendered() && getDocView()->isTimeChanged())
                doPaint();
        }
    }
}

static bool getBatteryState(int& state, int& chargingConn, int& level) {
#ifdef _WIN32
    // update battery state
    SYSTEM_POWER_STATUS bstatus;
    BOOL pow = GetSystemPowerStatus(&bstatus);
    if (pow) {
        state = CR_BATTERY_STATE_DISCHARGING;
        if (bstatus.BatteryFlag & 128)
            state = CR_BATTERY_STATE_NO_BATTERY; // no system battery
        else if (bstatus.BatteryFlag & 8)
            state = CR_BATTERY_STATE_CHARGING; // charging
        chargingConn = CR_BATTERY_CHARGER_NO;
        if (bstatus.ACLineStatus == 1)
            chargingConn = CR_BATTERY_CHARGER_AC; // AC power charging connected
        if (bstatus.BatteryLifePercent >= 0 && bstatus.BatteryLifePercent <= 100)
            level = bstatus.BatteryLifePercent;
        return true;
    }
    return false;
#else
    wxPowerType wxpwrtype = wxGetPowerType();
    switch (wxpwrtype) {
        case wxPOWER_SOCKET:
            state = CR_BATTERY_STATE_CHARGING;
            chargingConn = CR_BATTERY_CHARGER_AC;
            break;
        case wxPOWER_BATTERY:
            state = CR_BATTERY_STATE_DISCHARGING;
            chargingConn = CR_BATTERY_CHARGER_NO;
            break;
        default:
            state = CR_BATTERY_STATE_NO_BATTERY;
            chargingConn = CR_BATTERY_CHARGER_NO;
            break;
    }
    wxBatteryState wxbatstate = wxGetBatteryState();
    switch (wxbatstate) {
        case wxBATTERY_NORMAL_STATE:
            level = 100;
            break;
        case wxBATTERY_LOW_STATE:
            level = 50;
            break;
        case wxBATTERY_CRITICAL_STATE:
            level = 5;
            break;
        case wxBATTERY_SHUTDOWN_STATE:
            level = 0;
            break;
        default:
            level = 0;
            break;
    }
    return true;
#endif
}

void ReaderView::doPaint() {
    //printf("cr3view::Paint() \n");
    int battery_state;
    int charging_conn;
    int charge_level;
    if (getBatteryState(battery_state, charging_conn, charge_level)) {
        getDocView()->setBatteryState(battery_state, charging_conn, charge_level);
    }
    // Refresh() generates a call to the onPaint() function, where we render the page if necessary
    Refresh(false);
}

lString32 ReaderView::GetHistoryFileName() {
    return getConfigDir() + cs32("cruihist.bmk");
}

void ReaderView::CloseDocument() {
    //printf("cr3view::CloseDocument()  \n");
    getDocView()->savePosition();
    getDocView()->Clear();
    LVStreamRef stream = LVOpenFileStream(GetHistoryFileName().c_str(), LVOM_WRITE);
    if (!stream.isNull())
        getDocView()->getHistory()->saveToStream(stream.get());
}

void ReaderView::UpdateScrollBar() {
    if (!_scrollbar)
        return;
    if (!getDocView()->IsRendered())
        return;
    const LVScrollInfo* lvsi = getDocView()->getScrollInfo();
    _scrollbar->SetScrollbar(lvsi->pos,                     //int position,
                             lvsi->pagesize,                //int thumbSize,
                             lvsi->maxpos + lvsi->pagesize, //int range,
                             lvsi->pagesize,                //int pageSize,
                             true                           //const bool refresh = true
    );
    wxStatusBar* sb = ((wxFrame*)GetParent())->GetStatusBar();
    if (sb) {
        if (!_linkStatusText.IsEmpty()) {
            sb->PopStatusText();
            _linkStatusText = wxString();
        }
        int sw[2] = { -1, 0 };
        wxString posText = cr2wx(lvsi->posText);
        wxSize addSize = sb->GetTextExtent("MWM");
        wxSize sz = sb->GetTextExtent(posText);
        sw[1] = sz.GetWidth() + addSize.GetWidth();
        sb->SetStatusWidths(2, sw);
        sb->SetStatusText(posText, 1);
    }
}

bool ReaderView::loadCSS(wxString filename) {
    lString32 fn32 = wx2cr(filename);
    lString32 configDir = getConfigDir();
    lString32 engineDataDir = getEngineDataDir();
    lString32 cssFileInConfigDir = configDir + fn32;
    lString32 cssFileInEngineDir = engineDataDir + fn32;
    lString8 cssData;
    bool res = LVLoadStylesheetFile(cssFileInConfigDir, cssData);
    if (res)
        CRLog::info("Using style sheet from %s", LCSTR(cssFileInConfigDir));
    else {
        res = LVLoadStylesheetFile(cssFileInEngineDir, cssData);
        if (res)
            CRLog::info("Using style sheet from %s", LCSTR(cssFileInEngineDir));
    }
    if (res)
        getDocView()->setStyleSheet(cssData);
    else
        CRLog::error("Failed to load style sheet %s", LCSTR(fn32));
    return res;
}

void ReaderView::OnMouseMotion(wxMouseEvent& event) {
    int x = event.GetX();
    int y = event.GetY();
    ldomXPointer ptr = getDocView()->getNodeByPoint(lvPoint(x, y));
    if (ptr.isNull()) {
        return;
    }
    wxStatusBar* sb = ((wxFrame*)GetParent())->GetStatusBar();
    lString32 href = ptr.getHRef();
    if (href.empty()) {
        SetCursor(_normalCursor);
        if (sb) {
            if (!_linkStatusText.IsEmpty()) {
                sb->PopStatusText();
                _linkStatusText = wxString();
            }
        }
    } else {
        SetCursor(_linkCursor);
        if (sb) {
            wxString link = cr2wx(href);
            if (link != _linkStatusText) {
                if (!_linkStatusText.IsEmpty())
                    sb->PopStatusText();
                _linkStatusText = link;
                sb->PushStatusText(_linkStatusText);
            }
        }
    }
    if (_isFullscreen) {
        // Scheduled to hide the cursor after 3 seconds.
        _cursorTimer->Stop();
        _cursorTimer->Start(3 * 1000, wxTIMER_ONE_SHOT);
    }
}

void ReaderView::OnMouseLDown(wxMouseEvent& event) {
    int x = event.GetX();
    int y = event.GetY();
    //lString32 txt = _docview->getPageText( true );
    //CRLog::debug( "getPageText : %s", UnicodeToUtf8(txt).c_str() );
    ldomXPointer ptr = getDocView()->getNodeByPoint(lvPoint(x, y));
    if (ptr.isNull()) {
        CRLog::debug("cr3view::OnMouseLDown() : node not found!\n");
        return;
    }
    lString32 href = ptr.getHRef();
    if (ptr.getNode()->isText()) {
        lString8 s = UnicodeToUtf8(ptr.toString());
        CRLog::debug("Text node clicked (%d, %d): %s", x, y, s.c_str());
        ldomXRange* wordRange = new ldomXRange();
        if (ldomXRange::getWordRange(*wordRange, ptr)) {
            wordRange->setFlags(0x10000);
            getDocView()->getDocument()->getSelections().clear();
            getDocView()->getDocument()->getSelections().add(wordRange);
            getDocView()->updateSelections();
        } else {
            delete wordRange;
        }
        if (!href.empty()) {
            getDocView()->goLink(href);
            doPaint();
            return;
        }
        doPaint();
        printf("text : %s     \t", s.c_str());
    } else {
        printf("element : %s  \t", UnicodeToUtf8(ptr.toString()).c_str());
    }
    lvPoint pt2 = ptr.toPoint();
    printf("  (%d, %d)  ->  (%d, %d)\n", x, y + getDocView()->GetPos(), pt2.x, pt2.y);
}

void ReaderView::OnMouseRDown(wxMouseEvent& event) {
    wxMenu pm;
    pm.Append(wxID_OPEN, wxT("&Open...\tCtrl+O"));
    pm.Append(Menu_View_History, wxT("Recent books list\tF4"));
    pm.Append(wxID_SAVE, wxT("&Export...\tCtrl+S"));
    pm.AppendSeparator();
    pm.Append(Menu_File_Options, wxT("Options...\tF9"));
    pm.AppendSeparator();
    pm.Append(Menu_View_TOC, wxT("Table of Contents\tF5"));
    pm.Append(Menu_File_About, wxT("&About...\tF1"));
    pm.AppendSeparator();
    pm.Append(Menu_View_ZoomIn, wxT("Zoom In"));
    pm.Append(Menu_View_ZoomOut, wxT("Zoom Out"));
    pm.AppendSeparator();
    pm.Append(Menu_View_ToggleFullScreen, wxT("Toggle Fullscreen\tAlt+Enter"));
    pm.Append(Menu_View_TogglePages, wxT("Toggle Pages/Scroll\tCtrl+P"));
    pm.Append(Menu_View_TogglePageHeader, wxT("Toggle page heading\tCtrl+H"));
    pm.AppendSeparator();
    pm.Append(Menu_File_Quit, wxT("E&xit\tAlt+X"));

    ((wxFrame*)GetParent())->PopupMenu(&pm);
}

void ReaderView::ToggleViewMode() {
    int appViewMode = _props->getIntDef(PROP_APP_PAGE_VIEW_MODE, 2);
    int pages = _props->getIntDef(PROP_LANDSCAPE_PAGES, 2);
    appViewMode = (0 == appViewMode) ? pages : 0;
    _props->setInt(PROP_APP_PAGE_VIEW_MODE, appViewMode);
    ApplyProps();
}

void ReaderView::ApplyProps() {
    // Process application properties
    int appViewMode = _props->getIntDef(PROP_APP_PAGE_VIEW_MODE, 2);
    switch (appViewMode) {
        case 0:
            _props->setBool(PROP_PAGE_VIEW_MODE, false);
            break;
        case 1:
            _props->setBool(PROP_PAGE_VIEW_MODE, true);
            _props->setInt(PROP_LANDSCAPE_PAGES, 1);
            break;
        case 2:
        default:
            _props->setBool(PROP_PAGE_VIEW_MODE, true);
            _props->setInt(PROP_LANDSCAPE_PAGES, 2);
            break;
    }
    getDocView()->propsApply(_props);
    getDocView()->requestRender();
    doPaint();
}

void ReaderView::ApplyDocLang() {
    lString32 prevDocLang = _props->getStringDef(PROP_TEXTLANG_MAIN_LANG, "");
    lString32 docLang = getDocView()->getLanguage();
    if (prevDocLang != docLang) {
        _props->setString(PROP_TEXTLANG_MAIN_LANG, docLang);
        getDocView()->propsApply(_props);
        getDocView()->requestRender();
    }
}

void ReaderView::OnCommand(wxCommandEvent& event) {
    switch (event.GetId()) {
        case Menu_View_ZoomIn: {
            wxCursor hg(wxCURSOR_WAIT);
            this->SetCursor(hg);
            wxSetCursor(hg);
            //===========================================
            doCommand(DCMD_ZOOM_IN, 0);
            //===========================================
            this->SetCursor(wxNullCursor);
            wxSetCursor(wxNullCursor);
        } break;
        case Menu_View_ZoomOut: {
            wxCursor hg(wxCURSOR_WAIT);
            this->SetCursor(hg);
            wxSetCursor(hg);
            //===========================================
            doCommand(DCMD_ZOOM_OUT, 0);
            //===========================================
            this->SetCursor(wxNullCursor);
            wxSetCursor(wxNullCursor);
        } break;
        case Menu_View_NextPage:
            doCommand(DCMD_PAGEDOWN, 1);
            getDocView()->cachePageImage(0);
            getDocView()->cachePageImage(1);
            break;
        case Menu_Link_Forward:
            doCommand(DCMD_LINK_FORWARD, 1);
            break;
        case Menu_Link_Back:
            doCommand(DCMD_LINK_BACK, 1);
            break;
        case Menu_Link_Next:
            doCommand(DCMD_LINK_NEXT, 1);
            break;
        case Menu_Link_Prev:
            doCommand(DCMD_LINK_PREV, 1);
            break;
        case Menu_Link_Go:
            doCommand(DCMD_LINK_GO, 1);
            break;
        case Menu_View_PrevPage:
            doCommand(DCMD_PAGEUP, 1);
            getDocView()->cachePageImage(0);
            getDocView()->cachePageImage(-1);
            break;
        case Menu_View_NextLine:
            doCommand(DCMD_LINEDOWN, 1);
            break;
        case Menu_View_PrevLine:
            doCommand(DCMD_LINEUP, 1);
            break;
        case Menu_View_Begin:
            doCommand(DCMD_BEGIN, 0);
            break;
        case Menu_View_End:
            doCommand(DCMD_END, 0);
            break;
        case Menu_View_TogglePages:
            ToggleViewMode();
            break;
        case Menu_View_TogglePageHeader: {
            int mode = _props->getIntDef(PROP_STATUS_LINE, 1);
            _props->setInt(PROP_STATUS_LINE, (0 == mode) ? 1 : 0);
            ApplyProps();
            break;
        }
        case Menu_View_Text_Format:
            doCommand(DCMD_TOGGLE_TEXT_FORMAT, 0);
            break;
    }
}

void ReaderView::OnSetFocus(wxFocusEvent& event) {
    GetParent()->SetFocus();
}

void ReaderView::OnScroll(wxScrollEvent& event) {
    int id = event.GetEventType();
    //printf("Scroll event: %d\n", id);
    if (id == wxEVT_SCROLL_TOP)
        doCommand(DCMD_BEGIN, 0);
    else if (id == wxEVT_SCROLL_BOTTOM)
        doCommand(DCMD_BEGIN, 0);
    else if (id == wxEVT_SCROLL_LINEUP)
        doCommand(DCMD_LINEUP, 0);
    else if (id == wxEVT_SCROLL_LINEDOWN)
        doCommand(DCMD_LINEDOWN, 0);
    else if (id == wxEVT_SCROLL_PAGEUP)
        doCommand(DCMD_PAGEUP, 0);
    else if (id == wxEVT_SCROLL_PAGEDOWN)
        doCommand(DCMD_PAGEDOWN, 0);
    else if (id == wxEVT_SCROLL_THUMBRELEASE || id == wxEVT_SCROLL_THUMBTRACK) {
        doCommand(DCMD_GO_POS, getDocView()->scrollPosToDocPos(event.GetPosition()));
    }
}

void ReaderView::OnMouseWheel(wxMouseEvent& event) {
    int rotation = event.GetWheelRotation();
    if (rotation > 0)
        doCommand(DCMD_LINEUP, 3);
    else if (rotation < 0)
        doCommand(DCMD_LINEDOWN, 3);
}

void ReaderView::OnKeyDown(wxKeyEvent& event) {
    int code = event.GetKeyCode();

    switch (code) {
        case WXK_NUMPAD_ADD: {
            doCommand(DCMD_ZOOM_IN, 0);
        } break;
        case WXK_NUMPAD_SUBTRACT: {
            doCommand(DCMD_ZOOM_OUT, 0);
        } break;
            /*        case WXK_UP:
            {
		doCommand( DCMD_LINEUP, 1 );
            }
            break;
        case WXK_DOWN:
            {
		doCommand( DCMD_LINEDOWN, 1 );
            }
            break;
        case WXK_PAGEUP:
            {
		doCommand( DCMD_PAGEUP, 1 );
            }
            break;
        case WXK_PAGEDOWN:
            {
		doCommand( DCMD_PAGEDOWN, 1 );
            }
            break;
        case WXK_HOME:
            {
		doCommand( DCMD_BEGIN, 0 );
            }
            break;
        case WXK_END:
            {
		doCommand( DCMD_END, 0 );
            }
            break;
*/
    }
}

bool ReaderView::LoadDocument(const wxString& fname) {
    //printf("cr3view::LoadDocument()\n");
    _renderTimer->Stop();
    _clockTimer->Stop();
    CloseDocument();

    wxCursor hg(wxCURSOR_WAIT);
    this->SetCursor(hg);
    wxSetCursor(hg);
    //===========================================
    GetParent()->Update();
    //printf("   loading...  ");
    lString32 fname32 = wx2cr(fname);
    bool res = getDocView()->LoadDocument(fname32.c_str());
    //printf("   done. \n");
    if (!res)
        getDocView()->createDefaultDocument(lString32("File open error"), lString32("Cannot open file ") + fname32);
    lString32 title = getDocView()->getAuthors();
    if (!title.empty() && !getDocView()->getTitle().empty())
        title << U". ";
    title << getDocView()->getTitle();
    GetParent()->SetLabel(cr2wx(title));

    ApplyDocLang();
    getDocView()->restorePosition();

    ScheduleRender();
    GetParent()->SetFocus();
    //===========================================
    wxSetCursor(wxNullCursor);
    this->SetCursor(wxNullCursor);
    return res;
}

void ReaderView::goToBookmark(ldomXPointer bm) {
    getDocView()->goToBookmark(bm);
    doPaint();
}

void ReaderView::SetRotate(cr_rotate_angle_t angle) {
    _props->setInt(PROP_ROTATE_ANGLE, angle);
    ApplyProps();
}

void ReaderView::Rotate(bool ccw) {
    int angle = (getDocView()->GetRotateAngle() + 4 + (ccw ? -1 : 1)) & 3;
    SetRotate((cr_rotate_angle_t)angle);
}

void ReaderView::doCommand(LVDocCmd cmd, int param) {
    _docwin->onCommand(cmd, param);
    doPaint();
}

void ReaderView::doResize(int dx, int dy) {
    //printf("   Resize(%d,%d) \n", dx, dy );
    if (dx == 0 && dy == 0) {
        GetClientSize(&dx, &dy);
    }
    if (getDocView()->IsRendered() && getDocView()->GetWidth() == dx && getDocView()->GetHeight() == dy) {
        Refresh(false);
        return; // no resize
    }
    if (dx < 5 || dy < 5 || dx > 10000 || dy > 10000) {
        return;
    }

    _renderTimer->Stop();
    _renderTimer->Start(100, wxTIMER_ONE_SHOT);
    _clockTimer->Stop();
    _clockTimer->Start(10 * 1000, wxTIMER_CONTINUOUS);

    if (_isFullscreen) {
        // Scheduled to hide the cursor after 3 seconds.
        _cursorTimer->Stop();
        _cursorTimer->Start(3 * 1000, wxTIMER_ONE_SHOT);
    }
    // Reset window's cursor to default
    SetCursor(wxNullCursor);
}

void ReaderView::OnPaint(wxPaintEvent& event) {
    //printf("   OnPaint()  \n" );
    wxPaintDC dc(this);

    int dx, dy;
    GetClientSize(&dx, &dy);
    if (!getDocView()->IsRendered())
        getDocView()->Resize(dx, dy);
    if (getDocView()->GetWidth() != dx || getDocView()->GetHeight() != dy) {
        wxBitmap bmp = _screen.getWxBitmap();
        if (bmp.IsOk())
            dc.DrawBitmap(bmp, 0, 0, false);
        doResize(dx, dy);
        return;
    }

    // draw
    _wm.update(true);
    wxBitmap bmp = _screen.getWxBitmap();
    dc.DrawBitmap(bmp, 0, 0, false);

    // Update the scrollbar only after the current page has rendered.
    UpdateScrollBar();
}

void ReaderView::OnSize(wxSizeEvent& event) {
    int width, height;
    GetClientSize(&width, &height);
    //printf("   OnSize(%d, %d)  \n", width, height );
    doResize(width, height);
}

// LVDocViewCallback override
void ReaderView::OnExternalLink(lString32 url, ldomNode* node) {
    ::wxLaunchDefaultBrowser(cr2wx(url));
}

void ReaderView::OnLoadFileFormatDetected(doc_format_t fileFormat) {
    wxString filename = "fb2.css";
    switch (fileFormat) {
        case doc_format_txt:
            filename = "txt.css";
            break;
        case doc_format_fb3:
            filename = "fb3.css";
            break;
        case doc_format_rtf:
            filename = "rtf.css";
            break;
        case doc_format_epub:
            filename = "epub.css";
            break;
        case doc_format_html:
            filename = "htm.css";
            break;
        case doc_format_md:
            filename = "markdown.css";
            break;
        case doc_format_doc:
            filename = "doc.css";
            break;
        case doc_format_odt:
        case doc_format_docx:
            filename = "docx.css";
            break;
        case doc_format_chm:
            filename = "chm.css";
            break;
        default:
                // do nothing
                ;
    }
    CRLog::debug("CSS file to load: %s", LCSTR(wx2cr(filename)));
    loadCSS(filename);
}
