// Copyright (c) Warwick Allison, 1999.
// Qt4 conversion copyright (c) Ray Chason, 2012-2014.
// NetHack may be freely redistributed.  See license for details.

// qt_map.cpp -- the map window

extern "C" {
#include "hack.h"

extern glyph_map glyphmap[MAX_GLYPH]; /* from tile.c */
}

#include "qt_pre.h"
#include <QtGui/QtGui>
#if QT_VERSION >= 0x050000
#include <QtWidgets/QtWidgets>
#endif
#include "qt_post.h"
#include "qt_map.h"
#include "qt_map.moc"
#include "qt_click.h"
#include "qt_glyph.h"
#include "qt_set.h"
#include "qt_str.h"

// pet- and pile-mark xpm arrays moved out of qt_xpms.h so that we don't
// include it here anymore; including that header in two files resulted in
// two copies of all the static xpm data and all the rest is for qt_stat.cpp
//
/* XPM */
static const char *pet_mark_xpm[] = {
/* width height ncolors chars_per_pixel */
"8 7 2 1",
/* colors */
". c None",
"  c #FF0000",
/* pixels */
"........",
"..  .  .",
".       ",
".       ",
"..     .",
"...   ..",
".... ..."
};
/* XPM */
static const char *pet_mark_small_xpm[] = {
/* width height ncolors chars_per_pixel */
"5 5 2 1",
/* colors */
". c None",
"X c #FF0000",
/* pixels */
".X.X.",
"XXXXX",
".XXX.",
"..X.."
};
/* XPM */
static const char *pile_mark_xpm[] = {
/* width height ncolors chars_per_pixel */
"5 5 2 1",
/* colors */
". c None",
"X c #00FF00",
/* pixels */
"..X..",
"..X..",
"XXXXX",
"..X..",
"..X.."
};

// temporary
extern int qt_compact_mode;
// end temporary

namespace nethack_qt_ {

#ifdef TEXTCOLOR
static const QPen nhcolor_to_pen(uint32_t c)
{
    static QPen *pen = (QPen *) 0;
    if (!pen) {
        pen = new QPen[17];
        //
        // FIXME:  these are duplicated in qt_menu.cpp
        //
        pen[ 0] = QColor(64, 64, 64);    // black
        pen[ 1] = QColor(Qt::red);
        pen[ 2] = QColor(0, 191, 0);     // green
        pen[ 3] = QColor(127, 127, 0);   // brownish
        pen[ 4] = QColor(Qt::blue);
        pen[ 5] = QColor(Qt::magenta);
        pen[ 6] = QColor(Qt::cyan);
        pen[ 7] = QColor(Qt::gray);
        // on tty, "light" variations are "bright" instead; here they're paler
        pen[ 8] = QColor(Qt::white);     // no color
        pen[ 9] = QColor(255, 127, 0);   // orange
        pen[10] = QColor(127, 255, 127); // light green
        pen[11] = QColor(Qt::yellow);
        pen[12] = QColor(127, 127, 255); // light blue
        pen[13] = QColor(255, 127, 255); // light magenta
        pen[14] = QColor(127, 255, 255); // light cyan
        pen[15] = QColor(Qt::white);
        // ? out of range for 0..15
        pen[16] = QColor(Qt::black);
    }

#ifdef ENHANCED_SYMBOLS
    if (c & 0x80000000) {
        return QColor(
            (c >> 16) & 0xFF,
            (c >>  8) & 0xFF,
            (c >>  0) & 0xFF);
    } else
#endif
    {
        return pen[c];
    }
}
#endif

NetHackQtMapViewport::NetHackQtMapViewport(NetHackQtClickBuffer& click_sink) :
    QWidget(NULL),
    rogue_font(NULL),
    clicksink(click_sink),
    change(10)
{
    pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm
                                             : pet_mark_xpm);
    pile_annotation = QPixmap(pile_mark_xpm);

    Clear(); // initializes glyph[][], glyphttychar, glyphcolor, glyphflags
    cursor.setX(0);
    cursor.setY(0);
}

NetHackQtMapViewport::~NetHackQtMapViewport(void)
{
    delete rogue_font; //rogue_font = NULL;
}

// pick a font to use for text map; rogue level is always displayed as text
void NetHackQtMapViewport::SetupTextmapFont(QPainter &painter)
{
    QString fontfamily = iflags.wc_font_map ? iflags.wc_font_map
                                            : "Monospace";
    int maybebold = QFont::Normal;
    if (fontfamily.right(5).toLower() == "-bold") {
        fontfamily.truncate(fontfamily.length() - 5);
        maybebold = QFont::Bold;
    }
    // Find font...
    int pts = 5;
    while (pts < 32) {
        QFont f(fontfamily, pts, maybebold);
        painter.setFont(QFont(fontfamily, pts));
        QFontMetrics fm = painter.fontMetrics();
        if (fm.QFM_WIDTH("M") > qt_settings->glyphs().width())
            break;
        if (fm.height() > qt_settings->glyphs().height())
            break;
        pts++;
    }
    rogue_font = new QFont(fontfamily, pts - 1);
}

void NetHackQtMapViewport::paintEvent(QPaintEvent* event)
{
    NetHackQtGlyphs &glyphs = qt_settings->glyphs();
    int gW = glyphs.width(),
        gH = glyphs.height();
    QRect area = event->rect();
    QRect garea;
    garea.setCoords(std::max(0, area.left() / gW),
                    std::max(0, area.top() / gH),
                    std::min(COLNO - 1, area.right() / gW),
                    std::min(ROWNO - 1, area.bottom() / gH));

    QPainter painter;
    painter.begin(this);

    unsigned special, tileidx;
#ifdef TEXTCOLOR
    uint32 color;
    uint32 framecolor;
#endif
    if (Is_rogue_level(&u.uz) || iflags.wc_ascii_map) {
	// You enter a VERY primitive world!

	painter.setClipRect( event->rect() ); // (normally we don't clip)
	painter.fillRect( event->rect(), Qt::black );

	if (!rogue_font)
            SetupTextmapFont(painter);
	painter.setFont(*rogue_font);

        for (int j = garea.top(); j <= garea.bottom(); j++) {
            for (int i = garea.left(); i <= garea.right(); i++) {
                char32_t ch = Glyphttychar(i, j);

                special = Glyphflags(i, j);
                if (SYMHANDLING(H_IBM)) {
                    ch = cp437(ch);
                }
#ifdef TEXTCOLOR
                color = Glyphcolor(i, j);
                painter.setPen(nhcolor_to_pen(color));
#else
                painter.setPen(Qt::green);
#endif
                if (!DrawWalls(painter, i * gW, j * gH, gW, gH, ch)) {
                    ushort utf16[3];
                    if (ch < 0x10000) {
                        utf16[0] = static_cast<ushort>(ch);
                        utf16[1] = 0;
                    } else {
                        utf16[0] = static_cast<ushort>((ch >> 10) + 0xD7C0);
                        utf16[1] = static_cast<ushort>((ch & 0x3FF) + 0xDC00);
                        utf16[2] = 0;
                    }
                    painter.drawText(i * gW, j * gH, gW, gH, Qt::AlignCenter,
                                     QString::fromUtf16(utf16));
                }
                if ((special & MG_PET) != 0 && ::iflags.hilite_pet) {
                    painter.drawPixmap(QPoint(i * gW, j * gH),
                                       pet_annotation);
                } else if ((special & MG_OBJPILE) != 0
                           && ::iflags.hilite_pile) {
                    painter.drawPixmap(QPoint(i * gW, j * gH),
                                       pile_annotation);
                }
#ifdef TEXTCOLOR
                framecolor = GlyphFramecolor(i, j);
                if (framecolor != NO_COLOR) {
                   painter.setPen(nhcolor_to_pen(framecolor));
                   painter.drawRect(i * qt_settings->glyphs().width(),
                                    j * qt_settings->glyphs().height(),
                                    qt_settings->glyphs().width() - 1,
                                    qt_settings->glyphs().height() - 1);
                }
#endif
            }
        }

        painter.setFont(font());
    } else { // tiles
        for (int j = garea.top(); j <= garea.bottom(); j++) {
            for (int i = garea.left(); i <= garea.right(); i++) {
                unsigned short g = Glyph(i, j);

                special = Glyphflags(i, j);
                tileidx = Glyphtileidx(i, j);
                glyphs.drawCell(painter, g, tileidx, i, j);

                if ((special & MG_PET) != 0 && ::iflags.hilite_pet) {
                    painter.drawPixmap(QPoint(i * gW, j * gH),
                                       pet_annotation);
                } else if ((special & MG_OBJPILE) != 0
                           && ::iflags.hilite_pile) {
                    painter.drawPixmap(QPoint(i * gW, j * gH),
                                       pile_annotation);
                }
#ifdef TEXTCOLOR
                framecolor = GlyphFramecolor(i, j);
                if (framecolor != NO_COLOR) {
                   painter.setPen(nhcolor_to_pen(framecolor));
                   painter.drawRect(i * qt_settings->glyphs().width(),
                                    j * qt_settings->glyphs().height(),
                                    qt_settings->glyphs().width() - 1,
                                    qt_settings->glyphs().height() - 1);
                }
#endif
	    }
	}
    }

    if (garea.contains(cursor)) {
	if (Is_rogue_level(&u.uz)) {
#ifdef TEXTCOLOR
	    painter.setPen( Qt::white );
#else
	    painter.setPen( Qt::green ); // REALLY primitive
#endif
	} else {
	    int hp = !Upolyd ? u.uhp : u.mh,
                hpmax = !Upolyd ? u.uhpmax :  u.mhmax,
                hp100 = hpmax ? (hp * 100 / hpmax) : 100;

            // this uses a different color scheme from hitpoint bar but has
            // the same cutoff thresholds (except for lack of separate 100%)
            if (hp100 < 10 || hp < 5)
                painter.setPen(Qt::magenta);
            else if (hp100 < 25 || hp < 10 )
                painter.setPen(Qt::red);
            else if (hp100 < 50)
                painter.setPen(QColor(0xff, 0xbf, 0x00)); // orange
            else if (hp100 < 75)
                painter.setPen(Qt::yellow);
            else
                painter.setPen(Qt::white);
	}

	painter.drawRect(cursor.x() * gW, cursor.y() * gH, gW - 1, gH - 1);
    }

#if 0
    if (area.intersects(messages_rect)) {
	painter.setPen(Qt::black);
        painter.drawText(viewport.contentsX() + 1, viewport.contentsY() + 1,
                         viewport.width(), 0,
                         (Qt::TextWordWrap | Qt::AlignTop
                          | Qt::AlignLeft | Qt::TextDontClip), messages);
	painter.setPen(Qt::white);
        painter.drawText(viewport.contentsX(), viewport.contentsY(),
                         viewport.width(), 0,
                         (Qt::TextWordWrap | Qt::AlignTop
                          | Qt::AlignLeft | Qt::TextDontClip), messages);
    }
#endif

    painter.end();
}

bool NetHackQtMapViewport::DrawWalls(QPainter& painter, int x, int y,
                                     int w, int h, unsigned short ch)
{
    enum wallbits {
	w_left      = 0x01,
	w_right     = 0x02,
	w_up        = 0x04,
	w_down      = 0x08,
	w_sq_top    = 0x10,
	w_sq_bottom = 0x20,
	w_sq_left   = 0x40,
	w_sq_right  = 0x80
    };
    unsigned linewidth;
    unsigned walls;
    int x1, y1, x2, y2, x3, y3;
 
    linewidth = ((w < h) ? w : h) / 8;
    if (linewidth == 0)
        linewidth = 1;

    // Single walls
    walls = 0;
    switch (ch) {
    case 0x2500: // box drawings light horizontal
	walls = w_left | w_right;
	break;
    case 0x2502: // box drawings light vertical
	walls = w_up | w_down;
	break;
    case 0x250C: // box drawings light down and right
	walls = w_down | w_right;
	break;
    case 0x2510: // box drawings light down and left
	walls = w_down | w_left;
	break;
    case 0x2514: // box drawings light up and right
	walls = w_up | w_right;
	break;
    case 0x2518: // box drawings light up and left
	walls = w_up | w_left;
	break;
    case 0x251C: // box drawings light vertical and right
	walls = w_up | w_down | w_right;
	break;
    case 0x2524: // box drawings light vertical and left
	walls = w_up | w_down | w_left;
	break;
    case 0x252C: // box drawings light down and horizontal
	walls = w_down | w_left | w_right;
	break;
    case 0x2534: // box drawings light up and horizontal
	walls = w_up | w_left | w_right;
	break;
    case 0x253C: // box drawings light vertical and horizontal
	walls = w_up | w_down | w_left | w_right;
	break;
    }

    if (walls != 0) {
	x1 = x + w / 2;
	switch (walls & (w_up | w_down)) {
	case w_up:
	    painter.drawLine(x1, y, x1, y + h / 2);
	    break;
	case w_down:
	    painter.drawLine(x1, y + h / 2, x1, y + h - 1);
	    break;
	case w_up | w_down:
	    painter.drawLine(x1, y, x1, y + h - 1);
	    break;
	}

	y1 = y + h / 2;
	switch (walls & (w_left | w_right)) {
	case w_left:
	    painter.drawLine(x, y1, x + w / 2, y1);
	    break;
	case w_right:
	    painter.drawLine(x + w / 2, y1, x + w - 1, y1);
	    break;
	case w_left | w_right:
	    painter.drawLine(x, y1, x + w - 1, y1);
	    break;
	}

	return true;
    }

    // Double walls
    walls = 0;
    switch (ch) {
    case 0x2550: // box drawings double horizontal
	walls = w_left | w_right | w_sq_top | w_sq_bottom;
	break;
    case 0x2551: // box drawings double vertical
	walls = w_up | w_down | w_sq_left | w_sq_right;
	break;
    case 0x2554: // box drawings double down and right
	walls = w_down | w_right | w_sq_top | w_sq_left;
	break;
    case 0x2557: // box drawings double down and left
	walls = w_down | w_left | w_sq_top | w_sq_right;
	break;
    case 0x255A: // box drawings double up and right
	walls = w_up | w_right | w_sq_bottom | w_sq_left;
	break;
    case 0x255D: // box drawings double up and left
	walls = w_up | w_left | w_sq_bottom | w_sq_right;
	break;
    case 0x2560: // box drawings double vertical and right
	walls = w_up | w_down | w_right | w_sq_left;
	break;
    case 0x2563: // box drawings double vertical and left
	walls = w_up | w_down | w_left | w_sq_right;
	break;
    case 0x2566: // box drawings double down and horizontal
	walls = w_down | w_left | w_right | w_sq_top;
	break;
    case 0x2569: // box drawings double up and horizontal
	walls = w_up | w_left | w_right | w_sq_bottom;
	break;
    case 0x256C: // box drawings double vertical and horizontal
	walls = w_up | w_down | w_left | w_right;
	break;
    }

    if (walls != 0) {
	x1 = x + w / 2 - linewidth;
	x2 = x + w / 2 + linewidth;
	x3 = x + w - 1;
	y1 = y + h / 2 - linewidth;
	y2 = y + h / 2 + linewidth;
	y3 = y + h - 1;
	if (walls & w_up) {
	    painter.drawLine(x1, y, x1, y1);
	    painter.drawLine(x2, y, x2, y1);
	}
	if (walls & w_down) {
	    painter.drawLine(x1, y2, x1, y3);
	    painter.drawLine(x2, y2, x2, y3);
	}
	if (walls & w_left) {
	    painter.drawLine(x, y1, x1, y1);
	    painter.drawLine(x, y2, x1, y2);
	}
	if (walls & w_right) {
	    painter.drawLine(x2, y1, x3, y1);
	    painter.drawLine(x2, y2, x3, y2);
	}
	if (walls & w_sq_top) {
	    painter.drawLine(x1, y1, x2, y1);
	}
	if (walls & w_sq_bottom) {
	    painter.drawLine(x1, y2, x2, y2);
	}
	if (walls & w_sq_left) {
	    painter.drawLine(x1, y1, x1, y2);
	}
	if (walls & w_sq_right) {
	    painter.drawLine(x2, y1, x2, y2);
	}

	return true;
    }

    // Solid blocks
    if (0x2591 <= ch && ch <= 0x2593) {
	unsigned shade = ch - 0x2590;
	QColor rgb(painter.pen().color());
        QColor rgb2(rgb.red() * shade / 4,
                    rgb.green() * shade / 4,
                    rgb.blue() * shade / 4);
	painter.fillRect(x, y, w, h, rgb2);
	return true;
    }

    return false;
}

void NetHackQtMapViewport::mousePressEvent(QMouseEvent* event)
{
    clicksink.Put(event->pos().x() / qt_settings->glyphs().width(),
                  event->pos().y() / qt_settings->glyphs().height(),
                  (event->button() == Qt::LeftButton) ? CLICK_1 : CLICK_2);
    qApp->exit();
}

void NetHackQtMapViewport::updateTiles()
{
    change.clear();
    change.add(0, 0, COLNO, ROWNO);
    delete rogue_font; rogue_font = NULL;
}

QSize NetHackQtMapViewport::sizeHint() const
{
    return QSize(qt_settings->glyphs().width() * COLNO,
                 qt_settings->glyphs().height() * ROWNO);
}

QSize NetHackQtMapViewport::minimumSizeHint() const
{
    return sizeHint();
}

void NetHackQtMapViewport::clickCursor()
{
    clicksink.Put(cursor.x(), cursor.y(), CLICK_1);
    qApp->exit();
}

// [re-]init map display to unexplored with no changed cells
void NetHackQtMapViewport::Clear()
{
    for (int j = 0; j < ROWNO; ++j) {
        //
        // FIXME:  map column 0 should be surpressed from being displayed
        //
        Glyph(0, j) = GLYPH_NOTHING;
        Glyphttychar(0, j) = ' ';
        Glyphcolor(0, j) = NO_COLOR;
        GlyphFramecolor(0, j) = NO_COLOR;
        Glyphflags(0, j) = 0U;
        Glyphtileidx(0, j) = ::glyphmap[GLYPH_NOTHING].tileidx;

        for (int i = 1; i < COLNO; ++i) {
            Glyph(i, j) = GLYPH_UNEXPLORED;
            Glyphttychar(i, j) = ' ';
            Glyphcolor(i, j) = NO_COLOR;
            GlyphFramecolor(i, j) = NO_COLOR;
            Glyphflags(i, j) = 0U;
            Glyphtileidx(i, j) = ::glyphmap[GLYPH_UNEXPLORED].tileidx;
        }
    }

    change.clear();
    change.add(0, 0, COLNO, ROWNO);
}

void NetHackQtMapViewport::Display(bool block)
{
    int gW = qt_settings->glyphs().width(),
        gH = qt_settings->glyphs().height();

    for (int i = 0; i < change.clusters(); i++) {
	const QRect& chg = change[i];
	repaint(chg.x() * gW, chg.y() * gH,
                chg.width() * gW, chg.height() * gH);
    }
    change.clear();

    if (block) {
	yn_function("Press a key when done viewing", NULL, '\0', TRUE);
    }
}

void NetHackQtMapViewport::CursorTo(int x,int y)
{
    Changed(cursor.x(), cursor.y());
    cursor.setX(x);
    cursor.setY(y);
    Changed(cursor.x(), cursor.y());
}

void NetHackQtMapViewport::PrintGlyph(int x, int y,
                                      const glyph_info *glyphinfo,
                                      const glyph_info *bkglyphinfo)
{
    Glyph(x, y) = (unsigned short) glyphinfo->glyph;
    Glyphttychar(x, y) = (char32_t) glyphinfo->ttychar;
    Glyphcolor(x, y) = (uint32) glyphinfo->gm.sym.color;
    GlyphFramecolor(x, y) = (uint32) bkglyphinfo->framecolor;
#ifdef ENHANCED_SYMBOLS
    if (SYMHANDLING(H_UTF8)
        && glyphinfo->gm.u
        && glyphinfo->gm.u->utf8str) {
        Glyphttychar(x, y) = glyphinfo->gm.u->utf32ch;
        if (glyphinfo->gm.u->ucolor != 0) {
            Glyphcolor(x, y) = glyphinfo->gm.u->ucolor | 0x80000000;
        }
    }
#endif
    Glyphflags(x, y) = glyphinfo->gm.glyphflags;
    Glyphtileidx(x, y) = (unsigned short) glyphinfo->gm.tileidx;
    Changed(x, y);
}

void NetHackQtMapViewport::Changed(int x, int y)
{
    change.add(x, y);
}

NetHackQtMapWindow2::NetHackQtMapWindow2(NetHackQtClickBuffer& click_sink) :
    QScrollArea(NULL),
    m_viewport(new NetHackQtMapViewport(click_sink))
{
    QPalette palette;
    palette.setColor(backgroundRole(), Qt::black);
    setPalette(palette);

    setWidget(m_viewport);

    connect(qt_settings, SIGNAL(tilesChanged()), this, SLOT(updateTiles()));
    updateTiles();
}

void NetHackQtMapWindow2::updateTiles()
{
    NetHackQtGlyphs& glyphs = qt_settings->glyphs();
    int gW = glyphs.width();
    int gH = glyphs.height();
    // Be exactly the size we want to be - full map...
    m_viewport->resize(COLNO * gW, ROWNO * gH);

    verticalScrollBar()->setSingleStep(gH);
    verticalScrollBar()->setPageStep(gH);
    horizontalScrollBar()->setSingleStep(gW);
    horizontalScrollBar()->setPageStep(gW);

    m_viewport->updateTiles();
    Display(false);

    emit resized();
}

void NetHackQtMapWindow2::clearMessages()
{
    messages = "";
    update(messages_rect);
    messages_rect = QRect();
}

void NetHackQtMapWindow2::putMessage(int attr UNUSED, const QString& text)
{
    if (!messages.isEmpty())
	messages += "\n";
    messages += QString(text).replace(QChar(0x200B), "");
#if 0
    QFontMetrics fm = fontMetrics();
    messages_rect = fm.boundingRect(viewport.contentsX(), viewport.contentsY(),
                                    viewport.width(), 0,
                                    (Qt::TextWordWrap | Qt::AlignTop
                                     | Qt::AlignLeft | Qt::TextDontClip),
                                    messages);
    update(messages_rect);
#endif
}

void NetHackQtMapWindow2::clickCursor()
{
    m_viewport->clickCursor();
}

QWidget *NetHackQtMapWindow2::Widget()
{
    return this;
}

void NetHackQtMapWindow2::Clear()
{
    m_viewport->Clear();
}

void NetHackQtMapWindow2::Display(bool block)
{
    m_viewport->Display(block);
}

void NetHackQtMapWindow2::CursorTo(int x,int y)
{
    m_viewport->CursorTo(x, y);
}

void NetHackQtMapWindow2::PutStr(int attr UNUSED, const QString& text UNUSED)
{
    puts("unexpected PutStr in MapWindow");
}

void NetHackQtMapWindow2::ClipAround(int x,int y)
{
    // Convert to pixel of center of tile
    x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2;
    y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2;

    // Then ensure that pixel is visible
    ensureVisible(x,y,width()*0.45,height()*0.45);
}

void NetHackQtMapWindow2::PrintGlyph(int x,int y,
                                     const glyph_info *glyphinfo,
                                     const glyph_info *bkglyphinfo)
{
    m_viewport->PrintGlyph(x, y, glyphinfo, bkglyphinfo);
}

#if 0 //RLC
// XXX Hmmm... crash after saving bones file if Map window is
// XXX deleted.  Strange bug somewhere.
bool NetHackQtMapWindow::Destroy() { return false; }

NetHackQtMapWindow::NetHackQtMapWindow(NetHackQtClickBuffer& click_sink) :
    clicksink(click_sink),
    change(10),
    rogue_font(0)
{
    viewport.addChild(this);

    QPalette palette;
    palette.setColor(backgroundRole(), Qt::black);
    setPalette(palette);
    palette.setColor(viewport.backgroundRole(), Qt::black);
    viewport.setPalette(palette);

    pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm
                                             : pet_mark_xpm);
    pile_annotation = QPixmap(pile_mark_xpm);

    cursor.setX(0);
    cursor.setY(0);
    Clear();

    connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles()));
    connect(&viewport, SIGNAL(contentsMoving(int,int)), this,
		SLOT(moveMessages(int,int)));

    updateTiles();
    //setFocusPolicy(Qt::StrongFocus);
}

void NetHackQtMapWindow::moveMessages(int x, int y)
{
    QRect u = messages_rect;
    messages_rect.moveTopLeft(QPoint(x,y));
    u |= messages_rect;
    update(u);
}

void NetHackQtMapWindow::clearMessages()
{
    messages = "";
    update(messages_rect);
    messages_rect = QRect();
}

void NetHackQtMapWindow::putMessage(int attr, const QString& text)
{
    if ( !messages.isEmpty() )
	messages += "\n";
    messages += QString(text).replace(QChar(0x200B), "");
    QFontMetrics fm = fontMetrics();
    messages_rect = fm.boundingRect(viewport.contentsX(), viewport.contentsY(),
                                    viewport.width(), 0,
                                    (Qt::TextWordWrap | Qt::AlignTop
                                     | Qt::AlignLeft | Qt::TextDontClip),
                                    messages);
    update(messages_rect);
}

void NetHackQtMapWindow::updateTiles()
{
    NetHackQtGlyphs& glyphs = qt_settings->glyphs();
    int gw = glyphs.width();
    int gh = glyphs.height();
    // Be exactly the size we want to be - full map...
    resize(COLNO*gw,ROWNO*gh);

    viewport.verticalScrollBar()->setSingleStep(gh);
    viewport.verticalScrollBar()->setPageStep(gh);
    viewport.horizontalScrollBar()->setSingleStep(gw);
    viewport.horizontalScrollBar()->setPageStep(gw);
    /*
    viewport.setMaximumSize(
	gw*COLNO + viewport.verticalScrollBar()->width(),
	gh*ROWNO + viewport.horizontalScrollBar()->height()
    );
    */
    viewport.updateScrollBars();

    change.clear();
    change.add(0,0,COLNO,ROWNO);
    delete rogue_font; rogue_font = 0;
    Display(false);

    emit resized();
}

NetHackQtMapWindow::~NetHackQtMapWindow()
{
    // Remove from viewport porthole, since that is a destructible member.
    viewport.removeChild(this);
    setParent(0,0);
}

QWidget* NetHackQtMapWindow::Widget()
{
    return &viewport;
}

void NetHackQtMapWindow::Scroll(int dx, int dy)
{
    if (viewport.horizontalScrollBar()->isVisible()) {
	while (dx<0) { viewport.horizontalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); dx++; }
	while (dx>0) { viewport.horizontalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); dx--; }
    }
    if (viewport.verticalScrollBar()->isVisible()) {
	while (dy<0) { viewport.verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); dy++; }
	while (dy>0) { viewport.verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); dy--; }
    }
}

void NetHackQtMapWindow::Clear()
{
    for (int j = 0; j < ROWNO; ++j) {
        Glyph(0, j) = GLYPH_NOTHING;
        Glyphcolor(0, j) = NO_COLOR;
        GlyphFramecolor(0, j) = NO_COLOR;
        Glyphttychar(0, j) = ' ';
        Glyphflags(0, j) = 0;
        Glyphtileidx(0, j) = ::glyphmap[GLYPH_NOTHING].tileidx;

        for (int i = 1; i < COLNO; ++i) {
            Glyph(i, j) = GLYPH_UNEXPLORED;
            Glyphcolor(i, j) = NO_COLOR;
            GlyphFramecolor(i, j) = NO_COLOR;
            Glyphttychar(i, j) = ' ';
            Glyphflags(i, j) = 0;
            Glyphtileidx(i, j) = ::glyphmap[GLYPH_UNEXPLORED].tileidx;
        }
    }

    change.clear();
    change.add(0,0,COLNO,ROWNO);
}

void NetHackQtMapWindow::clickCursor()
{
    clicksink.Put(cursor.x(),cursor.y(),CLICK_1);
    qApp->exit();
}

void NetHackQtMapWindow::mousePressEvent(QMouseEvent* event)
{
    clicksink.Put(
	event->pos().x()/qt_settings->glyphs().width(),
	event->pos().y()/qt_settings->glyphs().height(),
	event->button()==Qt::LeftButton ? CLICK_1 : CLICK_2
    );
    qApp->exit();
}

void NetHackQtMapWindow::paintEvent(QPaintEvent* event)
{
    QRect area=event->rect();
    QRect garea;
    garea.setCoords(
	std::max(0,area.left()/qt_settings->glyphs().width()),
	std::max(0,area.top()/qt_settings->glyphs().height()),
	std::min(COLNO-1,area.right()/qt_settings->glyphs().width()),
	std::min(ROWNO-1,area.bottom()/qt_settings->glyphs().height())
    );

    QPainter painter;

    painter.begin(this);

    if (is_rogue_level(&u.uz) || iflags.wc_ascii_map) {
	// You enter a VERY primitive world!

	painter.setClipRect( event->rect() ); // (normally we don't clip)
	painter.fillRect( event->rect(), Qt::black );

	if ( !rogue_font ) {
	    // Find font...
	    int pts = 5;
	    QString fontfamily = iflags.wc_font_map
		? iflags.wc_font_map : "Courier";
	    bool bold = false;
	    if ( fontfamily.right(5).toLower() == "-bold" ) {
		fontfamily.truncate(fontfamily.length()-5);
		bold = true;
	    }
	    while ( pts < 32 ) {
		QFont f(fontfamily, pts, bold ? QFont::Bold : QFont::Normal);
		painter.setFont(QFont(fontfamily, pts));
		QFontMetrics fm = painter.fontMetrics();
		if ( fm.width("M") > qt_settings->glyphs().width() )
		    break;
		if ( fm.height() > qt_settings->glyphs().height() )
		    break;
		pts++;
	    }
	    rogue_font = new QFont(fontfamily,pts-1);
	}
	painter.setFont(*rogue_font);

	for (int j=garea.top(); j<=garea.bottom(); j++) {
	    for (int i=garea.left(); i<=garea.right(); i++) {
		unsigned short g=Glyph(i,j);
		uint32 color = Glyphcolor(i,j);
                uint32 framecolor = GlyphFramecolor(i,j);
		char32_t ch = Glyphttychar(i,j);
		unsigned special = Glyphflags(i,j);

                if (framecolor != NO_COLOR) {
                    painter.fillRect(i*qt_settings->glyphs().width(),
                                     j*qt_settings->glyphs().height(),
                                     qt_settings->glyphs().width()-1,
                                     qt_settings->glyphs().height()-1,
                                     nhcolor_to_pen(framecolor).color());
                }               
		painter.setPen( Qt::green );
#ifdef TEXTCOLOR
		painter.setPen( nhcolor_to_pen(color) );
#endif
		painter.drawText(
		    i*qt_settings->glyphs().width(),
		    j*qt_settings->glyphs().height(),
		    qt_settings->glyphs().width(),
		    qt_settings->glyphs().height(),
		    Qt::AlignCenter,
		    QString(QChar(ch)).left(1)
		);
#ifdef TEXTCOLOR
                if ((special & MG_PET) != 0 && ::iflags.hilite_pet) {
                    painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(),
                                             j*qt_settings->glyphs().height()),
                                       pet_annotation);
                } else if ((special & MG_OBJPILE) != 0
                           && ::iflags.hilite_pile) {
                    painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(),
                                             j*qt_settings->glyphs().height()),
                                       pile_annotation);
                }
#endif
                if (framecolor != NO_COLOR) {
                    painter.setPen( nhcolor_to_pen(framecolor) );
                    painter.drawRect(i*qt_settings->glyphs().width(),
                                     j*qt_settings->glyphs().height(),
                                     qt_settings->glyphs().width()-1,
                                     qt_settings->glyphs().height()-1);
                }
            }
        }

        painter.setFont(font());
    } else {
	for (int j=garea.top(); j<=garea.bottom(); j++) {
	    for (int i=garea.left(); i<=garea.right(); i++) {
		unsigned short g=Glyph(i,j);
		unsigned special = Glyphflags(i,j);
		unsigned short tileidx = Glyphtileidx(i,j);
                uint32 framecolor = GlyphFramecolor(i,j);

                qt_settings->glyphs().drawCell(painter, g, tileidx, i, j);
#ifdef TEXTCOLOR
                if ((special & MG_PET) != 0 && ::iflags.hilite_pet) {
                    painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(),
                                             j*qt_settings->glyphs().height()),
                                       pet_annotation);
                } else if ((special & MG_OBJPILE) != 0
                           && ::iflags.hilite_pile) {
                    painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(),
                                             j*qt_settings->glyphs().height()),
                                       pile_annotation);
                }
#endif
                if (framecolor != NO_COLOR) {
                    painter.setPen( nhcolor_to_pen(framecolor) );
                    painter.drawRect(i*qt_settings->glyphs().width(),
                                     j*qt_settings->glyphs().height(),
                                     qt_settings->glyphs().width()-1,
                                     qt_settings->glyphs().height()-1);
                }
	    }
	}
    }

    if (garea.contains(cursor)) {
	if (Is_rogue_level(&u.uz)) {
#ifdef TEXTCOLOR
	    painter.setPen( Qt::white );
#else
	    painter.setPen( Qt::green ); // REALLY primitive
#endif
	} else
	{
	    int hp100;
	    if (Upolyd) {
		hp100=u.mhmax ? u.mh*100/u.mhmax : 100;
	    } else {
		hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100;
	    }

            if (hp100 > 75)
                painter.setPen(Qt::white);
            else if (hp100 > 50)
                painter.setPen(Qt::yellow);
            else if (hp100 > 25)
                painter.setPen(QColor(0xff, 0xbf, 0x00)); // orange
            else if (hp100 > 10)
                painter.setPen(Qt::red);
            else
                painter.setPen(Qt::magenta);
	}

        painter.drawRect(cursor.x() * qt_settings->glyphs().width(),
                         cursor.y() * qt_settings->glyphs().height(),
                         qt_settings->glyphs().width() - 1,
                         qt_settings->glyphs().height() - 1);
    }

    if (area.intersects(messages_rect)) {
	painter.setPen(Qt::black);
        painter.drawText(viewport.contentsX() + 1, viewport.contentsY() + 1,
                         viewport.width(), 0,
                         (Qt::TextWordWrap | Qt::AlignTop
                          | Qt::AlignLeft | Qt::TextDontClip), messages);
	painter.setPen(Qt::white);
        painter.drawText(viewport.contentsX(), viewport.contentsY(),
                         viewport.width(), 0,
                         (Qt::TextWordWrap | Qt::AlignTop
                          | Qt::AlignLeft | Qt::TextDontClip), messages);
    }

    painter.end();
}

void NetHackQtMapWindow::Display(bool block)
{
    for (int i=0; i<change.clusters(); i++) {
	const QRect& ch=change[i];
	repaint(
	    ch.x()*qt_settings->glyphs().width(),
	    ch.y()*qt_settings->glyphs().height(),
	    ch.width()*qt_settings->glyphs().width(),
	    ch.height()*qt_settings->glyphs().height()
	);
    }

    change.clear();

    if (block) {
	yn_function("Press a key when done viewing",0,'\0');
    }
}

void NetHackQtMapWindow::CursorTo(int x,int y)
{
    Changed(cursor.x(),cursor.y());
    cursor.setX(x);
    cursor.setY(y);
    Changed(cursor.x(),cursor.y());
}

void NetHackQtMapWindow::PutStr(int attr, const QString& text)
{
    puts("unexpected PutStr in MapWindow");
}

void NetHackQtMapWindow::ClipAround(int x,int y)
{
    // Convert to pixel of center of tile
    x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2;
    y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2;

    // Then ensure that pixel is visible
    viewport.center(x,y,0.45,0.45);
}

void NetHackQtMapWindow::PrintGlyph(int x,int y, const glyph_info *glyphinfo)
{
    Glyph(x,y)=glyphinfo->glyph;
    Glyphttychar(x,y)=glyphinfo->ttychar;
    Glyphcolor(x,y)=glyphinfo->color;
    GlyphFramecolor(x,y)=bkglyphinfo->framecolor;
#ifdef ENHANCED_SYMBOLS
    if (SYMHANDLING(H_UTF8)
        && glyphinfo->gm.u
        && glyphinfo->gm.u->utf8str) {
        Glyphttychar(x, y) = glyphinfo->gm.u->utf32ch;
        if (glyphinfo->gm.u->ucolor != 0) {
            Glyphcolor(x, y) = glyphinfo->gm.u->ucolor | 0x80000000;
        }
    }
#endif
    Glyphflags(x,y)=glyphinfo->glyphflags;
    Glyphtileidx(x,y)=glyphinfo->tileidx;
    Changed(x,y);
}

//void NetHackQtMapWindow::PrintGlyphCompose(int x,int y,int glyph1, int glyph2)
//{
    // TODO: composed graphics
//}

void NetHackQtMapWindow::Changed(int x, int y)
{
    change.add(x,y);
}
#endif /* 0 //RLC */

} // namespace nethack_qt_
