/** 
 *  Yudit Unicode Editor Source File
 *
 *  GNU Copyright (C) 1997-2023  Gaspar Sinai <gaspar@yudit.org>  
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License, version 2,
 *  dated June 1991. See file COPYYING for details.
 *
 *  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 2 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 FITNES 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, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "swidget/SYuditInput.h"

/**
 * SYuditInput is receiving events from STextEdit
 * It insert glyphs underlined into editorID and 
 * when composition finished it sends it to the editor, after that 
 * it may put half finished compsitions in the buffer.
 * @param name is the input name.
 * @param _eif is the editor interface to modify text.
 * @param ed is the editor.
 */
SYuditInput::SYuditInput (const SString& name, SEditorIF* _eif, SEditor* ed):
     encoder (name)
{
  eif = _eif;
  editor = ed;
  startDirection = SS_DR_L;
}

/**
 * Delete SYuditInput.
 * for normal operaztion use clear before deleting.
 */
SYuditInput::~SYuditInput ()
{
}

/**
 * If SYuditInput is present and working, STextEdit sends the
 * events here first for processing. 
 */
void
SYuditInput::keyPressed (SWindowListener::SKey key, const SString& _s,
            bool ctrl, bool shift, bool meta)
{ 

  SString s = _s;
  if (!eif->isEditable())
  {
    clear();
    editor->keyPressed (key, s, ctrl, shift, meta);
  }
  switch (key)
  {
  case SWindowListener::Key_BackSpace:
  case SWindowListener::Key_Delete:
   if (startIndex.getTextIndex()!=endIndex.getTextIndex())
   {
     SString pre = encoder.preEditBuffer();
     SV_UCS4 pb = encoder.postEditBuffer();
     almostClear();
     (void) encoder.decode ("", false); /* clear */
     SString post = encoder.encode (pb);
     SString a = encoder.encode (SV_UCS4());
     if (a.size()) post.append (a);
     if (pre.size()) post.append (pre);
     if (post.size() > 0) post.truncate(post.size()-1);
     s = post;
   }
   else
   {
     editor->keyPressed (key, s, ctrl, shift, meta);
     return;
   }
   break;
  /* Do not clear - we might need to press shift! */
  case SWindowListener::Key_Shift_R:
  case SWindowListener::Key_Shift_L:
   if (startIndex.getTextIndex()==endIndex.getTextIndex())
   {
     editor->keyPressed (key, s, ctrl, shift, meta);
   }
   break;
  case SWindowListener::Key_Control_R:
  case SWindowListener::Key_Control_L:
  case SWindowListener::Key_Meta_R:
  case SWindowListener::Key_Meta_L:
  case SWindowListener::Key_Alt_R:
  case SWindowListener::Key_Alt_L:
  case SWindowListener::Key_Escape:
  case SWindowListener::Key_Home:
  case SWindowListener::Key_End:
  case SWindowListener::Key_Prior:
  case SWindowListener::Key_Next:
  case SWindowListener::Key_Up:
  case SWindowListener::Key_Down:
  case SWindowListener::Key_Left:
  case SWindowListener::Key_Right:
  case SWindowListener::Key_Enter:
  case SWindowListener::Key_Return:
  case SWindowListener::Key_F1:
  case SWindowListener::Key_F2:
  case SWindowListener::Key_F3:
  case SWindowListener::Key_F4:
  case SWindowListener::Key_F5:
  case SWindowListener::Key_F6:
  case SWindowListener::Key_F7:
  case SWindowListener::Key_F8:
  case SWindowListener::Key_F9:
  case SWindowListener::Key_F10:
  case SWindowListener::Key_F11:
  case SWindowListener::Key_F12:
    if (startIndex.getTextIndex()!=endIndex.getTextIndex())
    {
      clear();
    }
    editor->keyPressed (key, s, ctrl, shift, meta);
    return;
  default:
    if (s.size()==0 && !(ctrl || meta)) return;
    if (ctrl || meta)
    {
      if (startIndex.getTextIndex()!=endIndex.getTextIndex()) clear();
      editor->keyPressed (key, s, ctrl, shift, meta); 
      return;
    }
  }
  if (s.size()==0) return;
  SString remaining = s;
  SV_UCS4 pbefore = encoder.postEditBuffer();
  SV_UCS4 u;
  // input is not ascii.
  if (s.size() > 1 && (s[0] &0x80) == 0x80) {
    SEncoder utf8;
    u = encoder.decode("", false);
    u.append (utf8.decode(s));
  } else {
    u = encoder.decode (s);
  }
  SV_UCS4 pafter = encoder.postEditBuffer();
  SV_UCS4 pedit;
  /* we just append ? */
  if (u.size() > 0 || pafter.size() != pbefore.size())
  {
    remaining = encoder.preEditBuffer();
    pedit = pafter;
    almostClear ();
    SEncoder utf8;
    editor->keyPressed (SWindowListener::Key_Send, utf8.encode(u), 
           false, false, false); 
  }
  /* process glyphs here */
  if (startIndex.getTextIndex()==endIndex.getTextIndex())
  {
    startIndex = eif->getCursorIndex();
    startDirection = eif->getDirection();
    endIndex = startIndex;
  }
  if (pedit.size()) 
  {
    SEncoder utf8;
    eif->insertPreEditText (utf8.encode (pedit), SPreEditor::Style_Default);
    endIndex = eif->getCursorIndex();
  }
  if (remaining.size())
  {
    eif->insertPreEditText (remaining, SPreEditor::Style_Default);
    endIndex = eif->getCursorIndex();
  }
  preEditSize = remaining.size() + pedit.size();
}

/**
 * clear the input text.
 * @param tosend is true if the remaining characters should
 * be sent out.
 * @return true if it had a text.
 */
bool
SYuditInput::clear(bool tosend)
{
  SString toSend;
  if (preEditSize)
  {
    /* flush */
    SV_UCS4 u = encoder.decode ("", false);
    if (u.size() > 0)
    {
      SEncoder utf8;
      toSend = utf8.encode(u);
    }
  }
  /* make it pre-emptive */
  encoder = SEncoder(encoder.getName());
  almostClear();
  if (toSend.size())
  {
     if (tosend)
     {
       editor->keyPressed (SWindowListener::Key_Send, toSend, 
           false, false, false); 
     }
     return true;
  }
  return false;
}

/**
 * clear it.
 */
void
SYuditInput::almostClear()
{
  if (startIndex.getTextIndex() == endIndex.getTextIndex())
  {
    startIndex = SCursorIndex (0, 0);
    endIndex = startIndex;
    preEditSize = 0;
    return;
  }
  /* Make it pre-emptive */
  SCursorIndex s = startIndex;
  SCursorIndex e = endIndex;

  startIndex = SCursorIndex (0, 0);
  endIndex = startIndex;
  preEditSize = 0;

  eif->endSelect ();
  eif->setCursorIndex (s);
  eif->eraseText (e.getTextIndex());
//fprintf (stderr, "setCursor\n");
  eif->setCursorIndex (s);
//fprintf (stderr, "setDirection\n");
  eif->setDirection (startDirection);
  return;
}

/**
 * return true if input method works fine.
 */
bool
SYuditInput::isOK()
{
  return encoder.isOK();
}

/**
 * Try to reverse encode the input with the current keymap
 */
SString
SYuditInput::encode (const SV_UCS4& v) const
{
  SString ret;
  if (v.size()==0) return SString(ret);
  SEncoder e = encoder;
  ret = e.encode (v);
  /* flush */
  SV_UCS4 empty;
  ret.append (e.encode (empty));

  /* check it against input */
  SV_UCS4 check = e.decode (ret, true);
  SString es;
  check.append (e.decode (es, false));
  bool tryagain = false;
  if (v.size() != check.size())
  {
    tryagain = true;
  }
  else for (unsigned int i=0; i<v.size(); i++)
  {
    if (v[i] != check[i])
    {
      tryagain = true;
      break;
    }
  }
  if (tryagain)
  {
    /* try with a space */
    check = e.decode (ret, true);
    check.append (e.decode (SString(" "), false));
    if (check.size() > v.size() && check.size() > 1 
      && check[check.size()-1] == (SS_UCS4)' ')
    {
      check.truncate (check.size()-1); 
    }
    if (v.size() != check.size())
    {
      ret.clear();
    }
    else for (unsigned int i=0; i<v.size(); i++)
    {
      if (v[i] != check[i])
      {
        ret.clear();
        break;
      }
    }
  }
  return SString (ret);
}
