/*
   A very simple text editor window, which
   I implemented just to demo pen-text. - Ken
*/

import java.awt.*;

public class TextEditor
{
   public TextEditor() {
      clear();
   }

   // PUBLIC STATIC METHODS

   public static int stringWidth(Graphics g, String s) {
      return g.getFontMetrics(g.getFont()).stringWidth(s);
   }

   public static int fontHeight(Graphics g) {
      return g.getFontMetrics(g.getFont()).getHeight();
   }

   public static boolean isSpace(int c) { return c == ' '; }

   // PUBLIC METHODS

   public void setBounds(int w, int h) { W = w; H = h; }

   public void clear() {
      line = cursor = 0;
      for (int i = 0 ; i < msg.length ; i++)
         msg[i] = "";
   }

   public void render(Graphics g) {
      this.g = g;
      lineHeight = fontHeight(g) * 4 / 5;
      margin = lineHeight / 5;

      for (int l = msg.length-1 ; l >= 0 ; l--)
	 if (stringWidth(g, msg[l]) > W - 2*margin)
	    splitLine(l);

      g.setColor(Color.black);
      int y = lineHeight;

      for (int l = 0 ; l < msg.length ; l++) {
         int x = margin;
         int cursorX = x;
         for (int i = 0 ; i < msg[l].length() ; i++) {
            String s = "" + msg[l].charAt(i);
	    int w = stringWidth(g,s);
	    boolean isSelected = 
	       l>sl1 && l<sl2 ||
               sl1<sl2 ? l==sl1 && i>=sc1 || l==sl2 && i<sc2
                       : l==sl1 && i>=sc1 && i<sc2;
            if (isSelected) {
               g.setColor(selectedColor);
	       g.fillRect(x, y-lineHeight*7/10-1, w, lineHeight);
               g.setColor(Color.black);
            }
            g.drawString(s, x, y);
	    x += w;
	    if (l == line && i == cursor-1)
	       cursorX = x;
         }
	 if (l == line)
	    drawCursor(cursorX, y);
	 y += lineHeight;
      }
   }

   void drawCursor(int x, int y) {
      g.setColor(Color.red);
      g.fillRect(x-1, y - lineHeight * 4 / 5 + 1, 3,lineHeight-2);
      g.fillRect(x-2, y - lineHeight * 4 / 5 + 1, 5,3);
      g.fillRect(x-2, y + lineHeight     / 5 - 2, 5,3);
      g.setColor(Color.black);
   }

   public void placeCursor(int x, int y) {
      isOverText(x,y);
      if (line == xyLine && cursor == xyCursor)
         selectWord();  
      line = xyLine;
      cursor = xyCursor;
   }

   public boolean hasSelection() {
      return sl1 != sl2 || sc1 != sc2;
   }

   public void select(int x1, int y1, int x2, int y2) {
      isOverText(x1, y1); int sla = xyLine, sca = xyCursor;
      isOverText(x2, y2); int slb = xyLine, scb = xyCursor;
      if (sla == slb) {
         sl1 = sl2 = sla;
	 sc1 = Math.min(sca, scb);
	 sc2 = Math.max(sca, scb);
      }
      else if (sla < slb) {
	 sl1 = sla; sc1 = sca;
	 sl2 = slb; sc2 = scb;
      }
      else {
         sl1 = slb; sc1 = scb;
         sl2 = sla; sc2 = sca;
      }
   }

   public boolean isOverText(int x, int y) {
      xyLine = y / lineHeight;
      String m = msg[xyLine];
      if (stringWidth(g, m) > x)
         for (xyCursor = 0 ; xyCursor < m.length() ; xyCursor++)
            if (stringWidth(g, m.substring(0,xyCursor+1)) > x)
               return true;
      return stringWidth(g, m) + lineHeight / 2 > x;
   }

   public void copy() {
      copyToCutBuffer();

      sl1 = sl2 = sc1 = sc2 = 0;
   }

   public void cut() {
      copyToCutBuffer();

      msg[sl1] = msg[sl1].substring(0,sc1) +
                 msg[sl2].substring(sc2, msg[sl2].length());
      for (int l = sl2+1 ; l < msg.length ; l++)
	 msg[l-(sl2-sl1)] = msg[l];
      if (line > sl1)
         line -= sl2 - sl1;
      else if (cursor > sc1)
         cursor = sc1;

      sl1 = sl2 = sc1 = sc2 = 0;
   }

   public void paste() {
      enterText(cutBuffer);
   }

   public void enterText(String s) {
      for (int i = 0 ; i < s.length() ; i++)
         enterChar(s.charAt(i));
   }

   public String getCutBuffer() { return "" + cutBuffer; }

   // INTERNAL METHODS

   void copyToCutBuffer() {
      if (sl1 == sl2)
         cutBuffer = msg[sl1].substring(sc1,sc2);
      else {
         cutBuffer = msg[sl1].substring(sc1,msg[sl1].length()) + "\n";
         for (int l = sl1+1 ; l < sl2 ; l++)
	    cutBuffer += msg[l] + "\n";
         cutBuffer += msg[sl2].substring(0,sc2);
      }
   }

   void selectWord() {
      sl1 = line;
      for (sc1 = cursor ; sc1 >= 1 &&
                          ! isSpace(msg[sl1].charAt(sc1-1)) ; sc1--)
          ;

      sl2 = line;
      for (sc2 = cursor ; sc2 < msg[sl2].length() &&
                          ! isSpace(msg[sl2].charAt(sc2)) ; sc2++)
          ;
      for ( ; sc2 < msg[sl2].length() &&
              isSpace(msg[sl2].charAt(sc2)) ; sc2++)
          ;
   }

   void enterChar(int c) {
      switch (c) {
      case '\b': // BACKSPACE
         deleteChar();
         break;
      case '\n': // NEWLINE
         insertNewline();
	 break;
      case 1004: // UP ARROW
         cursorUp();
         break;
      case 1005: // DOWN ARROW
         cursorDown();
         break;
      case 1006: // LEFT ARROW
         cursorLeft();
         break;
      case 1007: // RIGHT ARROW
         cursorRight();
         break;
      case 27: // ESCAPE
         System.out.println("ESCAPE");
         break;
      case '\t': // TAB
         System.out.println("TAB");
         break;
      default:
         cursor = Math.min(cursor, msg[line].length());
         msg[line] = msg[line].substring(0,cursor) + (char)c +
                     msg[line].substring(cursor,msg[line].length());
         cursor++;
      }
   }

   // TODO: GET RID OF SPACE AT END OF NEW LINE

   void splitLine(int l) {
      int n = msg[l].length(), i = n-1;
      while (i > 0 &&   isSpace(msg[l].charAt(i))) i--;
      while (i > 0 && ! isSpace(msg[l].charAt(i))) i--;
      if (i > 0) {
	 for (int j = msg.length-1 ; j > l ; j--)
	    msg[j] = msg[j-1];
         msg[l+1] = msg[l].substring(i+1,n);
         msg[l  ] = msg[l].substring(0,i);
	 if (line == l) {
            line++;
	    if (cursor >= i)
               cursor -= i;
         }
	 else if (line > l)
	    line++;
      }
   }

   public void deleteWord() {
      if (currentChar() == ' ')
         deleteChar();
      while (cursor > 0 && currentChar() != ' ')
         deleteChar();
   }

   int currentChar() {
      //return cursor >= msg[line].length() ? 0 : msg[line].charAt(cursor);
      return cursor == 0 ? 0 : msg[line].charAt(cursor-1);
   }

   void deleteChar() {
      cursor = Math.min(cursor, msg[line].length());
      if (msg[line].length() > 0 && cursor > 0) {
         msg[line] = msg[line].substring(0,cursor-1) +
                     msg[line].substring(cursor, msg[line].length());
         cursor--;
      }
      else if (line > 0) {
         line--;
         cursor = msg[line].length();
	 msg[line] = msg[line] + msg[line+1];
	 for (int l = line+1 ; l < msg.length-1 ; l++)
	    msg[l] = msg[l+1];
      }
   }
   void insertNewline() {
      for (int l = msg.length-1 ; l > line ; l--)
         msg[l] = msg[l-1];
      msg[line+1] = msg[line].substring(cursor, msg[line].length());
      msg[line] = msg[line].substring(0,cursor);
      line++;
      cursor = 0;
   }
   void cursorUp() {
      if (line > 0)
         line--;
   }
   void cursorDown() {
      if (line < msg.length)
         line++;
   }
   void cursorLeft() {
      if (cursor > 0)
         cursor--;
      else if (line > 0) {
         line--;
         cursor = msg[line].length();
      }
   }
   void cursorRight() {
      if (cursor < msg[line].length())
         cursor++;
      else if (line < msg.length) {
         line++;
         cursor = 0;
      }
   }

   public String toString() {

      int lastLine = msg.length;;
      while (--lastLine > 0)
	 if (msg[lastLine].length() > 0)
	    break;

      String s = "";
      for (int i = 0 ; i <= lastLine ; i++)
	 s += msg[i] + "\n";
      return s;
   }

   String msg[] = new String[50];
   int line = 0, cursor = 0;
   String cutBuffer = "";
   Color selectedColor = new Color(200,200,200);
   Graphics g;
   int W, H;
   int sl1 = 0, sc1 = 0, sl2 = 0, sc2 = 0;
   int xyLine, xyCursor;
   int lineY[] = new int[100];
   int lineHeight, margin;
}

