// XDrawChem
// Copyright (C) 2004-2005  Bryan Herger <bherger@users.sourceforge.net>
// Copyright (C) 2020  Yaman Qalieh <ybq987@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 3 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, see <https://www.gnu.org/licenses/>.

// text.cpp - Text's implementation of functions

// (obsolete note!)
// NOTE ABOUT TEXTMASK: + marks superscript, - marks subscript
// B marks bold, I marks italic, U marks underline

#include <QKeyEvent>

#include "defs.h"
#include "drawable.h"
#include "render2d.h"
#include "text.h"

// lastflag settings

Text::Text(Render2D *r1, QObject *parent) : Drawable(parent) {
    r = r1;

    highlighted = false;
    shiftdown = false;
    cursor = 0;
    selectMin = -1;
    selectMax = -1;
    molecule = 0;
    tjustify = TEXT_AUTO;
    whichside = 1;
    DataType = TEXT_DATA_NORMAL;
    shape = 0;
    shapewidth = -1;
    shapeheight = -1;
    oshapewidth = -1;
    oshapeheight = -1;
    drawBorder = false;
    drawFill = false;
    borderColor = QColor(0, 0, 0);
    fillColor = QColor(0, 255, 255);
}

Text *Text::CloneTo(Drawable *target) const {
    if (!target)
        target = new Text(r, parent());

    Text *t = static_cast<Text *>(this->Drawable::CloneTo(target));
    t->displayText = displayText;
    t->displayTextMask = displayTextMask;
    t->whichside = whichside;
    t->font = font;
    t->justify = justify;
    t->tjustify = tjustify;
    t->BBox = BBox;
    t->cursor = cursor;
    t->selectMin = selectMin;
    t->selectMax = selectMax;
    t->shiftdown = shiftdown;
    t->DataType = DataType;
    t->shape = shape;
    t->drawBorder = drawBorder;
    t->drawFill = drawFill;
    t->borderColor = borderColor;
    t->fillColor = fillColor;
    t->shapewidth = shapewidth;
    t->shapeheight = shapeheight;
    t->oshapewidth = oshapewidth;
    t->oshapeheight = oshapeheight;
    // t->molecule = molecule;
    return t;
}

QString Text::ToXML(QString xml_id) {
    QString s, n1;
    int fsize;

    fsize = font.pointSize();
    if (fsize < 1)
        fsize = font.pixelSize();

    // begin text
    s.append("<text id=");
    s.append(xml_id);
    s.append(">\n");

    // write Start
    s.append("<Start>");
    n1.setNum(start->x);
    s.append(n1);
    s.append(" ");
    n1.setNum(start->y);
    s.append(n1);
    s.append("</Start>\n");

    // write text
    s.append("<textstring>");
    s.append(start->element);
    s.append("</textstring>\n");

    // write textmask
    s.append("<richstring>");
    s.append(displayText);
    s.append("</richstring>\n");

    // shape info
    s.append("<shape>");
    n1.setNum(shape);
    s.append(n1);
    s.append("</shape>\n");

    // border info
    s.append("<border>");
    if (drawBorder == true)
        s.append("true");
    else
        s.append("false");
    s.append("</border>\n<bordercolor>");
    n1.setNum(borderColor.red());
    s.append(n1);
    s.append(" ");
    n1.setNum(borderColor.green());
    s.append(n1);
    s.append(" ");
    n1.setNum(borderColor.blue());
    s.append(n1);
    s.append("</bordercolor>\n");

    // fill info
    s.append("<fill>");
    if (drawFill == true)
        s.append("true");
    else
        s.append("false");
    s.append("</fill>\n<fillcolor>");
    n1.setNum(fillColor.red());
    s.append(n1);
    s.append(" ");
    n1.setNum(fillColor.green());
    s.append(n1);
    s.append(" ");
    n1.setNum(fillColor.blue());
    s.append(n1);
    s.append("</fillcolor>\n");

    // shape dimensions
    s.append("<shapegeometry>");
    n1.setNum(shapewidth);
    s.append(n1);
    s.append(" ");
    n1.setNum(shapeheight);
    s.append(n1);
    s.append(" ");
    n1.setNum(oshapewidth);
    s.append(n1);
    s.append(" ");
    n1.setNum(oshapeheight);
    s.append(n1);
    s.append("</shapegeometry>\n");

    // end text
    s.append("</text>\n");

    return s;
}

QString Text::ToCDXML(QString xml_id) {
    QString s, n1;

    // begin text
    s.append("<t id=\"");
    s.append(xml_id);
    s.append("\" p=\"");
    n1.setNum(start->x);
    s.append(n1);
    s.append(" ");
    n1.setNum(start->y);
    s.append(n1);
    s.append("\"><s font=\"21\" size=\"10\" face=\"96\">");
    s.append(start->element);
    s.append("</s></t>\n");

    return s;
}

void Text::FromXML(QString xml_tag) {
    int i1, i2;

    i1 = xml_tag.indexOf("<Start>");
    i2 = xml_tag.indexOf("</Start>") + 8;
    SetStartFromXML(xml_tag.mid(i1, i2 - i1));
    i1 = xml_tag.indexOf("<color>");
    if (i1 >= 0) {
        i2 = xml_tag.indexOf("</color>") + 8;
        SetColorFromXML(xml_tag.mid(i1, i2 - i1));
    }
    i1 = xml_tag.indexOf("<font>");
    if (i1 >= 0) {
        i2 = xml_tag.indexOf("</font>") + 7;
        SetFontFromXML(xml_tag.mid(i1, i2 - i1));
    }
    i1 = xml_tag.indexOf("<textstring>");
    i2 = xml_tag.indexOf("</textstring>") + 13;
    SetTextstringFromXML(xml_tag.mid(i1, i2 - i1));
    i1 = xml_tag.indexOf("<richstring>");
    i2 = xml_tag.indexOf("</richstring>") + 13;
    SetRichstringFromXML(xml_tag.mid(i1, i2 - i1));
    i1 = xml_tag.indexOf("<textmask>");
    i2 = xml_tag.indexOf("</textmask>") + 11;
    SetTextmaskFromXML(xml_tag.mid(i1, i2 - i1));
    i1 = xml_tag.indexOf("<shape>");
    if (i1 > 0) {
        i2 = xml_tag.indexOf("</shape>");
        shape = xml_tag.mid(i1 + 7, i2 - i1 - 7).toInt();
        qDebug() << "shape:" << shape;
    }
    i1 = xml_tag.indexOf("<border>");
    if (i1 > 0) {
        i2 = xml_tag.indexOf("</border>");
        qDebug() << "border:" << xml_tag.mid(i1 + 8, 4);
        if (xml_tag.mid(i1 + 8, 4) == "true")
            drawBorder = true;
        else
            drawBorder = false;
    }
    i1 = xml_tag.indexOf("<fill>");
    if (i1 > 0) {
        i2 = xml_tag.indexOf("</fill>");
        qDebug() << "fill:" << xml_tag.mid(i1 + 6, 4);
        if (xml_tag.mid(i1 + 6, 4) == "true")
            drawFill = true;
        else
            drawFill = false;
    }
    i1 = xml_tag.indexOf("<bordercolor>");
    if (i1 >= 0) {
        i2 = xml_tag.indexOf("</bordercolor>") + 14;
        SetBorderColorFromXML(xml_tag.mid(i1, i2 - i1));
    }
    i1 = xml_tag.indexOf("<fillcolor>");
    if (i1 >= 0) {
        i2 = xml_tag.indexOf("</fillcolor>") + 12;
        SetFillColorFromXML(xml_tag.mid(i1, i2 - i1));
    }
    i1 = xml_tag.indexOf("<shapegeometry>");
    if (i1 >= 0) {
        i2 = xml_tag.indexOf("</shapegeometry>") + 16;
        SetShapeGeometryFromXML(xml_tag.mid(i1, i2 - i1));
    }
}

void Text::SetBorderColorFromXML(QString xml_tag) {
    qDebug() << "SetBColorFromXML:" << xml_tag;
    int i1, i2;
    int d1, d2, d3;

    i1 = xml_tag.indexOf("<bordercolor>");
    i2 = xml_tag.indexOf("</bordercolor>");
    xml_tag.remove(i2, 999);
    xml_tag.remove(i1, 13);

    QTextStream ts(&xml_tag, QIODevice::ReadOnly);

    ts >> d1 >> d2 >> d3;

    borderColor = QColor(d1, d2, d3);
}

void Text::SetFillColorFromXML(QString xml_tag) {
    qDebug() << "SetFColorFromXML:" << xml_tag;
    int i1, i2;
    int d1, d2, d3;

    i1 = xml_tag.indexOf("<fillcolor>");
    i2 = xml_tag.indexOf("</fillcolor>");
    xml_tag.remove(i2, 999);
    xml_tag.remove(i1, 11);

    QTextStream ts(&xml_tag, QIODevice::ReadOnly);

    ts >> d1 >> d2 >> d3;

    fillColor = QColor(d1, d2, d3);
}

void Text::SetShapeGeometryFromXML(QString xml_tag) {
    qDebug() << "SetSGFromXML:" << xml_tag;
    int i1, i2;
    int d1, d2, d3, d4;

    i1 = xml_tag.indexOf("<shapegeometry>");
    i2 = xml_tag.indexOf("</shapegeometry>");
    xml_tag.remove(i2, 999);
    xml_tag.remove(i1, 15);

    QTextStream ts(&xml_tag, QIODevice::ReadOnly);

    ts >> d1 >> d2 >> d3 >> d4;

    shapewidth = d1;
    shapeheight = d2;
    oshapewidth = d3;
    oshapeheight = d4;
}

// convert XML <font> tag to QFont and set current
void Text::SetFontFromXML(QString xml_tag) {
    qDebug() << "SetFontFromXML:" << xml_tag;
    int i1, i2;

    // int d1, d2, d3;

    i1 = xml_tag.indexOf("<font>");
    i2 = xml_tag.indexOf("</font>");
    xml_tag.remove(i2, 999);
    xml_tag.remove(i1, 6);
    i1 = xml_tag.indexOf("#");
    qDebug() << xml_tag.mid(0, i1) << "*" << xml_tag.mid(i1 + 1);
    font = QFont(xml_tag.mid(0, i1), xml_tag.mid(i1 + 1).toInt());
}

// get Text::text from <textstring> tag
void Text::SetTextstringFromXML(QString xml_tag) {
    qDebug() << "SetTextstringFromXML:" << xml_tag;
    int i1, i2;

    // int d1, d2, d3;

    i1 = xml_tag.indexOf("<textstring>");
    i2 = xml_tag.indexOf("</textstring>");
    xml_tag.remove(i2, 999);
    xml_tag.remove(i1, 12);
    if (start != 0)
        start->element = xml_tag;
}

// get Text::displayText from <richstring> tag
void Text::SetRichstringFromXML(QString xml_tag) {
    qDebug() << "SetRichstringFromXML:" << xml_tag;
    int i1, i2;

    // int d1, d2, d3;

    i1 = xml_tag.indexOf("<richstring>");
    i2 = xml_tag.indexOf("</richstring>");
    xml_tag.remove(i2, 999);
    xml_tag.remove(i1, 12);
    if (start != 0)
        displayText = xml_tag;
}

// get Text::textmask from <textmask> tag
void Text::SetTextmaskFromXML(QString xml_tag) {
    // not used!!!  either <richstring> or <elementmask> tag used to set display

    // qDebug() << "SetTextmaskFromXML:" << xml_tag ;

    // obsolete tag!  silently update...
    // note that user may need to reformat text
    // displayText = start->element;
    // displayText.replace("\n", "</p>");

    /*
       int i1, i2;
       //int d1, d2, d3;

       i1 = xml_tag.indexOf("<textmask>");
       i2 = xml_tag.indexOf("</textmask>");
       xml_tag.remove(i2, 999);
       xml_tag.remove(i1, 10);
       if (start != 0)
       start->elementmask = xml_tag;
     */
}

bool Text::isWithinRect(QRect n, bool shiftdown) {
    if (DPointInRect(start, n))
        highlighted = true;
    else
        highlighted = false;
    return highlighted;
}

void Text::Render() {
    qDebug() << "Text::Render begin";
    //  r->drawString( "foobar", start->toQPoint(), QColor(0,0,0), QFont() );
    //  return;
    qDebug() << "displayText.length():" << displayText.length();

    bool superFlag = false, subFlag = false;
    QFont currentFont = font;
    QColor currentColor = color;

    if (r->getFontKludge()) {
        currentFont.setPointSize(currentFont.pointSize() * 2);
        qDebug() << "fontkludge (top)";
    }

    QStringList parseList, renderList, lineWidths;

    int lineheight = 0, linewidth = 0, textheight = 0, textwidth = 0;
    int cp1, lp1, lp2;
    QString n1, currentElement = QString();
    bool flag1 = false;

    for (cp1 = 0; cp1 < displayText.length(); cp1++) {
        flag1 = true;
        if (displayText[cp1] == '<') {
            parseList.append(currentElement);
            currentElement = "<";
            flag1 = false;
        }
        if (displayText[cp1] == '>') {
            currentElement.append(displayText[cp1]);
            if (!currentElement.contains("!DOCTYPE")) {
                parseList.append(currentElement);
            }
            currentElement = "";
            flag1 = false;
        }
        if (flag1) {
            currentElement.append(displayText[cp1]);
        }
    }

    // just in case there were no tags...
    if (currentElement.length() > 0)
        parseList.append(currentElement);

    // mock render to determine width and height
    QRect b1;
    int maxfont = 0, thisfont;
    bool styletag = false;

    for (QStringList::Iterator it = parseList.begin(); it != parseList.end(); ++it) {
        currentElement = *it;
        // qDebug() << "currentElement:" << currentElement;
        // dump the style tag!
        if (currentElement == "</style>") {
            styletag = false;
            continue;
        }
        if (styletag)
            continue;
        if (currentElement.left(6) == "<style") {
            styletag = true;
            continue;
        }

        if (currentElement == "")
            continue;
        if (currentElement == " ")
            continue;
        if (currentElement == "\n")
            continue;
        if (currentElement == "<p>")
            continue;
        if (currentElement == "<html>")
            continue;
        if (currentElement == "</html>")
            continue;
        if (currentElement == "<head>")
            continue;
        if (currentElement == "</head>")
            continue;
        if (currentElement == "</body>")
            continue;
        if (currentElement.left(3) == "<p ")
            continue;
        if (currentElement.left(6) == "<body ")
            continue;
        if (currentElement.left(6) == "<meta ")
            continue;
        if (currentElement == "</span>") {
            // this likely undoes a super/sub flag
            if (superFlag == true)
                superFlag = false;
            if (subFlag == true)
                subFlag = false;
            renderList.append(currentElement);
            continue;
        }
        currentElement.replace("&lt;", "<");
        currentElement.replace("&gt;", ">");
        renderList.append(currentElement);
        if (currentElement == "</p>") {
            n1.setNum(linewidth);
            lineWidths.append(n1);
            if (linewidth > textwidth)
                textwidth = linewidth;
            textheight += lineheight;
            linewidth = 0;
            lineheight = 0;
            continue;
        }
        if (currentElement.left(6) == "<span ") {
            // set font
            // qDebug() << "span: " << currentElement;
            superFlag = false, subFlag = false;
            if (currentElement.contains("font-family") > 0) {
                lp2 = currentElement.indexOf("font-family");
                lp1 = currentElement.indexOf(":", lp2);
                lp2 = currentElement.indexOf(";", lp1);
                currentFont.setFamily(currentElement.mid(lp1 + 1, lp2 - lp1 - 1));
            }
            if (currentElement.contains("font-size") > 0) {
                lp2 = currentElement.indexOf("font-size");
                lp1 = currentElement.indexOf(":", lp2);
                lp2 = currentElement.indexOf("pt", lp1);
                thisfont = currentElement.mid(lp1 + 1, lp2 - lp1 - 1).toInt();
                currentFont.setPointSize(thisfont);
                if (thisfont > maxfont)
                    maxfont = thisfont;
                if (r->getFontKludge() == true) {
                    currentFont.setPointSize(currentFont.pointSize() * 2);
                    qDebug() << "fontkludge";
                }
                qDebug() << "points: " << currentFont.pointSize();
            }
            if (currentElement.contains("font-weight") > 0) {
                currentFont.setBold(true);
            }
            if (currentElement.contains("italic") > 0) {
                currentFont.setItalic(true);
            }
            if (currentElement.contains("underline") > 0) {
                currentFont.setUnderline(true);
            }
            if (currentElement.contains("super") > 0) {
                superFlag = true;
            }
            if (currentElement.contains("sub") > 0) {
                subFlag = true;
            }
            continue;
        }
        b1 = r->GetTextDimensions(currentElement, currentFont);
        if (b1.height() > lineheight)
            lineheight = b1.height();
        linewidth += b1.width();
    }
    // end for

    if (lineheight > textheight)
        textheight = lineheight;
    if (linewidth > textwidth)
        textwidth = linewidth;

    qDebug() << displayText;
    qDebug() << "Text dimensions = " << textwidth << "," << textheight;
    // qDebug() << "Text check 1 ---";

    int supersub, subx;
    QFont supersubfont;
    QColor drawcolor;

    if (highlighted)
        drawcolor = QColor(255, 0, 0);
    else
        drawcolor = color;
    QRect b(0, 0, textwidth, textheight);
    QPoint t = GetTopLeftPoint();

    b.translate(t.x(), t.y());
    // this guarantees that the text will not overlap other objects
    // original code
    r->drawFillBox(b.topLeft(), b.bottomRight(), r->getBGColor(), false, QColor(0, 0, 0), 1);

    // draw shape, if applicable
    QPoint shapetl, shapebr, textcenter;

    oshapewidth = b.width() + maxfont;
    oshapeheight = b.height() + maxfont;
    textcenter.setX(b.left() + (b.width() / 2));
    textcenter.setY(b.top() + (b.height() / 2));
    int lsw, lsh;

    if (shapewidth < 0) {
        lsw = oshapewidth;
        lsh = oshapeheight;
    } else {
        lsw = shapewidth;
        lsh = shapeheight;
    }
    if (shape > 0) {
        shapetl.setX(textcenter.x() - (lsw / 2));
        shapebr.setX(textcenter.x() + (lsw / 2));
        shapetl.setY(textcenter.y() - (lsh / 2));
        shapebr.setY(textcenter.y() + (lsh / 2));
        if (shape == TEXT_SHAPE_RECTANGLE) {
            if (drawFill) {
                r->drawFillBox(shapetl, shapebr, fillColor, drawBorder, borderColor, 0);
            } else {
                r->drawBox(shapetl, shapebr, borderColor);
            }
        }
        if (shape == TEXT_SHAPE_SQUARE) {
            if (drawFill) {
                r->drawFillBox(shapetl, shapebr, fillColor, drawBorder, borderColor, 0);
            } else {
                r->drawBox(shapetl, shapebr, borderColor);
            }
        }
        if (shape == TEXT_SHAPE_ELLIPSE) {
            r->drawEllipse(shapetl, shapebr, drawBorder, borderColor, drawFill, fillColor);
        }
        if (shape == TEXT_SHAPE_CIRCLE) {
            r->drawEllipse(shapetl, shapebr, drawBorder, borderColor, drawFill, fillColor);
        }
    }
    // right-align behavior is different for stand-alone text
    QTextStream dts(&displayTextMask, QIODevice::ReadOnly);
    QString displayLine;
    int in1 = 0;

    // displayLine = dts.readLine();
    // in1 = r->GetStringWidth(displayLine, font);
    // qDebug() << b.width() << ":" << in1 ;
    // in1 = lineWidths[0].toInt();
    in1 = b.width() - in1;
    // bool switchpos = false;
    QPoint curpos(t.x(), t.y() + r->GetTextHeight(font));

    if ((tjustify == TEXT_RALIGN) && (justify == JUSTIFY_TOPLEFT))
        curpos.setX(curpos.x() + in1 + 2);
    if ((tjustify == TEXT_CALIGN) && (justify == JUSTIFY_TOPLEFT))
        curpos.setX(curpos.x() + (in1 / 2) + 2);
    bool smaller_font = false;
    QPoint startpos = curpos;

    // actual rendering section
    // qDebug() << "Text check 2 ---";

    int lw1 = 1;

    r->resetTextOrigin();
    foreach (currentElement, renderList) {
        if (currentElement == "</span>") {
            superFlag = false, subFlag = false;
            continue;
        }
        if (currentElement == "</p>") {
            superFlag = false, subFlag = false;
            currentFont.setBold(false);
            currentFont.setItalic(false);
            currentFont.setUnderline(false);
            if (justify == JUSTIFY_CENTER)
                continue;
            r->resetTextOrigin();
            // displayLine = dts.readLine();
            // in1 = r->GetStringWidth(displayLine, font);
            // qDebug() << b.width() << ":" << in1 ;
            if (lw1 < lineWidths.size()) {
                in1 = lineWidths[lw1].toInt();
                lw1++;
                in1 = b.width() - in1;
                curpos.setX(t.x());
                if ((tjustify == TEXT_RALIGN) && (justify == JUSTIFY_TOPLEFT))
                    curpos.setX(curpos.x() + in1 + 2);
                if ((tjustify == TEXT_CALIGN) && (justify == JUSTIFY_TOPLEFT))
                    curpos.setX(curpos.x() + (in1 / 2) + 2);
                curpos.setY(curpos.y() + r->GetTextFullHeight(font));
            }
            continue;
        }

        if (currentElement.left(6) == "<span ") {
            // set font
            // qDebug() << "span: " << currentElement;
            superFlag = false, subFlag = false;
            if (currentElement.contains("font-family") > 0) {
                lp2 = currentElement.indexOf("font-family");
                lp1 = currentElement.indexOf(":", lp2);
                lp2 = currentElement.indexOf(";", lp1);
                currentFont.setFamily(currentElement.mid(lp1 + 1, lp2 - lp1 - 1));
            }
            if (currentElement.contains("font-size") > 0) {
                lp2 = currentElement.indexOf("font-size");
                lp1 = currentElement.indexOf(":", lp2);
                lp2 = currentElement.indexOf("pt", lp1);
                currentFont.setPointSize(currentElement.mid(lp1 + 1, lp2 - lp1 - 1).toInt());
                if (r->getFontKludge() == true) {
                    currentFont.setPointSize(currentFont.pointSize() * 2);
                    qDebug() << "fontkludge";
                }
            }
            currentFont.setBold(false);
            currentFont.setItalic(false);
            currentFont.setUnderline(false);
            if (currentElement.contains("font-weight") > 0) {
                currentFont.setBold(true);
            }
            if (currentElement.contains("italic") > 0) {
                currentFont.setItalic(true);
            }
            if (currentElement.contains("underline") > 0) {
                currentFont.setUnderline(true);
            }
            if (currentElement.contains("super") > 0) {
                superFlag = true;
            }
            if (currentElement.contains("sub") > 0) {
                subFlag = true;
            }
            currentColor = QColor(0, 0, 0);
            if (currentElement.contains("color") > 0) {
                lp2 = currentElement.indexOf("color");
                lp1 = currentElement.indexOf(":", lp2);
                currentColor.setNamedColor(currentElement.mid(lp1 + 1, 7));
            }
            continue;
        }

        supersub = r->GetTextHeight(currentFont) / 2;
        supersubfont = subscriptFont(currentFont);
        subx = r->GetTextHeight(supersubfont) / 2;

        if (highlighted)
            currentColor = QColor(255, 0, 0);

        if (superFlag) {
            r->drawString(currentElement, QPoint(curpos.x(), curpos.y() - supersub), currentColor,
                          supersubfont);
            smaller_font = true;
        }
        if (subFlag) {
            r->drawString(currentElement, QPoint(curpos.x(), curpos.y() + subx), currentColor,
                          supersubfont);
            smaller_font = true;
        }
        if (smaller_font == true) {
            curpos.setX(curpos.x() + r->GetStringWidth(currentElement, supersubfont));
        } else {
            r->drawString(currentElement, curpos, currentColor, currentFont);
            curpos.setX(curpos.x() + r->GetStringWidth(currentElement, currentFont));
        }
        smaller_font = false;
    }
    qDebug() << "Text::Render end";
}

int Text::Type() { return TYPE_TEXT; }

bool Text::Find(DPoint *target) {
    // if (start == target) return true;
    return false;
}

DPoint *Text::FindNearestPoint(DPoint *target, double &dist) {
    dist = 99999.0;
    return 0;
}

Drawable *Text::FindNearestObject(DPoint *target, double &dist) {
    if (WithinBounds(target))
        dist = 0.01;
    else
        dist = 99999.0;
    return this;
}

void Text::setPoint(DPoint *s) { start = s; }

bool Text::WithinBounds(DPoint *target) {
    QRect b = r->GetTextDimensions(start->element, font);
    QPoint t = GetTopLeftPoint();

    b.translate(t.x(), t.y());
    if ((target->x > b.left()) && (target->x < b.right()) && (target->y > b.top()) &&
        (target->y < b.bottom()))
        return true;
    else
        return false;
}

QRect Text::BoundingBox() {
    if (highlighted == false)
        return QRect(QPoint(999, 999), QPoint(0, 0));

    QTextDocument localtextdocument;
    localtextdocument.setHtml(start->element);
    QString tmpline = localtextdocument.toPlainText();

    qInfo() << "Text::BoundingBox:" << start->element << "||" << tmpline;

    // QRect b = r->GetTextDimensions( start->element, font );
    QRect b = r->GetTextDimensions(tmpline, font);
    QPoint t = GetTopLeftPoint();

    b.translate(t.x(), t.y());
    return b;
}

// return nearest center point to m (see ChemData::AutoLayout)
QPoint Text::NearestCenter(QPoint m, int di, int &ns) {
    QRect b = r->GetTextDimensions(start->element, font);
    QPoint t = GetTopLeftPoint();

    b.translate(t.x(), t.y());
    QPoint c1, cmin;
    double dist = 9999.0, ndist;

    if (di == ARROW_VERTICAL) {
        c1.setX(b.left());
        c1.setY(b.center().y());
        ndist = DistanceBetween(m, c1);
        if (ndist < dist) {
            cmin = c1;
            dist = ndist;
            ns = TEXT_LEFT;
        }
        c1.setX(b.right());
        c1.setY(b.center().y());
        ndist = DistanceBetween(m, c1);
        if (ndist < dist) {
            cmin = c1;
            dist = ndist;
            ns = TEXT_RIGHT;
        }
    } else { // ARROW_HORIZONTAL
        c1.setX(b.center().x());
        c1.setY(b.top());
        ndist = DistanceBetween(m, c1);
        if (ndist < dist) {
            cmin = c1;
            dist = ndist;
            ns = TEXT_TOP;
        }
        c1.setX(b.center().x());
        c1.setY(b.bottom());
        ndist = DistanceBetween(m, c1);
        if (ndist < dist) {
            cmin = c1;
            dist = ndist;
            ns = TEXT_BOTTOM;
        }
    }
    return cmin;
}

void Text::DeleteKeyPressed() {
    qDebug() << "Delete key";
    start->element.remove(cursor, 1);
    start->elementmask.remove(cursor, 1);
}

void Text::InsertCharacter(QKeyEvent *k1) {
    // if shift pressed, start new selection
    if (k1->modifiers() == Qt::ShiftModifier) {
        if (shiftdown == false) {
            shiftdown = true;
            selectMin = cursor;
            selectMax = cursor - 1;
            qDebug() << cursor << "-" << selectMin << "-" << selectMax;
        } else {
            shiftdown = true;
        }
    } else {
        shiftdown = false;
    }

    // if return pressed, add newline only if JUSTIFY_TOPLEFT (not a label)
    if ((k1->key() == Qt::Key_Return) && (justify == JUSTIFY_CENTER))
        return;

    if (k1->key() == Qt::Key_Return) {
        qDebug() << "Return";
        start->element.insert(cursor, (char)10);
        // start->element = text;
        start->elementmask.insert(cursor, (char)10);
        cursor++;
        return;
    }
    // if left or right arrow pressed with shift, make or extend new selection
    if ((k1->key() == Qt::Key_Left) && shiftdown) {
        qDebug() << cursor << "-" << selectMin << "-" << selectMax;
        if (selectMin > 0) {
            if (cursor > selectMin)
                selectMax--;
            else
                selectMin--;
        }
        if (cursor > 0)
            cursor--;
        return;
    }
    if ((k1->key() == Qt::Key_Right) && shiftdown) {
        qDebug() << cursor << "-" << selectMin << "-" << selectMax;
        if (selectMax < start->element.length()) {
            if (cursor <= selectMax)
                selectMin++;
            else
                selectMax++;
        }
        if (cursor < start->element.length())
            cursor++;
        return;
    }
    // if Home or End pressed with shift, make or extend new selection
    if ((k1->key() == Qt::Key_Home) && shiftdown) {
        qDebug() << cursor << "-" << selectMin << "-" << selectMax;
        if (selectMin > 0) {
            if (cursor > selectMin)
                selectMax--;
            else
                selectMin = 0;
        }
        if (cursor > 0)
            cursor = 0;
        return;
    }
    if ((k1->key() == Qt::Key_End) && shiftdown) {
        qDebug() << cursor << "-" << selectMin << "-" << selectMax;
        if (selectMax < start->element.length()) {
            if (cursor <= selectMax)
                selectMin++;
            else
                selectMax = start->element.length();
        }
        if (cursor < start->element.length())
            cursor = start->element.length();
        return;
    }
    // if left or right arrow pressed w/o shift, clear selection
    if (k1->key() == Qt::Key_Left) {
        if (cursor > 0)
            cursor--;
        selectMin = -1;
        selectMax = -1;
        return;
    }
    if (k1->key() == Qt::Key_Right) {
        if (cursor < start->element.length())
            cursor++;
        selectMin = -1;
        selectMax = -1;
        return;
    }
    // if Home or End was pressed, go home or end
    if (k1->key() == Qt::Key_Home) {
        if (cursor > 0)
            cursor = 0;
        selectMin = -1;
        selectMax = -1;
        return;
    }
    if (k1->key() == Qt::Key_End) {
        if (cursor < start->element.length())
            cursor = start->element.length();
        selectMin = -1;
        selectMax = -1;
        return;
    }
    if (k1->key() == Qt::Key_Backspace) {
        if (cursor == 0)
            return;
        if ((selectMin >= 0) && (selectMax >= 1)) { // something selected
            start->element.remove(selectMin, selectMax - selectMin + 1);
            start->elementmask.remove(selectMin, selectMax - selectMin + 1);
            // start->element = text;
            cursor = selectMin;
            selectMin = -1, selectMax = -1;
            return;
        }
        start->element.remove(cursor - 1, 1);
        // start->element = text;
        start->elementmask.remove(cursor - 1, 1);
        cursor--;
        return;
    }
    // if key > 96 and not already handled, ignore (letters/numbers/space < 96)
    // note that this breaks international layouts and is probably not needed
    // if (k1->key() > 96) return;
    //
    // this is a more useful test in a Unicode world
    // qDebug() << "Key text:" << k1->text() << ":";
    QChar kc1 = k1->text().at(0);

    if (kc1.isPrint() == false)
        return;
    // qDebug() << kc1 << "(key accepted)";

    // regular letter/number pressed
    start->element.insert(cursor, k1->text());
    // start->element = text;
    start->elementmask.insert(cursor, QChar(' '));
    cursor++;
}

void Text::InsertString(QString pt) {
    start->element.insert(cursor, pt);
    pt.fill(' ');
    start->elementmask.insert(cursor, pt);
    cursor += pt.length();
}

// Superscript selected text
void Text::DoSuperscript() {
    if (selectMin < 0)
        return;
    for (int i = selectMin; i < selectMax + 1; i++) {
        if (start->elementmask[i] == ' ') {
            start->elementmask[i] = '+';
            continue;
        }
        if (start->elementmask[i] == '+') {
            start->elementmask[i] = ' ';
            continue;
        }
        if (start->elementmask[i] == '-') {
            start->elementmask[i] = '+';
            continue;
        }
    }
}

// Subscript selected text
void Text::DoSubscript() {
    if (selectMin < 0)
        return;
    for (int i = selectMin; i < selectMax + 1; i++) {
        if (start->elementmask[i] == ' ') {
            start->elementmask[i] = '-';
            continue;
        }
        if (start->elementmask[i] == '+') {
            start->elementmask[i] = '-';
            continue;
        }
        if (start->elementmask[i] == '-') {
            start->elementmask[i] = ' ';
            continue;
        }
    }
}

// Bold selected text
void Text::DoBold() {
    if (selectMin < 0)
        return;
    for (int i = selectMin; i < selectMax + 1; i++) {
        if (start->elementmask[i] == ' ') {
            start->elementmask[i] = 'B';
            continue;
        }
        if (start->elementmask[i] == 'B') {
            start->elementmask[i] = ' ';
            continue;
        }
    }
}

// Italicize selected text
void Text::DoItalic() {
    if (selectMin < 0)
        return;
    for (int i = selectMin; i < selectMax + 1; i++) {
        if (start->elementmask[i] == ' ') {
            start->elementmask[i] = 'I';
            continue;
        }
        if (start->elementmask[i] == 'I') {
            start->elementmask[i] = ' ';
            continue;
        }
    }
}

// Underline selected text
void Text::DoUnderline() {
    if (selectMin < 0)
        return;
    for (int i = selectMin; i < selectMax + 1; i++) {
        if (start->elementmask[i] == ' ') {
            start->elementmask[i] = 'U';
            continue;
        }
        if (start->elementmask[i] == 'U') {
            start->elementmask[i] = ' ';
            continue;
        }
    }
}

// move cursor to target
void Text::MoveCursor(DPoint *target) {
    selectMin = -1;
    selectMax = -1;
    double mindist = 99999.0, ldist;
    int newcur;
    DPoint *e = new DPoint;

    qDebug() << "Move";
    if (WithinBounds(target) == false)
        return;
    QPoint t = GetTopLeftPoint();
    int lx = t.x(), ly = t.y() + (r->GetTextFullHeight(font) / 2);

    for (int i = 0; i < start->element.length(); i++) {
        e->x = lx;
        e->y = ly;
        ldist = e->distanceTo(target);
        if (ldist < mindist) {
            mindist = ldist;
            newcur = i;
        }
        if (QChar(start->element[i]).digitValue() == 10) {
            lx = t.x();
            ly = ly + r->GetTextFullHeight(font);
        } else {
            lx = lx + r->GetCharWidth(start->element[i], font);
        }
    }
    cursor = newcur;
    delete e;
}

// Select text between endpoints e1 and e2
void Text::Select(DPoint *e1, DPoint *e2) {
    if (WithinBounds(e1) == false)
        return;
    if (WithinBounds(e2) == false)
        return;
    qDebug() << "select";
    double mindist = 99999.0, ldist;
    int newcur;
    int cr1 = 0, cr2 = 0;
    QPoint t;
    int i, lx, ly;
    DPoint *e = new DPoint;

    t = GetTopLeftPoint();
    lx = t.x();
    ly = t.y() + (r->GetTextFullHeight(font) / 2);
    for (i = 0; i < start->element.length(); i++) {
        e->x = lx;
        e->y = ly;
        ldist = e->distanceTo(e1);
        if (ldist < mindist) {
            mindist = ldist;
            newcur = i;
        }
        if (QChar(start->element[i]).digitValue() == 10) {
            cr1++;
            lx = t.x();
            ly = ly + r->GetTextFullHeight(font);
        } else {
            lx = lx + r->GetCharWidth(start->element[i], font);
        }
    }
    selectMin = newcur;
    mindist = 99999.0;
    t = GetTopLeftPoint();
    lx = t.x();
    ly = t.y() + (r->GetTextFullHeight(font) / 2);
    for (i = 0; i < start->element.length(); i++) {
        e->x = lx;
        e->y = ly;
        ldist = e->distanceTo(e2);
        if (ldist < mindist) {
            mindist = ldist;
            newcur = i;
        }
        if (QChar(start->element[i]).digitValue() == 10) {
            cr2++;
            lx = t.x();
            ly = ly + r->GetTextFullHeight(font);
        } else {
            lx = lx + r->GetCharWidth(start->element[i], font);
        }
    }
    selectMax = newcur;
    if (selectMin > selectMax) {
        int swp = selectMin;

        selectMin = selectMax;
        selectMax = swp - 1;
    }
    qDebug() << selectMin << " " << selectMax;
    delete e;
}

QPoint Text::GetTopLeftPoint() {
    bool leftcenter = true;

    if (justify == JUSTIFY_TOPLEFT) {
        return start->toQPoint();
    } else {
        QPoint a;
        QTextDocument doc;
        doc.setHtml(start->element);
        QString plaintext = doc.toPlainText();

        QRect b = r->GetTextDimensions(plaintext, font);

        // center on first or last character.  Try to guess what user intended
        // (a dangerous idea at best :)
        if ((plaintext.left(1) == "H") && (plaintext.length() > 1))
            leftcenter = false;
        if ((plaintext.left(1) == "+") && (plaintext.length() > 1))
            leftcenter = false;
        if ((plaintext.left(1) == "-") && (plaintext.length() > 1))
            leftcenter = false;
        if (plaintext.at(0).isNumber() == true)
            leftcenter = false;
        if (whichside == 2)
            leftcenter = false;
        if (tjustify == TEXT_LALIGN)
            leftcenter = true;
        if (tjustify == TEXT_CALIGN)
            leftcenter = true;
        if (tjustify == TEXT_RALIGN)
            leftcenter = false;
        if (leftcenter) {
            int lc = r->GetCharWidth(plaintext.at(0), font);

            a.setX(qRound(start->x - lc / 2.0));
        } else {
            int rc = r->GetCharWidth(plaintext.at(plaintext.length() - 1), font);

            a.setX(qRound(start->x - b.width() + rc / 2.0));
        }
        a.setY(qRound(start->y - b.height() / 2.0));
        return a;
    }
}

// position atom labels according to hindrance
void Text::CheckAlignment(int alignside) {
    // 1 = left, 2 = right
    whichside = alignside;
    qDebug() << "whichside = " << whichside;
}

void Text::ForceAlignment(int alignside) { tjustify = alignside; }

// find next smaller font for subscripts
QFont Text::subscriptFont(QFont f_in) {
    QFont f_out = f_in;

    if (f_out.pointSize() > 1)
        f_out.setPointSize(f_out.pointSize() - 2);
    else
        f_out.setPixelSize(f_out.pixelSize() - 2);

    return f_out;
}

void Text::UpdateDisplayText() {
    return;
    // this function doesn't do anything now that we're using RichText

    // old code follows

    // use user string if justify chosen
    if (tjustify != TEXT_AUTO) {
        displayText = start->element;
        displayTextMask = start->elementmask;
        return;
    }
    // validate saved text, make sure format is 'appropriate'
    if (start->element == "MeO")
        start->element = "OMe";

    // decide how to display the text string
    if (whichside == 1) {
        // cerr << "left hindered (normal) version" ;
        displayText = start->element;
        displayTextMask = start->elementmask;
        return;
    }
    // create right-aligned version.
    // cerr << "right hindered version" ;
    if (start->element == "OMe") {
        displayText = "MeO";
        displayTextMask = "   ";
        return;
    }
    displayText = start->element;
    displayTextMask = start->elementmask;
}
