#include "vdr/remote.h"

#include "vdricqTools.h"
#include "vdricqosd.h"
#include "WndStatusbar.h"


static const char *ALLOWEDCHARS = "$ abcdefghijklmnopqrstuvwxyz0123456789-_.#~/[]{}()+*^$@";

static tKey quickKeyTable[] = { { k1,    "1-_()+?$&§\0"        }, 
  			                        { k2,    "2abc\0\0\0\0\0\0\0"	  }, 
			                          { k3,    "3def#\0\0\0\0\0\0"	  }, 
			                          { k4,    "4ghi~\0\0\0\0\0\0"	  }, 
			                          { k5,    "5jkl%\0\0\0\0\0\0"	  }, 
			                          { k6,    "6mno\0\0\0\0\0\0\0"	  }, 
			                          { k7,    "7pqrs\0\0\0\0\0\0"		}, 
			                          { k8,    "8tuv\0\0\0\0\0\0\0"	  },
			                          { k9,    "9wxyz\0\0\0\0\0\0"		}, 
			                          { k0,    "0 ,.*!;:@\0\0"	      },
			                          { kNone, NULL		                }};

static char* quickKeyInfoList[] = {"2:ABC 3:DEF 4:GHI 5:JKL 6:MNO 7:PQRS 8:TUV 9:WXYZ",
                                   "1:-_()+?$&§   3:#   4:~   5:%   0: ,.*!;:@",
                                   NULL};

// ----------- cEditField ---------------------------------------------------------------

cEditField::cEditField(int iLeft, int iTop, int iWidth, int iHeight, int iLength, cWnd *pParent, cOsd *pOsd, 
                       cStatusbarWnd *pStatusbarWnd, int iTextInputmode /*= 0*/)
{
  m_iLeft   = iLeft;
  m_iTop    = iTop;
  m_iWidth  = iWidth;
  m_iHeight = iHeight;
  m_iLength = iLength;
  if (m_iLength > MAXTEXTLENGHT)
    m_iLength = MAXTEXTLENGHT;
  m_pParent           = pParent;
  m_pOsd              = pOsd;
  m_pStatusbarWnd     = pStatusbarWnd;
  m_iTextInputmode    = iTextInputmode;
  memset(m_Value, 0 , sizeof(m_Value));
  memset(m_OutValue, 0 , sizeof(m_OutValue));
  m_iPos              = -1;
  m_bInsert           = false;
  m_bUppercase        = false;
  m_bNewchar          = true;
  m_pSelectedkey      = &quickKeyTable[10];
  m_iSelectedkeyinfo  = 0;
  m_iCursorPos        = 0;
  m_iScrollPos        = 0;
  m_iBorderColor        = clrBlue;
  m_iFocusedBorderColor = clrRed;
  m_iBGColor            = clrGray50;
  m_iTextColor          = clrWhite;
  m_pFont               = (cFont*)cFont::GetFont(fontOsd);
  m_bFocused            = false;
}

cEditField::~cEditField(void)
{
}

void cEditField::SetText(char *pText)
{
  strcpy(m_Value, pText);
  Set();
}

eOSState cEditField::ProcessKey(eKeys Key)
{
  if ((Key == kBack) && (m_iPos < 0))
    return osUnknown;

  if (Key != kBlue)
    m_iSelectedkeyinfo = 0;

  switch (Key) 
  {
  case kRed:   if (m_pSelectedkey->type != kNone) 
               {
                 SelectChar(1);
                 m_pSelectedkey = &quickKeyTable[10];
                 SetHelpKeys();
                 KeyRightPressed();
               }
               else 
               {
                 // Switch between upper- and lowercase characters
                 if (m_iPos >= 0 && (!m_bInsert || !m_bNewchar)) 
                 {
                   m_bUppercase = !m_bUppercase;
                   m_Value[m_iPos] = m_bUppercase ? toupper(m_Value[m_iPos]) : tolower(m_Value[m_iPos]);
                 }
               }
               break;
  case kGreen: if (m_pSelectedkey->type != kNone) 
               {
                 SelectChar(2);
                 m_pSelectedkey = &quickKeyTable[10];
                 SetHelpKeys();
                 KeyRightPressed();
               }
               else 
               {
                 // Toggle insert/overwrite modes
                 if (m_iPos >= 0) 
                 {
                   m_bInsert = !m_bInsert;
                   m_bNewchar = true;
                 }
                 SetHelpKeys();
               }
               break;
  case kYellow|k_Repeat:
  case kYellow: 
               if (m_pSelectedkey->type != kNone) 
               {
                 SelectChar(3);
                 m_pSelectedkey = &quickKeyTable[10];
                 SetHelpKeys();
                 KeyRightPressed();
               }
               else 
               { // Remove the character at current position; in insert mode it is the character to the right of cursor
                 if (m_iPos >= 0) 
                 {
                   if (strlen(m_Value) > 1) 
                   {
                     if (!m_bInsert || m_iPos < int(strlen(m_Value)) - 1)
                       memmove(m_Value + m_iPos, m_Value + m_iPos + 1, strlen(m_Value) - m_iPos);
                     // reduce position, if we removed the last character
                     if (m_iPos == int(strlen(m_Value)))
                       m_iPos--;
                   }
                   else if (strlen(m_Value) == 1)
                     m_Value[0] = ' '; // This is the last character in the string, replace it with a blank
                   if (isalpha(m_Value[m_iPos]))
                     m_bUppercase = isupper(m_Value[m_iPos]);
                   m_bNewchar = true;
                 }
               }
               break;
  case kBlue:  if (m_pSelectedkey->type != kNone) 
               {
                 SelectChar(4);
                 m_pSelectedkey = &quickKeyTable[10];
                 SetHelpKeys();
                 KeyRightPressed();
               }
               else 
               {
                 m_pStatusbarWnd->Message(mtInfo, tr(quickKeyInfoList[m_iSelectedkeyinfo]), 10);
                 if (quickKeyInfoList[++m_iSelectedkeyinfo] == NULL)
                   m_iSelectedkeyinfo = 0;
               }
               break;
  case kLeft|k_Repeat:
  case kLeft:  if (m_iPos > 0) 
               {
                 if (!m_bInsert || m_bNewchar)
                   m_iPos--;
                 m_bNewchar = true;
               }
               if (!m_bInsert && isalpha(m_Value[m_iPos]))
                 m_bUppercase = isupper(m_Value[m_iPos]);
               break;
  case kRight|k_Repeat:
  case kRight: KeyRightPressed();
               break;
  case kUp|k_Repeat:
  case kUp:
  case kDown|k_Repeat:
  case kDown:  if (m_iPos >= 0) 
               {
                 if (m_bInsert && m_bNewchar) 
                 {
                   // create a new character in insert mode
                   if (int(strlen(m_Value)) < m_iLength) 
                   {
                     memmove(m_Value + m_iPos + 1, m_Value + m_iPos, strlen(m_Value) - m_iPos + 1);
                     m_Value[m_iPos] = ' ';
                   }
                 }
                 if (m_bUppercase)
                   m_Value[m_iPos] = toupper(Inc(tolower(m_Value[m_iPos]), NORMALKEY(Key) == kUp));
                 else
                   m_Value[m_iPos] =         Inc(        m_Value[m_iPos],  NORMALKEY(Key) == kUp);
                 m_bNewchar = false;
               }
               break;
  case k0 ... k9:     
               if ((m_pSelectedkey->type != Key) && m_pSelectedkey != &quickKeyTable[10])
               {
                 if (m_iTextInputmode == 0)
                   m_pSelectedkey = &quickKeyTable[10];
                 else
                   KeyRightPressed();
               }

               if (m_pSelectedkey->type == kNone) 
               {
                 if ((m_iPos < 0) && (strlen(m_Value) == 0))
                   KeyRightPressed();

                 if (m_iPos >= 0) 
                 {
                   m_iSelectedchar = m_iTextInputmode == 0 ? 0 : 1 ;
                   for (m_pSelectedkey = &quickKeyTable[0]; (m_pSelectedkey->type != Key) && (m_pSelectedkey->name); m_pSelectedkey++)
                   {}

                   if (m_pSelectedkey->name) 
                   {
                     m_pSelectedkey->name = (char*)I18nTranslate(m_pSelectedkey->name, NULL);  
                     if (m_bInsert && m_bNewchar) 
                     {
                       // create a new character in insert mode
                       if (int(strlen(m_Value)) < m_iLength) 
                       {
                         memmove(m_Value + m_iPos + 1, m_Value + m_iPos, strlen(m_Value) - m_iPos + 1);
                         m_Value[m_iPos] = ' ';
                       }
                     }
                     SelectChar(m_iSelectedchar);
                   }
                 }
               }
               else 
               {
                 if (++m_iSelectedchar >= (int)strlen(m_pSelectedkey->name))
                   m_iSelectedchar = 0;

                 SelectChar(m_iSelectedchar);
               }
               SetHelpKeys();
               break;
  case kOk:    if (m_pSelectedkey->type != kNone) 
               {
                 m_pSelectedkey = &quickKeyTable[10];
                 SetHelpKeys();
                 KeyRightPressed();
               }
               else 
               {
                 if (m_iPos >= 0) 
                 {
                   m_iPos = -1;
                   m_bNewchar = true;
                   stripspace(m_Value);
                   Set();
                   SetHelpKeys();
                   string sText = m_Value;
                   bool bClear = OnTextChanged(sText);
                   if (bClear)
                   {
                    memset(&m_Value, 0, MAXTEXTLENGHT);
                    Set();
                   }
                   return osContinue;
                 }
               }
               break;

  case kBack:  if (m_iPos >= 0) 
               {
                 m_iPos = -1;
                 m_bNewchar = true;
                 stripspace(m_Value);
                 SetHelpKeys();
               }
               break;  // run into default

  default:   if (m_iPos >= 0 && BASICKEY(Key) == kKbd) 
             {
               int c = KEYKBD(Key);
               if (c <= 0xFF) 
               {
                 const char *p = strchr(ALLOWEDCHARS, tolower(c));
                 if (p) 
                 {
                   int l = strlen(m_Value);
                   if (m_bInsert && l < m_iLength)
                     memmove(m_Value + m_iPos + 1, m_Value + m_iPos, l - m_iPos + 1);
                   m_Value[m_iPos] = c;
                   if (m_iPos < m_iLength - 1)
                     m_iPos++;
                   if (m_iPos >= l) 
                   {
                     m_Value[m_iPos] = ' ';
                     m_Value[m_iPos + 1] = 0;
                   }
                 } 
                 else 
                 {
                   switch (c) 
                   {
                   case 0x7F: // backspace
                     if (m_iPos > 0) 
                     {
                       m_iPos--;
                       return ProcessKey(kYellow);
                     }
                     break;
                   }
                 }
               }
               else 
               {
                 switch (c) 
                 {
                   case kfHome: m_iPos = 0; break;
                   case kfEnd:  m_iPos = strlen(m_Value) - 1; break;
                   case kfIns:  return ProcessKey(kGreen);
                   case kfDel:  return ProcessKey(kYellow);
                 }
               }
             }
  }
  Set();

  return osContinue;
}

void cEditField::SelectChar(int CharPos) 
{
   if (m_pSelectedkey->name) 
   {
     if (CharPos >= (int)strlen(m_pSelectedkey->name)) 
     {
       CharPos = 0;
       m_iSelectedchar = 0;
     }

     if (m_bUppercase)
       m_Value[m_iPos] = toupper(m_pSelectedkey->name[CharPos]);
     else
       m_Value[m_iPos] =         m_pSelectedkey->name[CharPos];
    
     m_bNewchar = false;
   }
}

void cEditField::KeyRightPressed()
{
  if (m_iPos < m_iLength - 1 && m_iPos < int(strlen(m_Value))) 
  {
    if (++m_iPos >= int(strlen(m_Value))) 
    {
      if (m_iPos >= 2 && m_Value[m_iPos - 1] == ' ' && m_Value[m_iPos - 2] == ' ')
        m_iPos--; // allow only two blanks at the end
      else 
      {
        m_Value[m_iPos] = ' ';
        m_Value[m_iPos + 1] = 0;
      }
    }
  }
   
  m_bNewchar = true;
  if (!m_bInsert && isalpha(m_Value[m_iPos]))
    m_bUppercase = isupper(m_Value[m_iPos]);

   
  m_iSelectedchar = m_iTextInputmode == 0 ? 0 : 1 ;
  m_pSelectedkey = &quickKeyTable[10];
 
  SetHelpKeys();
}

void cEditField::Set(void)
{
  char buf[1000];
  const char *fmt;
  if (m_iPos >= 0)
  {
    fmt = m_bInsert && m_bNewchar ? "[]%c%s" : "[%c]%s";
    strncpy(buf, m_Value, m_iPos);
    snprintf(buf + m_iPos, sizeof(buf) - m_iPos - 2, fmt, *(m_Value + m_iPos), m_Value + m_iPos + 1);
  }
  else
  {
    strcpy(buf, m_Value);
  }

  int iWidth = m_iWidth - 8;
  if (m_pFont->Width(buf) <= iWidth) 
  {
    // the whole buffer fits on the screen
    strcpy(m_OutValue, buf);
    Draw();
    return;
  }
  iWidth -= m_pFont->Width('>'); // assuming '<' and '>' have the same with
  int w = 0;
  int i = 0;
  int l = strlen(buf);
  while (i < l && w <= iWidth)
    w += m_pFont->Width(buf[i++]);

  if (i >= m_iPos + 4) 
  {
    // the cursor fits on the screen
    buf[i - 1] = '>';
    buf[i] = 0;
    strcpy(m_OutValue, buf);
    Draw();
    return;
  }
  // the cursor doesn't fit on the screen
  w = 0;
  if (buf[i = m_iPos + 3]) 
  {
    buf[i] = '>';
    buf[i + 1] = 0;
  }
  else
    i--;
  while (i >= 0 && w <= iWidth)
    w += m_pFont->Width(buf[i--]);
  buf[++i] = '<';
  strcpy(m_OutValue, &buf[i]);
  Draw();
}

char cEditField::Inc(char c, bool Up)
{
  const char *p = strchr(ALLOWEDCHARS, c);
  if (!p)
     p = ALLOWEDCHARS;
  if (Up) {
     if (!*++p)
        p = ALLOWEDCHARS;
     }
  else if (--p < ALLOWEDCHARS)
     p = ALLOWEDCHARS + strlen(ALLOWEDCHARS) - 1;
  return *p;
}

void cEditField::SetHelpKeys(void)
{
  if (m_iPos >= 0)
  {
    if (m_pSelectedkey->type == kNone)
    {
      m_pStatusbarWnd->SetButtons(tr("ABC/abc"), tr(m_bInsert ? "Overwrite" : "Insert"), tr("Delete"), tr("Key-Info"));
    }
    else
    { 
      char redkey[2];
	    char greenkey[2];
	    char yellowkey[2]; 
	    char bluekey[2];
	
	    snprintf(redkey,    sizeof(redkey),    "%c", toupper(m_pSelectedkey->name[1]));
	    snprintf(greenkey,  sizeof(greenkey),  "%c", toupper(m_pSelectedkey->name[2]));
	    snprintf(yellowkey, sizeof(yellowkey), "%c", toupper(m_pSelectedkey->name[3]));
	    snprintf(bluekey,   sizeof(bluekey),   "%c", toupper(m_pSelectedkey->name[4]));
	
      if (strlen(bluekey) > 0)
       m_pStatusbarWnd->SetButtons(redkey, greenkey, yellowkey, bluekey);
      else if (strlen(yellowkey) > 0)
       m_pStatusbarWnd->SetButtons(redkey, greenkey, yellowkey);
      else if (strlen(greenkey) > 0)
       m_pStatusbarWnd->SetButtons(redkey, greenkey);
      else if (strlen(redkey) > 0)
       m_pStatusbarWnd->SetButtons(redkey);
      else
       m_pStatusbarWnd->SetButtons(NULL);
    }
  }
  else
    m_pParent->SetHelpKeys();
}

string cEditField::GetText()
{
  return m_Value;
}

void cEditField::SetStyle(tColor iBorderColor, tColor iFocusedBorderColor, tColor iBGColor, tColor iTextColor, 
                          cFont *pFont)
{
  m_iBorderColor        = iBorderColor;
  m_iFocusedBorderColor = iFocusedBorderColor;
  m_iBGColor            = iBGColor;
  m_iTextColor          = iTextColor;
  m_pFont               = pFont;
  Draw();
}

void cEditField::Draw(void)
{
  if (m_pOsd->GetBitmap(0) == NULL)
    return; 

  cMutexLock lock(&cVdrIcqOsd::OsdMutex);

  if (m_bFocused)
    m_pOsd->DrawRectangle(m_iLeft, m_iTop, m_iLeft + m_iWidth, m_iTop + m_iHeight, m_iFocusedBorderColor);
  else
    m_pOsd->DrawRectangle(m_iLeft, m_iTop, m_iLeft + m_iWidth, m_iTop + m_iHeight, m_iBorderColor);

  m_pOsd->DrawRectangle(m_iLeft + 2, m_iTop + 2, m_iLeft + m_iWidth - 2, m_iTop + m_iHeight - 2, m_iBGColor);
  m_pOsd->DrawText(m_iLeft + 2, m_iTop + 2, m_OutValue, m_iTextColor, m_iBGColor, m_pFont);
  m_pOsd->Flush();
}

bool cEditField::OnTextChanged(string& sText)
{
  // virtual function
  return false;
}


void cEditField::SetFocused(bool bFocused)
{
  m_bFocused = bFocused;
  Draw();
}


bool cEditField::IsFocused()
{
  return m_bFocused;
}
