/* Copyright (c) 2019-2026 hors<horsicq@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
#ifndef XHEXVIEW_H
#define XHEXVIEW_H

#include <QTextBoundaryFinder>
#include <QPixmapCache>

#include "dialoghexedit.h"
#include "xdevicetableeditview.h"

// TODO if cursor moved -> highlight location and header
// TODO modes symbols/disasm/types
class XHexView : public XDeviceTableEditView {
    Q_OBJECT

public:
    // TODO setOptions ???

    explicit XHexView(QWidget *pParent = nullptr);

    void _adjustView();
    virtual void adjustView();
    void setData(QIODevice *pDevice, const XBinaryView::OPTIONS &options, bool bReload, XInfoDB *pInfoDB = nullptr);
    void goToOffset(qint64 nOffset);
    // XADDR getStartLocation();  // TODO Check mb remove
    // XADDR getSelectionInitLocation();
    void setBytesProLine(qint32 nBytesProLine);
    virtual QList<XShortcuts::MENUITEM> getMenuItems();

private:
    enum COLUMN {
        COLUMN_LOCATION = 0,
        COLUMN_ELEMENTS,
        COLUMN_SYMBOLS
    };

    struct LOCATIONRECORD {  // TODO move
        QString sLocation;
        quint64 nViewPos;
    };

    struct SHOWRECORD {
        qint32 nRow;
        qint32 nViewPos;
        qint32 nRowViewPos;
        qint32 nSize;
        bool bFirstRowSymbol;
        bool bLastRowSymbol;
        QString sElement;
        QString sSymbol;
        bool bIsBold;
        bool bIsHighlighted;
        bool bIsSymbolError;
        QColor colBackground;
        QColor colBackgroundSelected;
        //        bool bIsSelected;
    };

    enum ELEMENT_MODE {
        ELEMENT_MODE_HEX = 0,
        ELEMENT_MODE_BYTE,
        ELEMENT_MODE_WORD,
        ELEMENT_MODE_DWORD,
        ELEMENT_MODE_QWORD,
        ELEMENT_MODE_UINT8,
        ELEMENT_MODE_INT8,
        ELEMENT_MODE_UINT16,
        ELEMENT_MODE_INT16,
        ELEMENT_MODE_UINT32,
        ELEMENT_MODE_INT32,
        ELEMENT_MODE_UINT64,
        ELEMENT_MODE_INT64,
    };

    SHOWRECORD _getShowRecordByViewPos(qint64 nOffset);

protected:
    virtual OS cursorPositionToOS(const CURSOR_POSITION &cursorPosition);
    virtual void updateData();
    virtual void paintMap(QPainter *pPainter, qint32 nLeft, qint32 nTop, qint32 nWidth, qint32 nHeight);
    virtual void paintCell(QPainter *pPainter, qint32 nRow, qint32 nColumn, qint32 nLeft, qint32 nTop, qint32 nWidth, qint32 nHeight);
    virtual void paintColumn(QPainter *pPainter, qint32 nColumn, qint32 nLeft, qint32 nTop, qint32 nWidth, qint32 nHeight);
    virtual void paintTitle(QPainter *pPainter, qint32 nColumn, qint32 nLeft, qint32 nTop, qint32 nWidth, qint32 nHeight, const QString &sTitle);
    virtual void wheelEvent(QWheelEvent *pEvent);
    virtual void keyPressEvent(QKeyEvent *pEvent);
    virtual XVPOS getCurrentViewPosFromScroll();
    virtual void setCurrentViewPosToScroll(XVPOS nOffset);
    virtual void adjustColumns();
    virtual void adjustHeader();
    virtual void _headerClicked(qint32 nColumn);
    virtual void _cellDoubleClicked(qint32 nRow, qint32 nColumn);
    virtual void adjustScrollCount();
    virtual void adjustMap();

private slots:
    void _disasmSlot();
    void _memoryMapSlot();
    void _mainHexSlot();
    void _setCodePage(const QString &sCodePage);
    void changeElementWidth();
    void changeElementMode();
    void _setMode(ELEMENT_MODE mode);
    void _addElementModeMenuItem(QList<XShortcuts::MENUITEM> *pListMenuItems, const QString &sText, ELEMENT_MODE mode);
    void _addElementWidthMenuItem(QList<XShortcuts::MENUITEM> *pListMenuItems, qint32 nWidth);
    QString _formatElement(char *pData, qint32 nOffset, qint32 nSize, const QString &sDataHexBuffer);

private:
    qint32 m_nBytesProLine;
    qint32 m_nPrintsProElement;
    qint32 m_nElementByteSize;
    qint32 m_nSymbolByteSize;
    ELEMENT_MODE m_mode;
    qint32 m_nDataBlockSize;
    QList<HIGHLIGHTREGION> m_listHighlightsRegion;
    qint32 m_nViewStartDelta;
    QByteArray m_baDataBuffer;
    QList<LOCATIONRECORD> m_listLocationRecords;
    QList<SHOWRECORD> m_listShowRecords;
    qint32 m_nAddressWidth;
    qint64 m_nThisBase;
    bool m_bIsLocationColon;

    QString m_sCodePage;
#if (QT_VERSION_MAJOR < 6) || defined(QT_CORE5COMPAT_LIB)
    QTextCodec *m_pCodec;
    QMenu *m_pCodePageMenu;
    XOptions m_xCodePageOptions;
#endif
    QPixmapCache m_pixmapCache;
};

#endif  // XHEXVIEW_H
