// render2d_text.cpp - text-editing functions in class Render2D

#include <iostream>
#include <math.h>
#include <qwidget.h>
#include <qbitmap.h>
#include <qpixmap.h>
#include <qcursor.h>
#include <qpainter.h>
#include <qfontmetrics.h>
#include <qtextstream.h>

#include "render2d.h"
#include "chemdata.h"
#include "molecule.h"
#include "text.h"
#include "defs.h"

// mouse events to handle text functions
// (button states should be set before we get here)
void Render2D::DrawText_mousePressEvent(QMouseEvent *e1) {
  text_drag = false;
  if (localtext != 0) {
    DPoint *e = new DPoint;
    e->x = e1->x(); e->y = e1->y();
    if (localtext->WithinBounds(e) == true) {
      // move cursor, select, or something...
      text_drag = true;
      start_drag = e;
    }
  }
  // do nothing, wait for release
}

void Render2D::DrawText_mouseReleaseEvent(QMouseEvent *e1) {
  if (localtext != 0) {  // if already working on something, change behavior
    DPoint *e = new DPoint;
    e->x = e1->x(); e->y = e1->y();
    if (text_drag == true) {
      double dlta = fabs((start_drag->x - e->x) + (start_drag->y - e->y));
      if (dlta < 0.5) { 
	localtext->MoveCursor(e); 
	text_drag = false;
	repaint(false); 
	return; 
      }
      // finish multiple selection
      localtext->Select(start_drag, e);
      repaint(false);
      text_drag = false;
      return;
    }
    if (localtext->WithinBounds(e) == true) {
      // move cursor, select, or something...
    } else {
      // save text, clean up
      localtext->DeselectAllText();
      emit TextOff();
      if (localtext->getMolecule() != 0)
	localtext->getMolecule()->Changed();
      if (!text_exists) {
	if (localtext->getText().length() > 0)
	  { c->addText(localtext); localtext = 0; }
      } else {
	if (localtext->getText().length() == 0) { 
	  // reset underlying point to "C" carbon
	  localtext->Start()->element = "C";
	  c->Erase(localtext); localtext = 0; 
	}
      }
      localtext = 0;
      highlightpoint = 0;
      if (highlightobject != 0) {
	highlightobject->Highlight(false);
	highlightobject = 0;
      }
      repaint(false);
    }

    double distobj;
    Drawable *no = c->FindNearestObject(e, distobj);
    if ( (no != 0) && (no->Type() == TYPE_TEXT) ) { // jump to closest text
      highlightpoint = 0;
      highlightobject = no;
    } else {
      return;
    }
  }

  if (highlightobject != 0) { // edit existing object
    text_exists = true;
    localtext = (Text *)highlightobject;
    emit TextOn(localtext->getFont());
    repaint(false);
  } else {  // create new object
    text_exists = false;
    localtext = new Text(this);
    localtext->setFont(currentFont);
    localtext->SetColor(currentColor);
    DPoint *e = new DPoint;
    e->x = e1->x(); e->y = e1->y();
    emit TextOn(localtext->getFont());
    if (highlightpoint) { 
      localtext->setPoint(highlightpoint);
      localtext->setJustify(JUSTIFY_CENTER);
    } else {
      localtext->setPoint(e);
      localtext->setJustify(JUSTIFY_TOPLEFT);
    }
    repaint(false);
  }
}

void Render2D::DrawText_mouseMoveEvent(QMouseEvent *e1) {
  //bool update;
  DPoint *prevhighlight = highlightpoint;
  Drawable *prevhighlightobject = highlightobject;
  // Create DPoint of current pointer position
  DPoint *e = new DPoint;
  DPoint *np = 0;
  e->x = e1->x(); e->y = e1->y();
  double dist, distobj;
  // Get DPoint of nearest point
  np = c->FindNearestPoint(e, dist);
  // get Drawable of nearest object
  Drawable *no = c->FindNearestObject(e, distobj);

  if (localtext != 0) {  // handle moves when there is a current object
    if (text_drag == true) {
      localtext->Select(start_drag, e);
      connect(localtext,SIGNAL(signalHasBold(bool)),this,SIGNAL(hasBold(bool)));
      connect(localtext,SIGNAL(signalHasBold(bool)),this,SIGNAL(hasItalic(bool)));
      connect(localtext,SIGNAL(signalHasUnderline(bool)),this,SIGNAL(hasUnderline(bool)));
      connect(localtext,SIGNAL(signalHasSuperscript(bool)),this,SIGNAL(hasSuperscript(bool)));
      connect(localtext,SIGNAL(signalHasSubscript(bool)),this,SIGNAL(hasSubscript(bool)));
      QFont temp=localtext->getFont();
      emit signalFont(temp.family());
      emit signalFontSize(temp.pointSize());
      repaint(false);
    }
    return;
  }

  // look for place to draw if no current text object
  if (no != 0) {
    if (no->Type() == TYPE_TEXT) { // highlight text if closest
      highlightpoint = 0;
      highlightobject = no;
      if (prevhighlightobject != 0) prevhighlightobject->Highlight(false);
      highlightobject->Highlight(true);
      if (prevhighlightobject != highlightobject) repaint(false);
      return;
    } else { // deselect and check for points
      // Clear highlighted object
      highlightobject = 0;
      if (prevhighlightobject != 0) prevhighlightobject->Highlight(false);
      if (prevhighlightobject != highlightobject) repaint(false);
    }
  }
  // clear highlighted object (if any)
  if (prevhighlightobject != 0) {
    prevhighlightobject->Highlight(false);
    highlightobject = 0;
    repaint(false);
  }
  // check points
  if (dist < 6.0) {
    highlightpoint = np;
    if (prevhighlight != highlightpoint) repaint(false);
    return;
  }
  if (dist >= 6.0) {
    // Clear highlighted point
    highlightpoint = 0;
    if (prevhighlight != highlightpoint) repaint(false);
    return;
  }
}

// Handle keypress event here, since it only really applies to text
void Render2D::keyPressEvent(QKeyEvent *k) {
  if (mode != MODE_TEXT) return;

  // discard delete key (to avoid random behavior)
  if (k->key() == Key_Delete) return;

  // if text object is active, do something.
  if (localtext != 0) {
    // if escape character pressed, save object
    if (k->key() == Key_Escape) {
      emit TextOff();
      if (localtext->getMolecule() != 0)
	localtext->getMolecule()->Changed();
      if (!text_exists) {
	if (localtext->getText().length() > 0)
	  c->addText(localtext);
      } else {
	if (localtext->getText().length() == 0) {
	  // reset underlying point to "C" carbon
	  localtext->Start()->element = "C";
	  c->Erase(localtext);
	}
      }
      localtext = 0;
      highlightpoint = 0;
      if (highlightobject != 0) {
	highlightobject->Highlight(false);
	highlightobject = 0;
      }
      repaint(false);
      return;
    }
    // if return pressed, and Justify == JUSTIFY_CENTER...
    if ( (k->key() == Key_Return) && 
	 (localtext->Justify() == JUSTIFY_CENTER) ) {
      emit TextOff();
      if (localtext->getMolecule() != 0)
	localtext->getMolecule()->Changed();
      if (!text_exists) {
	if (localtext->getText().length() > 0)
	  c->addText(localtext);
      } else {
	if (localtext->getText().length() == 0) {
	  // reset underlying point to "C" carbon
	  localtext->Start()->element = "C";
	  c->Erase(localtext);
	}
      }
      localtext = 0;
      highlightpoint = 0;
      if (highlightobject != 0) {
	highlightobject->Highlight(false);
      }      
      highlightobject = 0;  // it was deleted by ChemData::Erase().
      // ChemData etc. do this for us now.
      //if (highlightobject != 0) {
      //	highlightobject->Highlight(false);
      //	highlightobject = 0;
      //}
      repaint(false);
      return;
    }

    // if not escape, just insert.
    localtext->InsertCharacter(k);
    repaint(false);
  }
}

// Superscript selected text
void Render2D::Superscript(bool flag) {
  if (localtext != 0) {
    if(flag)
      localtext->DoSuperscript();
    else
      localtext->DoUnSuperscript();
    repaint(false);
  }
}

// Subscript selected text
void Render2D::Subscript(bool flag) {
  if (localtext != 0) {
    if(flag)
      localtext->DoSubscript();
    else
      localtext->DoUnSubscript();
    repaint(false);
  }
}

// Bold selected text
void Render2D::Bold(bool flag) {
  if (localtext != 0) {
    if(flag)
      localtext->DoBold();
    else
      localtext->DoUnBold();
    repaint(false);
  }
}

// Italicize selected text
void Render2D::Italic(bool flag) {
  if (localtext != 0) {
    if(flag)
      localtext->DoItalic();
    else
      localtext->DoUnItalic();
    repaint(false);
  }
}

// Underline selected text
void Render2D::Underline(bool flag) {
  if (localtext != 0) {
    if(flag)
      localtext->DoUnderline();
    else
      localtext->DoUnUnderline();
    repaint(false);
  }
}

// origin = origin point of text
// intext = text to draw (or blank); return if modified or not
// justify = how to arrange text around origin
// oneline = if true (i.e., a label), it's a one-line widget
QString Render2D::EditText(QPoint origin, QString intext, int justify,
			   bool oneline) 
{
  return intext;
}

int Render2D::GetTextHeight(QFont fn) {
  QPainter p(this);
  p.setFont(fn);
  QFontMetrics fm = p.fontMetrics();
  
  return fm.ascent();
}

int Render2D::GetTextFullHeight(QFont fn) {
  QPainter p(this);
  p.setFont(fn);
  QFontMetrics fm = p.fontMetrics();
  
  return fm.height();
}

int Render2D::GetCharWidth(QChar ch, QFont fn) {
  QPainter p(this);
  p.setFont(fn);
  QFontMetrics fm = p.fontMetrics();
  
  return fm.width(ch);
}

int Render2D::GetStringWidth(QString ch, QFont fn) {
  QPainter p(this);
  p.setFont(fn);
  QFontMetrics fm = p.fontMetrics();
  
  return fm.width(ch);
}

QRect Render2D::GetTextDimensions(QString txt, QFont fn) {
  QRect retval;

  // if empty, return box large enough to hold cursor and indicate place.
  if (txt.isNull() || txt.isEmpty()) {
    
  }

  QPainter p(this);
  p.setFont(fn);
  int maxwidth, lwidth, linecount, height;
  QFontMetrics fm = p.fontMetrics();
  QTextStream t(txt, IO_ReadOnly);

  linecount = 1;
  maxwidth = 0;
  QString l;
  // find maximum width
  do {
    l = t.readLine();
    lwidth = fm.width(l);
    if (lwidth > maxwidth) maxwidth = lwidth;
  } while (!t.atEnd());
  // find height
  for (int i = 0; i < txt.length(); i++) {
    if ((int)QChar(txt[i]) == 10) linecount++;
  }
  if (linecount < 1) linecount = 1;
  if (maxwidth < 5) maxwidth = 5;
  height = linecount * fm.height();

  retval.setWidth(maxwidth);
  retval.setHeight(height);

  return retval;
}
