/********************************************************************

Module: TiConsoleWindow

Author: Jon Wiesman

Description:

This window accepts text input and does command processing.

Copyright 2005 Sony Online Entertainment.  All rights reserved.

*********************************************************************/

#include "TiConsoleWindow.h"
#include "TiUI.h"
#include "InputConsole.h"


///////////////////////////////////////////////////////////////////////////////
// TiConsoleCommandHandler
///////////////////////////////////////////////////////////////////////////////

TitanI *TiConsoleCommandHandler::sm_pTitanI = NULL;

TiConsoleCommandHandler::TiConsoleCommandHandler()
{
  RegisterHandler(this);
}

TiConsoleCommandHandler::~TiConsoleCommandHandler()
{
  UnregisterHandler(this);
}

void TiConsoleCommandHandler::RegisterHandler(TiConsoleCommandHandler *pHandler)
{
  GetHandlerList().push_back(pHandler);
}

void TiConsoleCommandHandler::UnregisterHandler(TiConsoleCommandHandler *pHandler)
{
  GetHandlerList().remove(pHandler);
}

std::list<TiConsoleCommandHandler*> &TiConsoleCommandHandler::GetHandlerList()
{
  static std::list<TiConsoleCommandHandler*> s_listHandlers;

  return s_listHandlers;
}

bool TiConsoleCommandHandler::IsWhiteSpace(char8 ch)
{
  return (ch == ' ' || ch == '\t');
}

int32 TiConsoleCommandHandler::TokenizeString(const SyString &str, SyVector<SyString> &arStrings)
{
  arStrings.Clear();

  bool bLastCharWhiteSpace = true;
  SyString strToken;

  for(int i = 0; i < str.Length(); i++)
  {
    char8 ch = str.AsChar()[i];

    if(IsWhiteSpace(ch))
    {
      if(!bLastCharWhiteSpace)
      {
        arStrings.Add(strToken);
        strToken.Clear();
      }
      bLastCharWhiteSpace = true;
    }
    else
    {
      strToken += ch;
      bLastCharWhiteSpace = false;
    }
  }
  if(strToken.Length() > 0)
  {
    arStrings.Add(strToken);
  }
  return arStrings.Size();
}

///////////////////////////////////////////////////////////////////////////////
// TiConsoleWindow
///////////////////////////////////////////////////////////////////////////////
const TiSize c_sizeConsole(600, 51);

TiConsoleWindow *TiConsoleWindow::sm_pInstance = NULL;

TiConsoleWindow::TiConsoleWindow(TiApp* pApp, TitanI *pTitan) 
  : TiWindow(pApp)
{
  SyAssert(sm_pInstance == NULL);
  sm_pInstance = this;
  m_bShift = false;
  m_iInsertionPoint = 0;
  m_iHScroll = 0;
  m_itrHistory = m_listHistory.end();

  m_bdFrame.Initialize("Frame");

  m_rect.SetRect(TiPoint(0, 600), c_sizeConsole);

  TiConsoleCommandHandler::SetTitanInterface(pTitan);
}

TiConsoleWindow::~TiConsoleWindow()
{
  sm_pInstance = NULL;
}

void TiConsoleWindow::ArrowDown()
{
  if(m_listHistory.size() == 0 || m_itrHistory == m_listHistory.end())
    return;
  m_itrHistory++;

  if(m_itrHistory == m_listHistory.end())
    m_strText.Clear();
  else
    m_strText = *m_itrHistory;
  m_iInsertionPoint = m_strText.Length();
}

void TiConsoleWindow::ArrowUp()
{
  if(m_listHistory.size() == 0 || m_itrHistory == m_listHistory.begin())
    return;

  m_itrHistory--;
  m_strText = *m_itrHistory;
  m_iInsertionPoint = m_strText.Length();
}

void TiConsoleWindow::CursorLeft()
{
  m_iInsertionPoint = SY_MAX(0, m_iInsertionPoint - 1);
}

void TiConsoleWindow::CursorRight()
{
  m_iInsertionPoint = SY_MIN(m_strText.Length(), m_iInsertionPoint + 1);
}

void TiConsoleWindow::Delete()
{
  if(m_iInsertionPoint < m_strText.Length())
  {
    static SyString str;
    str.Clear();
    for(int i = 0; i < m_strText.Length(); i++)
    {
      if(i != m_iInsertionPoint)
        str += m_strText.AsChar()[i];
    }
    m_strText = str;
  }
}

void TiConsoleWindow::Backspace()
{
  if(m_iInsertionPoint == 0)
    return;
  CursorLeft();
  Delete();
}

void TiConsoleWindow::Home()
{
  m_iInsertionPoint = 0;
}

void TiConsoleWindow::End()
{
  m_iInsertionPoint = m_strText.Length();
}

void TiConsoleWindow::OnEnter()
{
  if(m_strText.Length()== 0)
    return;

  // add current string to array
  bool bFound = false;
  for(std::list<SyString>::iterator itr = m_listHistory.begin(); itr != m_listHistory.end(); itr++)
  {
    if(0 == m_strText.Compare(*itr))
      bFound = true;
  }
  if(!bFound)
  {
    m_listHistory.push_back(m_strText);
    m_itrHistory = m_listHistory.end();
  }

  ProcessConsoleCommand(m_strText);
  m_strText.Clear();
  m_iInsertionPoint = 0;
}

bool TiConsoleWindow::ProcessKeyDown( int32 nKeyCode )
{
  char ch = 0;

  if(nKeyCode == SyInputEvent::KEYCODE_SHIFT)
  {
    m_bShift = true;
  }
  else if(nKeyCode >= SyInputEvent::KEYCODE_A &&
    nKeyCode <= SyInputEvent::KEYCODE_Z)
  {
    if(m_bShift)
      ch = 'A' + (nKeyCode - SyInputEvent::KEYCODE_A);
    else
      ch = 'a' + (nKeyCode - SyInputEvent::KEYCODE_A);
  }
  else if(!m_bShift && 
    nKeyCode >= SyInputEvent::KEYCODE_0 && 
    nKeyCode <= SyInputEvent::KEYCODE_9  )
  {
    ch = '0' + (nKeyCode - SyInputEvent::KEYCODE_0);
  }
  else
  {
    switch(nKeyCode)
    {
    case SyInputEvent::KEYCODE_0:
      ch = ')';
      break;
    case SyInputEvent::KEYCODE_1:
      ch = '!';
      break;
    case SyInputEvent::KEYCODE_2:
      ch = '@';
      break;
    case SyInputEvent::KEYCODE_3:
      ch = '#';
      break;
    case SyInputEvent::KEYCODE_4:
      ch = '$';
      break;
    case SyInputEvent::KEYCODE_5:
      ch = '%';
      break;
    case SyInputEvent::KEYCODE_6:
      ch = '^';
      break;
    case SyInputEvent::KEYCODE_7:
      ch = '&';
      break;
    case SyInputEvent::KEYCODE_8:
      ch = '*';
      break;
    case SyInputEvent::KEYCODE_9:
      ch = '(';
      break;
    case SyInputEvent::KEYCODE_ACCENT:
      ch = m_bShift ? '~' : '`';
      break;
    case SyInputEvent::KEYCODE_MINUS:
      ch = m_bShift ? '_' : '-';
      break;
    case SyInputEvent::KEYCODE_EQUAL:
      ch = m_bShift ? '+' : '=';
      break;
    case SyInputEvent::KEYCODE_LEFT_BRACKET:
      ch = m_bShift ? '{' : '[';
      break;
    case SyInputEvent::KEYCODE_RIGHT_BRACKET:
      ch = m_bShift ? '}' : ']';
      break;
    case SyInputEvent::KEYCODE_BACKSLASH:
      ch = m_bShift ? '|' : '\\';
      break;
    case SyInputEvent::KEYCODE_SEMICOLON:
      ch = m_bShift ? ':' : ';';
      break;
    case SyInputEvent::KEYCODE_QUOTE:
      ch = m_bShift ? '\"' : '\'';
      break;
    case SyInputEvent::KEYCODE_COMMA:
      ch = m_bShift ? '<' : ',';
      break;
    case SyInputEvent::KEYCODE_PERIOD:
      ch = m_bShift ? '>' : '.';
      break;
    case SyInputEvent::KEYCODE_SLASH:
      ch = m_bShift ? '?' : '/';
      break;
    case SyInputEvent::KEYCODE_SPACE:
      ch = m_bShift ? ' ' : ' ';
      break;

    case SyInputEvent::KEYCODE_ENTER:
      OnEnter();
      break;
    case SyInputEvent::KEYCODE_BACKSPACE:
      Backspace();
      break;
    case SyInputEvent::KEYCODE_HOME:
      Home();
      break;
    case SyInputEvent::KEYCODE_DELETE:
      Delete();
      break;
    case SyInputEvent::KEYCODE_END:
      End();
      break;
    case SyInputEvent::KEYCODE_UP:
      ArrowUp();
      break;
    case SyInputEvent::KEYCODE_LEFT:
      CursorLeft();
      break;
    case SyInputEvent::KEYCODE_DOWN:
      ArrowDown();
      break;
    case SyInputEvent::KEYCODE_RIGHT:
      CursorRight();
      break;

    case SyInputEvent::KEYCODE_ESCAPE:
      break;

    case SyInputEvent::KEYCODE_TAB:
    case SyInputEvent::KEYCODE_INSERT:
      break;
    }
  }

  if(ch != 0)
  {
    if(m_iInsertionPoint == m_strText.Length())
    {
      m_strText += ch;
    }
    else
    {
      SyString strBeg, strEnd;
      if(m_iInsertionPoint > 0)
        strBeg.Init(m_strText, 0, m_iInsertionPoint - 1);
      strEnd.Init(m_strText, m_iInsertionPoint, m_strText.Length() - 1);
      strBeg += ch;
      strBeg += strEnd;
      m_strText = strBeg;
    }
    m_iInsertionPoint++;
  }

  return true;
}

bool TiConsoleWindow::ProcessKeyUp(int32 nKeyCode )
{
  if(nKeyCode == SyInputEvent::KEYCODE_SHIFT)
  {
    m_bShift = false;
  }
  return true;
}

void TiConsoleWindow::DrawSelf()
{
  TiRect rect = GetScreenRect();

  TiFont font( TiUI::Instance()->GetFontHandle("Default") );

  m_bdFrame.Draw(rect);

  TiRect rectText = rect;
  rectText.AdjustSize(-12, -12);
  font.Draw(m_strText, rectText, c_crWhite, 0);

  SyString strPartial;
  int nExtent = 0;
  if(m_iInsertionPoint > 0)
  {
    strPartial.Init(m_strText, 0, m_iInsertionPoint - 1);
    nExtent = font.GetTextWidth(strPartial.AsChar());
  }
  
  TiRect rectCursor = rectText;
  rectCursor.left += nExtent;
  rectCursor.right = rectCursor.left + 1;
  rectCursor.AdjustSize(0, -4);

  static float32 fCursor = 0.f;
  fCursor += TiUI::Instance()->GetFrameElapse();
  if(fCursor > 1.f)
  {
    fCursor -= 1.f;
  }

  if(fCursor > .5f)
  {
    TiUI::Instance()->FillRectangle(rectCursor, c_crWhite);
  }
}

void TiConsoleWindow::ProcessConsoleCommand(const SyString &str)
{
  for(std::list<TiConsoleCommandHandler*>::iterator itr = TiConsoleCommandHandler::GetHandlerList().begin(); 
    itr != TiConsoleCommandHandler::GetHandlerList().end(); 
    itr++)
  {
    TiConsoleCommandHandler *pHandler = *itr;

    pHandler->ProcessConsoleCommand(str);
  }
}
