#include "xPad.h"
#include "Keyboard.h"
#include <string>
#include <fstream>
#include <numeric>

TRANSFORMED_VERTEX back_verts[] ={
	{ 0.0f,				SCREEN_SIZE_Y,	1.0f,	1.0f,	0.0f,	1.0f },
	{ 0.0f,				0.0f,			1.0f,	1.0f,	0.0f,	0.0f },
	{ SCREEN_SIZE_X,	SCREEN_SIZE_Y,	1.0f,	1.0f,	1.0f,	1.0f },
	{ SCREEN_SIZE_X,	0.0f,			1.0f,	1.0f,	1.0f,	0.0f }
};

extern "C" XBOXAPI DWORD WINAPI IoCreateSymbolicLink( IN PUNICODE_STRING SymbolicLinkName, IN PUNICODE_STRING DeviceName );
extern "C" XBOXAPI DWORD WINAPI IoDeleteSymbolicLink( IN PUNICODE_STRING SymbolicLinkName );
UNICODE_STRING SymbolicLinkName = { strlen(DriveD), strlen(DriveD) + 1, DriveD };


UNICODE_STRING DeviceNameC = { strlen(MountPartitionC), strlen(MountPartitionC) + 1, MountPartitionC };
UNICODE_STRING DeviceNameD = { strlen(MountPartitionD), strlen(MountPartitionD) + 1, MountPartitionD };
UNICODE_STRING DeviceNameE = { strlen(MountPartitionE), strlen(MountPartitionE) + 1, MountPartitionE };
UNICODE_STRING DeviceNameF = { strlen(MountPartitionF), strlen(MountPartitionF) + 1, MountPartitionF };
UNICODE_STRING DeviceNameX = { strlen(MountPartitionX), strlen(MountPartitionX) + 1, MountPartitionX };
UNICODE_STRING DeviceNameY = { strlen(MountPartitionY), strlen(MountPartitionY) + 1, MountPartitionY };
UNICODE_STRING DeviceNameZ = { strlen(MountPartitionZ), strlen(MountPartitionZ) + 1, MountPartitionZ };

//-----------------------------------------------------------------------------
// Name: main()
// Desc: Entry point to the program.
//-----------------------------------------------------------------------------
VOID __cdecl main()
{
    CXxPad xbApp;
    if( FAILED( xbApp.Create() ) )
        return;
    xbApp.Run();
}

void CXxPad::ChangeDrive(IN PUNICODE_STRING DeviceName)
{
  DWORD m_dwStatus;
  m_dwStatus = IoDeleteSymbolicLink(&SymbolicLinkName);
  if( m_dwStatus == 0 )
  {
    m_dwStatus = IoCreateSymbolicLink(&SymbolicLinkName, DeviceName);
    //if( m_dwStatus == 0 )
    //  XLaunchNewImage("D:\\settings_adoc.xip", NULL);
  }
}

void CXxPad::SaveFile(LPCTSTR filename)
{
  ofstream file;

  file.open(filename);

  string line;

  // Iterator is used to loop through the vector.
  strings::iterator theIterator;
  for (theIterator = fileContents.begin(); theIterator != fileContents.end(); theIterator++)
  {
    line = (*theIterator);
    file.write(line.c_str(), line.size());
    if (theIterator != fileContents.end() - 1)
      file.write("\n", 1);
  }

  //file.seekg(0,ios::end);
  //filesize = file.tellg();
  //file.seekg(0,ios::beg);

  // read file
  //while (file.good())
  //{
  //  getline(file,line,'\n');
  //  fileContents.push_back(line);
  //}

	file.close();
}

void CXxPad::OpenFile(LPCTSTR filename)
{

  ifstream file;

  file.open(filename);

  string line;

  // Clear the buffer, and reset the workspace settings
  ResetWorkspace();
  
  file.seekg(0,ios::end);
  filesize = file.tellg();
  file.seekg(0,ios::beg);

  // read file
  while (file.good())
  {
    getline(file,line,'\n');
    fileContents.push_back(line);
  }

	file.close();
  strcpy(currentFilename, filename);

  reCalcFileSize = true;
  

	/*
  HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  DWORD dwRead = 0;
  DWORD dwSize = GetFileSize (hFile, NULL) ;
  if (dwSize > 4096)
    dwSize = 4096;
  // Create a buffer the size of the file.
  char entireFile[4096];
  memset(entireFile, 0, 4096);
  ReadFile(hFile, entireFile, dwSize, &dwRead, NULL);
  filesize = dwRead;

  // Iterator is used to loop through the vector.
  //INTVECTOR::iterator theIterator;
  
  // Now copy all the data read into the fileContents structure, breaking at new lines
  //for (theIterator = fileContents.begin(); theIterator != fileContents.end(); theIterator++)
 //{
  //    cout << *theIterator;
  //    if (theIterator != theVector.end()-1) cout << ", ";
  //}

  
  char newLine[256];    
  char *pdest = NULL;file
  do
  {
    pdest = strchr( entireFile, '\r');
    if (pdest != 0)
    {
      memset(newLine, 0, 256);
      strncpy(newLine, entireFile, strlen(entireFile) - strlen(pdest) );
      fileContents.push_back(newLine);
      result = strpbrk(entireFile, "\r");
      result++;
      strcpy(entireFile, result);
    }
    else
    {
      // copy all the rest
      strcpy(newLine, entireFile);
      fileContents.push_back(newLine);
    }
  } while (pdest != 0);
  

  // Close the file
  CloseHandle(hFile);
  */
}

//-----------------------------------------------------------------------------
// Name: CXxPad()
// Desc: Constructor
//-----------------------------------------------------------------------------
CXxPad::CXxPad():CXBApplication()
{
#ifdef _DEBUG    
    //m_d3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;    // Allow unlimited frame rate
#endif

    m_d3dpp.BackBufferWidth        = SCREEN_SIZE_X;
    m_d3dpp.BackBufferHeight       = SCREEN_SIZE_Y;

    m_pBackground = NULL;

    controlMode = CONTROL_DPAD;

    m_FileBrowser = new CXFileBrowser(this);
		m_KeyPadWindow = new CXKeyPadWindow(this, 350, 450, KEYPAD_OUTLINE, KEYPAD_BACK, 0xffffffff);
    m_MenuSystem = new CXMenuSystem(this, COLOUR_LIGHTBLUE, COLOUR_BLUE, 0x00000000);
    m_SettingsWindow = new CXSettingsWindow(this);
    kpX = 450;
    kpY = 350;
    curCol = 0;
    curColPage = 0;

}


//-----------------------------------------------------------------------------
// Name: Initialize()
// Desc: Initializes the 3D scene for the app
//-----------------------------------------------------------------------------
HRESULT CXxPad::Initialize()
{
    // Create the resources
    if( FAILED( m_xprResource.Create( m_pd3dDevice, "Resource.xpr", resource_NUM_RESOURCES ) ) )
        return XBAPPERR_MEDIANOTFOUND;

    // Create a font
    if( FAILED( m_Font.Create( m_pd3dDevice, "Font.xpr" ) ) )
        return XBAPPERR_MEDIANOTFOUND;

    if( FAILED( m_editFont.Create( m_pd3dDevice, "editFont.xpr" ) ) )
        return XBAPPERR_MEDIANOTFOUND;

    // Create texture for the background image
    m_pBackground = m_xprResource.GetTexture( resource_Background_OFFSET );

    ResetWorkspace();

    //=========================================================================
    // Initialize all our objects
    //=========================================================================
    m_FileBrowser->Initialize();
		m_KeyPadWindow->Initialize();
    m_MenuSystem->Initialize();
    m_SettingsWindow->Initialize();

    //=========================================================================
    // Add our Parent Menu Items
    //=========================================================================
    m_MenuSystem->AddMenuItem("File");
    //m_MenuSystem->AddMenuItem("Edit");
    m_MenuSystem->AddMenuItem("Options");

    //=========================================================================
    // Add our Sub Menu Items
    //=========================================================================
    MENUSUBITEM subItem;
    // Set the parent item to the File Item (0) and add all sub items for it
    subItem.enabled = true;
    subItem.checked = false;
    subItem.parent = 0;
    subItem.name = L"New";
    subItem.itemId = MNU_FILE_NEW;
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Open...";
    subItem.itemId = MNU_FILE_OPEN;
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Save";
    subItem.itemId = MNU_FILE_SAVE;
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Save As...";
    subItem.itemId = MNU_FILE_SAVEAS;
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Exit";
    subItem.itemId = MNU_FILE_EXIT;
    m_MenuSystem->AddSubMenuItem(subItem);
    // Set the parent item to the Edit Item (1) and add all sub items for it
    subItem.parent = 1;
    /*subItem.itemId = 0;
    subItem.name = L"Undo";
    subItem.itemId = MNU_EDIT_UNDO;
    subItem.enabled = false;      // Nothing to undo at the start :)
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Cut";
    subItem.itemId = MNU_EDIT_CUT;
    subItem.enabled = false;      // Nothing to cut because nothing is selected at start
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Copy";
    subItem.itemId = MNU_EDIT_COPY;
    subItem.enabled = false;      // Nothing to copy because nothing is selected at start
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Paste";
    subItem.itemId = MNU_EDIT_PASTE;
    subItem.enabled = false;      // Nothing to paste as this is the start (we could always save clipboard, between sessions?)
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Delete";
    subItem.itemId = MNU_EDIT_DELETE;
    subItem.enabled = false;      // Nothing to delete because nothing is selected at start
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Find";
    subItem.itemId = MNU_EDIT_FIND;
    subItem.enabled = true;      // Reset back to true, so that all the other items from here are enabled again
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Find Next";
    subItem.itemId = MNU_EDIT_FINDNEXT;
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Replace";
    subItem.itemId = MNU_EDIT_REPLACE;
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Go To";
    subItem.itemId = MNU_EDIT_GOTO;
    m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Select All";
    subItem.itemId = MNU_EDIT_SELECTALL;
    m_MenuSystem->AddSubMenuItem(subItem);
    // Set the parent item to the Options Item (2) and add all sub items for it
    subItem.parent = 2;
    */
    //subItem.name = L"Settings";
    //subItem.itemId = MNU_OPTIONS_SETTINGS;
    //m_MenuSystem->AddSubMenuItem(subItem);
    subItem.name = L"Dpad Control Mode";
    subItem.itemId = MNU_OPTIONS_DPADCONTROL;
    if (controlMode == CONTROL_DPAD)
      subItem.checked = true;
    else
      subItem.checked = false;
    m_MenuSystem->AddSubMenuItem(subItem);


    //=========================================================================
    // Set all default values
    //=========================================================================
    controlMode = CONTROL_DPAD;

    movingUp    = false;
    movingDown  = false;
    movingLeft  = false;
    movingRight = false;

    holdingXDown = false;
    holdingADown = false;
    holdingBDown = false;
    holdingLTDown = false;
    holdingRTDown = false;

    reCalcFileSize = true;
    ignoreInput = false;
    
    //ChangeDrive(&DeviceNameC);
    //OpenFile("D:\\menuold.ini");

    XBInput_InitDebugKeyboard();

    return S_OK;
}

HRESULT CXxPad::RenderTextSelector(DWORD color)
{
  float curY = 99;
  float curX = 40;
  curY+= (curLine * 20);
  curX+= (curCol * 12);
  DrawRectWithBorder( curX, curY, curX + 12, curY + 18, color, color, COLOUR_BLUE);
  return S_OK;
}

HRESULT CXxPad::RenderWindow()
{
  char blah[27];
  const char* pBlah = blah;
  sprintf(blah, "File Size: %d bytes", filesize);
  WCHAR wcFileSize[27];
  mbsrtowcs(wcFileSize, &pBlah , 27, NULL);
  m_Font.DrawText(584, 436, 0xffffffff, wcFileSize, XBFONT_RIGHT);

  char blah1[27];
  const char* pBlah1 = blah1;
  sprintf(blah1, "Ln %d, Col %d", curLine + curLinePage + 1, curCol + curColPage + 1);
  WCHAR wcLineDetails[27];
  mbsrtowcs(wcLineDetails, &pBlah1, 27, NULL);
  m_Font.DrawText(40, 436, 0xffffffff, wcLineDetails, XBFONT_LEFT);


  char blah2[27];
  const char* pBlah2 = blah2;
  sprintf(blah2, "ALPHA VERSION", 14);
  WCHAR wcVersion[27];
  mbsrtowcs(wcVersion, &pBlah2, 27, NULL);
  m_Font.DrawText(584, 30, 0xffffffff, wcVersion, XBFONT_RIGHT);
  
  return S_OK;
}

HRESULT CXxPad::RenderFileContents()
{
  // Only render what will be displayed on the screen.

  // Iterator is used to loop through the vector.
  strings::iterator theIterator;
  
  // Now copy all the data read into the fileContents structure, breaking at new lines
  int i = 0;
  for (theIterator = fileContents.begin(); theIterator != fileContents.end(); theIterator++)
  {
    //FLOAT curX = 40;
    // how may items have we displayed?
    if (i < curLinePage + 16 && i >= curLinePage)
    {
      WCHAR wcfileLine[46];
      const char* pfileContents = (*theIterator).c_str() + curColPage;
      mbsrtowcs(wcfileLine, &pfileContents, 46 - (CountNumberOfTabs((*theIterator)) * 3), NULL);
      //curX -= curColPage * 12;
 	    m_editFont.DrawText(40, 100 + (((FLOAT)i - curLinePage) * 20), 0xff000000, wcfileLine);
    }

    if (i > curLinePage + 16)
      break;

    i++;
  }

  /*
  // Now copy all the data read into the fileContents structure, breaking at new lines
  int i = 0;
  for (theIterator = fileContents.begin(); theIterator != fileContents.end(); theIterator++)
  {
    FLOAT curX = 40;
    // how may items have we displayed?
    if (i < curLinePage + 16 && i >= curLinePage)
    {
      WCHAR wcfileLine[256];
      const char* pfileContents = (*theIterator).c_str();
      mbsrtowcs(wcfileLine, &pfileContents, 256, NULL);
      curX -= curColPage * 12;
 	    m_editFont.DrawText(curX, 100 + (((FLOAT)i - curLinePage) * 20), 0xff000000, wcfileLine);
    }

    if (i > curLinePage + 16)
      break;

    i++;
  }
*/
  return S_OK;
}

//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Called once per frame, the call is the entry point for 3d
//       rendering. This function sets up render states, clears the
//       viewport, and renders the scene.
//-----------------------------------------------------------------------------
HRESULT CXxPad::Render()
{
    // Clear the viewport
    m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL,
                         0xFFFF0000, 1.0f, 0L );

    // Render the background
    RenderQuad( m_pBackground, 1.0f );

    RenderFileContents();

    RenderWindow();   // Always render the background window

    m_MenuSystem->Render();

    // Menu starts at 64, 88 and is 25 pixels high
    switch (curMode)
    {
      case MODE_NORMAL:
      { 
        RenderTextSelector(SEMITRANS_LIGHTBLUE);
        // Render the Keypad (Virtual Keyboard) window here (or whatever window user wants.
	      RenderKeypadWindow();
        break;
      }
      case MODE_MENU:
      { 
        // Render the Keypad (Virtual Keyboard) window here (or whatever window user wants.
	      RenderKeypadWindow();
        break;
      }
      case MODE_FILESAVEAS:
      case MODE_FILESELECT:
      {
        // Render the Keypad (Virtual Keyboard) window here (or whatever window user wants.
	      RenderKeypadWindow();
        m_FileBrowser->Render();
        break;
      }
      case MODE_SETTINGS:
      {
        // Render the Keypad (Virtual Keyboard) window here (or whatever window user wants.
	      RenderKeypadWindow();
        m_SettingsWindow->Render();
      }
    }

    //m_Font.End();
    //m_editFont.End();
  
    // Present the scene
    m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
    //m_pd3dDevice->PersistDisplay();

    return S_OK;
}

void CXxPad::RenderKeypadWindow()
{
  // Render the Keypad (Virtual Keyboard) window here (or whatever window user wants.
	m_KeyPadWindow->Render(kpX, kpY);
}

bool CXxPad::BeginCursorUpRepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;
  int theKeyState = false;
  if (controlMode == CONTROL_LEFTSTICK)
    theKeyState = m_DefaultGamepad.fY1 > 0.3;
  if (controlMode == CONTROL_DPAD)
    theKeyState = m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_UP;

  // Allways support kb, only if nothing else pressed.
  if (!theKeyState)
    theKeyState = lastKBChar == KEY_UP;

  //if( m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_UP )
  //if (m_DefaultGamepad.fY1 > 0.3)
  if (theKeyState)
  {
    if (!movingUp)
    {
      movingUp=true;
      starttime=timeGetTime();
      delay=200;

      //MoveCursorUp();
      return true;
    }
    else
    {
      // is pressed here
      
      if (timeGetTime()>starttime+delay)
      {

        //MoveCursorUp();

        starttime=timeGetTime();
        delay=50;
        return true;
      }
    }
  }
  else
  {
    movingUp=false;
  }
  return false;
}

bool CXxPad::BeginCursorDownRepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;
  int theKeyState = false;
  if (controlMode == CONTROL_LEFTSTICK)
    theKeyState = m_DefaultGamepad.fY1 < -0.3;
  if (controlMode == CONTROL_DPAD)
    theKeyState = m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_DOWN;

  // Allways support kb, only if nothing else pressed.
  if (!theKeyState)
    theKeyState = lastKBChar == KEY_DOWN;

  //if( m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_DOWN )
  //if (m_DefaultGamepad.fY1 < -0.3)
  if (theKeyState)
  {
    if (!movingDown)
    {
      movingDown=true;
      starttime=timeGetTime();
      delay=200;

      //MoveCursorDown();
      return true;
    }
    else
    {
      // is pressed here
      
      if (timeGetTime()>starttime+delay)
      {
        //MoveCursorDown();

        starttime=timeGetTime();
        delay=50;
        return true;
      }
    }
  }
  else
  {
    movingDown=false;
  }
  
  return false;
}

bool CXxPad::BeginCursorLeftRepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;
  int theKeyState = false;
  if (controlMode == CONTROL_LEFTSTICK)
    theKeyState = m_DefaultGamepad.fX1 < -0.3;
  if (controlMode == CONTROL_DPAD)
    theKeyState = m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_LEFT;
  
  // Allways support kb, only if nothing else pressed.
  if (!theKeyState)
    theKeyState = lastKBChar == KEY_LEFT;

  //if( m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_LEFT )
  //if (m_DefaultGamepad.fX1 < -0.3)
  if (theKeyState)
  {
    if (!movingLeft)
    {
      movingLeft=true;
      starttime=timeGetTime();
      delay=200;
      return true;
    }
    else
    {
      // is pressed here
      if (timeGetTime()>starttime+delay)
      {
        starttime=timeGetTime();
        delay=50;
        return true;
      }
    }
  }
  else
  {
    movingLeft=false;
  }
  
  return false;
}

bool CXxPad::BeginCursorRightRepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;
  int theKeyState = false;
  if (controlMode == CONTROL_LEFTSTICK)
    theKeyState = m_DefaultGamepad.fX1 > 0.3;
  if (controlMode == CONTROL_DPAD)
    theKeyState = m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_RIGHT;

  // Allways support kb, only if nothing else pressed.
  if (!theKeyState)
    theKeyState = lastKBChar == KEY_RIGHT;
  
  //if( m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_RIGHT )
  //if (m_DefaultGamepad.fX1 > 0.3)
  if (theKeyState)
  {
    if (!movingRight)
    {
      movingRight=true;
      starttime=timeGetTime();
      delay=200;
      return true;
    }
    else
    {
      // is pressed here
      if (timeGetTime()>starttime+delay)
      {

        starttime=timeGetTime();
        delay=50;
        return true;
      }
    }
  }
  else
  {
    movingRight=false;
  }
  return false;
}

bool CXxPad::BeginButtonLTRepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;

  int theKeyState = false;

  theKeyState = m_DefaultGamepad.bLastAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER];
  // Always support kb, only if nothing else pressed.
  if (!theKeyState)
    theKeyState = lastKBChar == KEY_PGUP;

  //if( m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_DOWN )
  //if (m_DefaultGamepad.fY1 < -0.3)
  if (theKeyState)
  {
    if (!holdingLTDown)
    {
      holdingLTDown=true;
      starttime=timeGetTime();
      delay=100;
      return true;
    }
    else
    {
      // is pressed here
      if (timeGetTime() > starttime + delay)
      {
        starttime=timeGetTime();
        delay=300 - m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER];
        return true;
      }
    }
  }
  else
  {
    holdingLTDown=false;
  }
  
  return false;
}

bool CXxPad::BeginButtonRTRepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;

  int theKeyState = false;

  theKeyState = m_DefaultGamepad.bLastAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER];
  // Always support kb, only if nothing else pressed.
  if (!theKeyState)
    theKeyState = lastKBChar == KEY_PGDN;

  //if( m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_DOWN )
  //if (m_DefaultGamepad.fY1 < -0.3)
  if (theKeyState)
  {
    if (!holdingRTDown)
    {
      holdingRTDown=true;
      starttime=timeGetTime();
      delay=100;
      return true;
    }
    else
    {
      // is pressed here
      if (timeGetTime() > starttime + delay)
      {
        starttime=timeGetTime();
        delay=300 - m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER];
        return true;
      }
    }
  }
  else
  {
    holdingRTDown=false;
  }
  
  return false;
}

bool CXxPad::BeginButtonXRepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;

  //if( m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_DOWN )
  //if (m_DefaultGamepad.fY1 < -0.3)
  if (m_DefaultGamepad.bLastAnalogButtons[XINPUT_GAMEPAD_X])
  {
    if (!holdingXDown)
    {
      holdingXDown=true;
      starttime=timeGetTime();
      delay=400;
      return true;
    }
    else
    {
      // is pressed here
      if (timeGetTime() > starttime + delay)
      {
        starttime=timeGetTime();
        delay=300 - m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_X];
        return true;
      }
    }
  }
  else
  {
    holdingXDown=false;
  }
  
  return false;
}

bool CXxPad::BeginButtonARepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;

  //if( m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_DOWN )
  //if (m_DefaultGamepad.fY1 < -0.3)
  if (m_DefaultGamepad.bLastAnalogButtons[XINPUT_GAMEPAD_A])
  {
    if (!holdingADown)
    {
      holdingADown=true;
      starttime=timeGetTime();
      delay=400;
      return true;
    }
    else
    {
      // is pressed here
      if (timeGetTime() > starttime + delay)
      {
        starttime=timeGetTime();
        delay=300 - m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_A];
        return true;
      }
    }
  }
  else
  {
    holdingADown=false;
  }
  
  return false;
}

bool CXxPad::BeginButtonBRepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;

  //if( m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_DOWN )
  //if (m_DefaultGamepad.fY1 < -0.3)
  if (m_DefaultGamepad.bLastAnalogButtons[XINPUT_GAMEPAD_B])
  {
    if (!holdingBDown)
    {
      holdingBDown=true;
      starttime=timeGetTime();
      delay=400;
      return true;
    }
    else
    {
      // is pressed here
      if (timeGetTime() > starttime + delay)
      {
        starttime=timeGetTime();
        delay=300 - m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_B];
        return true;
      }
    }
  }
  else
  {
    holdingBDown=false;
  }
  
  return false;
}

void CXxPad::MoveCursorToStartOfLine()
{
  curCol = 0;
  curColVirt = 0;
  curColPage = 0;
}

void CXxPad::MoveCursorToEndOfLine()
{
  curCol = GetCurrentLineLength();
  if ( curCol > 44 )
  {
    curColPage = curCol - 44;
    curCol-= curColPage;
  }
}


void CXxPad::ProcessKeyboard()
{
  // Get the keystroke
  X_KEYSTROKE ks = XBInput_GetKeyboardInput();
  lastKBChar = ks.VirtualKey;
  CHAR cInputKey = lastKBChar;
  if (curMode == MODE_NORMAL)
  {
    if( cInputKey != '\0' )
    {
      int lastCol = 0;
      int lastLine = 0;
      switch( cInputKey )
      {
        case KEY_HOME:
          MoveCursorToStartOfLine();
          break;
        case KEY_END:
          MoveCursorToEndOfLine();
          break;
        case KEY_ENTER:
        case KEY_RETURN:
          InsertLineAtCursor();
          //MoveCursorDown();
          break;
        case KEY_BACKSPACE:
          lastCol = curCol;
          lastLine = curLine;
          MoveCursorLeft();
          if (lastCol == curCol && lastLine == curLine)
            break;
        case KEY_DELETE:
          DeleteCharactersAtCursor(1);
          break;
      }

      // any ascii chars?
      if (ks.Ascii != '\0' && ks.Ascii != KEY_BACKSPACE && ks.Ascii != KEY_RETURN && ks.Ascii != KEY_ESC)
      {
        string theChar;
        theChar.insert(theChar.begin(), ks.Ascii);
        InsertStringAtCursor(theChar);
      }
      /*if( cInputKey != '\0' )
      {
        if ( (cInputKey >= 65 && cInputKey <= 90) 
              || cInputKey == 32 
              || cInputKey >= 48 && cInputKey <= 57
              || cInputKey >= 186 && cInputKey <= 192
              || cInputKey >= 219 && cInputKey <= 221
              || cInputKey == 111 
              || cInputKey == 106 
              || cInputKey == 107 
              || cInputKey == 109 
           )

        {
          string theChar;
          theChar.insert(theChar.begin(), cInputKey);
          InsertStringAtCursor(theChar);
        }
      }*/
    }
  }
}

void CXxPad::MoveKeypadWindow()
{
  if (m_DefaultGamepad.fX2 < -0.3 || m_DefaultGamepad.fX2 > 0.3)
    kpX += m_DefaultGamepad.fX2 * 5;
  if (m_DefaultGamepad.fY2 < -0.3 || m_DefaultGamepad.fY2 > 0.3)
    kpY += -m_DefaultGamepad.fY2 * 5;
  // Cap the values at the ends of the screen, so we don't disapear! :P
  kpX = max(kpX, 40);
  kpX = min(kpX, 580 - 130);
  kpY = max(kpY, 99);
  kpY = min(kpY, 417 - 105);
}

//-----------------------------------------------------------------------------
// Name: FrameMove()
// Desc: Called once per frame, the call is the entry point for animating
//       the scene.
//-----------------------------------------------------------------------------
HRESULT CXxPad::FrameMove()
{
  if (ignoreInput)
  {
    // Wait till all buttons are released.
    FLOAT val = 0;
    ignoreInput = false;
    val = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_A];
    if (val >= 30.0f)
      ignoreInput = true;
    val = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_B];
    if (val >= 30.0f)
      ignoreInput = true;
    val = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_X];
    if (val >= 30.0f)
      ignoreInput = true;
    val = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_Y];
    if (val >= 30.0f)
      ignoreInput = true;
    val = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_BLACK];
    if (val >= 30.0f)
      ignoreInput = true;
    val = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_WHITE];
    if (val >= 30.0f)
      ignoreInput = true;
    val = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER];
    if (val >= 30.0f)
      ignoreInput = true;
    val = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER];
    if (val >= 30.0f)
      ignoreInput = true;
  }
  
  // Process other items that need to be processed before main kb...
  if (curMode == MODE_FILESAVEAS || curMode == MODE_FILESELECT)
  {
    if (!ignoreInput)
      m_FileBrowser->FrameMove();
  }
  
  // Handle Keyboard Input.
  //if (g_bKeyboardInitialised)
  ProcessKeyboard();

  if (reCalcFileSize)
    calcFileSize();


  // When the user presses the Back button, launch the dashboard
  //if( m_DefaultGamepad.wPressedButtons & XINPUT_GAMEPAD_BACK )
  //{
    //m_pd3dDevice->PersistDisplay();
    //LD_LAUNCH_DASHBOARD LaunchData = { XLD_LAUNCH_DASHBOARD_MAIN_MENU };
    //XLaunchNewImage( NULL, (LAUNCH_DATA*)&LaunchData );
  //}

  if( m_DefaultGamepad.wPressedButtons & XINPUT_GAMEPAD_START || lastKBChar == KEY_F2 )
  {
    switch (curMode)
    {
      case MODE_NORMAL:
        curMode = MODE_MENU;
        m_MenuSystem->curMenuLine = 0;
        m_MenuSystem->curMainMenu = 0;
        break;
      case MODE_MENU:
        curMode = MODE_NORMAL;
        m_MenuSystem->curMainMenu = -1;
        m_MenuSystem->curMenuLine = 0;

        break;
    }
  }


  switch(curMode)
  {
    case MODE_NORMAL:
    {
      // Move the keypad window
      MoveKeypadWindow();

      // switch ( controlMode )

      if (!ignoreInput)
      {
        // Only move Cursor Right if we need to.
        if ( BeginCursorUpRepeat() )
          MoveCursorUp();

        float trigval = 0;
        if ( BeginButtonLTRepeat() )
        {
          if (lastKBChar == KEY_PGUP)
          {
            MoveCursorUp(13);
          }
          else
          {
            trigval = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER];
            if (trigval > 0.4f)
              MoveCursorUp((trigval / 255.0f) * 4);
          }
        }
        if ( BeginButtonRTRepeat() )
        {
          if (lastKBChar == KEY_PGDN)
          {
            MoveCursorDown(13);
          }
          else
          {
            trigval = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER];
            if (trigval > 0.4f)
              MoveCursorDown((trigval / 255.0f) * 4);
          }
        }
      
        // Only move Cursor Right if we need to.
        if ( BeginCursorDownRepeat() )
          MoveCursorDown();

        // Only move Cursor Right if we need to.
        if ( BeginCursorLeftRepeat() )
          MoveCursorLeft();

        // Only move Cursor Right if we need to.
        if ( BeginCursorRightRepeat() )
          MoveCursorRight();




        if ( BeginButtonARepeat())
          InsertCurrentCharacterFromKeypadAtCursor();

        if ( BeginButtonXRepeat())
          DeleteCharactersAtCursor(1);

        if ( BeginButtonBRepeat())
          InsertLineAtCursor();
      }

      m_KeyPadWindow->FrameMove();

      
			break;
    }
    case MODE_MENU:
    {
      m_MenuSystem->FrameMove();
      // Check for feedback from menu
      ProcessMenuFeedback( m_MenuSystem->GetLastItemId() );
      break;
    }
    case MODE_SETTINGS:
    {
      m_SettingsWindow->FrameMove();
      break;
    }
  }

  return S_OK;
}


HRESULT CXxPad::Cleanup()
{
  m_FileBrowser->Cleanup();
  m_KeyPadWindow->Cleanup();
  m_MenuSystem->Cleanup();
  m_SettingsWindow->Cleanup();
  delete m_FileBrowser;
	delete m_KeyPadWindow;
  delete m_MenuSystem;
  delete m_SettingsWindow;
  return S_OK;
}


//-----------------------------------------------------------------------------
// Name: RenderQuad()
// Desc: Renders a quad textured with the persisted surface
//-----------------------------------------------------------------------------
HRESULT CXxPad::RenderQuad( LPDIRECT3DTEXTURE8 pTexture, FLOAT fAlpha )
{
    // Set up the vertices (notice the pixel centers are shifted by -0.5f to
    // line them up with the texel centers). The texture coordinates assume
    // a linear texture will be used.
    struct VERTEX { D3DXVECTOR4 p; FLOAT tu, tv; };
    VERTEX v[4];
    v[0].p = D3DXVECTOR4(   0 - 0.5f,   0 - 0.5f, 0.0f, 0.0f ); v[0].tu =   0; v[0].tv =   0;
    v[1].p = D3DXVECTOR4( SCREEN_SIZE_X - 0.5f,   0 - 0.5f, 0.0f, 0.0f ); v[1].tu = SCREEN_SIZE_X; v[1].tv =   0;
    v[2].p = D3DXVECTOR4( SCREEN_SIZE_X - 0.5f, SCREEN_SIZE_Y - 0.5f, 0.0f, 0.0f ); v[2].tu = SCREEN_SIZE_X; v[2].tv = SCREEN_SIZE_Y;
    v[3].p = D3DXVECTOR4(   0 - 0.5f, SCREEN_SIZE_Y - 0.5f, 0.0f, 0.0f ); v[3].tu =   0; v[3].tv = SCREEN_SIZE_Y;

    // Set state to render the image
    m_pd3dDevice->SetTexture( 0, pTexture );
    m_pd3dDevice->SetPixelShader( NULL );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
    m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_DISABLE );
    m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSU,  D3DTADDRESS_CLAMP );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSV,  D3DTADDRESS_CLAMP );
    m_pd3dDevice->SetRenderState( D3DRS_TEXTUREFACTOR, (DWORD)(255.0f*fAlpha)<<24L );
    m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
    m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
    m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
    m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_FOGENABLE,    FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE );

    // Render the quad
    m_pd3dDevice->SetVertexShader( D3DFVF_XYZRHW|D3DFVF_TEX1 );
    m_pd3dDevice->DrawPrimitiveUP( D3DPT_QUADLIST, 1, v, sizeof(v[0]) );

    return S_OK;
}


//-----------------------------------------------------------------------------
// Name: DrawLine()
// Desc: 
//-----------------------------------------------------------------------------
VOID CXxPad::DrawLine( FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2, 
                            DWORD dwColor )
{
    SCREENVERTEX v[2];
    v[0].pos = D3DXVECTOR4( x1, y1, 0.5f, 1.0f );   v[0].color = dwColor;
    v[1].pos = D3DXVECTOR4( x2, y2, 0.5f, 1.0f );   v[1].color = dwColor;
    
    // Render the line
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_DISABLE );
    m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE,   TRUE );
    m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
    m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

    m_pd3dDevice->SetVertexShader( D3DFVF_SCREENVERTEX) ;
    m_pd3dDevice->DrawPrimitiveUP( D3DPT_LINELIST, 1, v, sizeof(SCREENVERTEX) );
}



//-----------------------------------------------------------------------------
// Name: DrawRectOutline()
// Desc: 
//-----------------------------------------------------------------------------
VOID CXxPad::DrawRectOutline( FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2, 
                                   DWORD dwColor )
{
    DrawLine( x1, y1, x1, y2, dwColor );
    DrawLine( x1, y1, x2, y1, dwColor );
    DrawLine( x2, y1, x2, y2, dwColor );
    DrawLine( x1, y2, x2, y2, dwColor );
}




//-----------------------------------------------------------------------------
// Name: DrawRect()
// Desc: 
//-----------------------------------------------------------------------------
VOID CXxPad::DrawRect( FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2, 
                            DWORD dwStartColor, DWORD dwEndColor )
{

    SCREENVERTEX v[4];
    v[0].pos = D3DXVECTOR4( x1-0.5f, y1-0.5f, 1.0f, 1.0f );  v[0].color = dwStartColor;
    v[1].pos = D3DXVECTOR4( x2-0.5f, y1-0.5f, 1.0f, 1.0f );  v[1].color = dwStartColor;
    v[2].pos = D3DXVECTOR4( x1-0.5f, y2-0.5f, 1.0f, 1.0f );  v[2].color = dwEndColor;
    v[3].pos = D3DXVECTOR4( x2-0.5f, y2-0.5f, 1.0f, 1.0f );  v[3].color = dwEndColor;
    
    // Render the rectangle
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_DISABLE );
    m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE,   TRUE );
    m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
    m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

    m_pd3dDevice->SetVertexShader( D3DFVF_SCREENVERTEX );
    
    m_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, v, sizeof(SCREENVERTEX) );

}

//-----------------------------------------------------------------------------
// Name: RenderTile
// Desc: Renders the texture at the given rect.  
//-----------------------------------------------------------------------------
VOID CXxPad::RenderTile( const D3DXVECTOR4& rc, const LPDIRECT3DTEXTURE8 pTile) const
{
    // Set up the vertices (notice the pixel centers are shifted by -0.5f to
    // line them up with the texel centers). The texture coordinates assume
    // a linear texture will be used.
    struct VERTEX { D3DXVECTOR4 p; FLOAT tu, tv; };
    VERTEX v[4];
    v[0].p = D3DXVECTOR4( rc.x - 0.5f,    rc.y - 0.5f,  0.0f, 0.0f );  v[0].tu = 0;   v[0].tv = 0;
    v[1].p = D3DXVECTOR4( rc.z - 0.5f,    rc.y - 0.5f,  0.0f, 0.0f );  v[1].tu = SCREEN_SIZE_X; v[1].tv = 0;
    v[2].p = D3DXVECTOR4( rc.z - 0.5f,    rc.w - 0.5f,  0.0f, 0.0f );  v[2].tu = SCREEN_SIZE_X; v[2].tv = SCREEN_SIZE_Y;
    v[3].p = D3DXVECTOR4( rc.x - 0.5f,    rc.w - 0.5f,  0.0f, 0.0f );  v[3].tu = 0;   v[3].tv = SCREEN_SIZE_Y;

    // Set state to render the image
    m_pd3dDevice->SetTexture( 0, pTile );
    m_pd3dDevice->SetPixelShader( NULL );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TFACTOR );
    m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_DISABLE );
    m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSU,  D3DTADDRESS_CLAMP );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSV,  D3DTADDRESS_CLAMP );
    m_pd3dDevice->SetRenderState( D3DRS_TEXTUREFACTOR, (DWORD)(255.0f*1.0f)<<24L );
    m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
    m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
    m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
    m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_FOGENABLE,    FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE );

    // Render the quad
    m_pd3dDevice->SetVertexShader( D3DFVF_XYZRHW|D3DFVF_TEX1 );
    m_pd3dDevice->DrawPrimitiveUP( D3DPT_QUADLIST, 1, v, sizeof(v[0]) );

}


XBGAMEPAD* CXxPad::GetGamePad()
{
  return &m_DefaultGamepad;
}

void CXxPad::ResetWorkspace()
{
  curLine = 0;
  curLinePage = 0;
  curCol = 0;
  curColVirt = 0;
  curColPage = 0;
  curMode = MODE_NORMAL;
  m_MenuSystem->curMenuLine = 0;
  m_MenuSystem->curMainMenu = 0;
  totalLines = 0;
  filesize = 0;
  memset(currentFilename, 0, MAX_PATH);
  lastKBChar = 0;

  fileContents.clear();

}

void CXxPad::InsertStringAtCursor(string stringToInsert)
{
  // Insert the current char from the keypad control.
  if ( curLine + curLinePage < totalLines )
  {
    string &curString = fileContents[curLine + curLinePage];

    string::iterator theIterator;
    for (theIterator = stringToInsert.begin(); theIterator != stringToInsert.end(); theIterator++)
    {
      // Now add the char to the current pos, 
      //if ( curCol < (signed int)curString.length() )
      int tabCount = CountNumberOfTabs(curString, true);
      curString.insert(curString.begin() + curCol + curColPage - (tabCount * 3), *theIterator);

      MoveCursorRight();
      reCalcFileSize = true;
    }
  }
  else
  {
    // Insert a line here.
    fileContents.insert(fileContents.begin() + curLine + curLinePage, stringToInsert);
    MoveCursorRight();
    reCalcFileSize = true;
  }
   
}

void CXxPad::MoveCursorUp(int count)
{
  for (int i = 0; i < count; i++)
  {
    if (curLine <3 && curLinePage > 0)
      curLinePage--;
    else if (curLine >0)
      curLine--;
  }

  ValidateCursorHorizPos();

  //MoveCursorRight();
  //MoveCursorLeft();
}

void CXxPad::MoveCursorDown(int count)
{
  for (int i = 0; i < count; i++)
  {
    if (curLine > 12 && curLinePage < (int)fileContents.size() - 16)
      curLinePage++;
    else if (curLine < 15 && curLine < (int)fileContents.size())
      curLine++;
  }
  ValidateCursorHorizPos();

  //MoveCursorRight();
  //MoveCursorLeft();
}

void CXxPad::MoveCursorLeft(int count)
{
  if (curCol < 5 && curColPage > 0)
    curColPage--;
  else if (curCol >=0)
    curCol--;

  // Was the last character a tab?  if so move the tab size - 1
  if (IsCharacterOfType(curCol + curColPage - 3, '\t'))
    curCol-=3;      // tab size is 4
  
  if (curCol < 0 && curLine + curLinePage > 0)
  {
    // go up a line..
    MoveCursorUp();
    MoveCursorToEndOfLine();

  }

  // clean this up if it fell through
  if (curCol < 0)
    curCol = 0;

  curColVirt = curCol;
}

void CXxPad::MoveCursorRight(int count)
{
  // Is there more to view at the right??
  if (curCol >= 39 && curCol + curColPage < GetCurrentLineLength())
    curColPage++;
  else if(curCol < 44)
    curCol++;

  // Was the last character a tab?  if so move the tab size - 1
  if (IsCharacterOfType(curCol + curColPage - 1, '\t'))
    curCol+=3;      // tab size is 4

  // Set curColVirt here, because otherwise the MoveCursor Down call will reset it to the end of the line again, not 0
  curColVirt = curCol;
  // If we are at the end of the line, the go to the next line.
  if ( curCol + curColPage > GetCurrentLineLength() )
  {
    curCol = 0;
    curColPage = 0;
    // Set curColVirt here, because otherwise the MoveCursor Down call will reset it to the end of the line again, not 0
    curColVirt = curCol;
    MoveCursorDown();
  }
}

void CXxPad::DeleteCharactersAtCursor(int count)
{
  // Delete the current character from the current string.
  // Figure out the current item
  if (curLine + curLinePage < (int)fileContents.size())
  {
    string &curString = fileContents[curLine + curLinePage];

    if (&curString)
    {
      // if the line is blank, just remove the entire line.
      if (curString.length() == 0)
      {
        DeleteLinesAtCursor(1);
        reCalcFileSize = true;
      }
      else
      {
        for (int i = 0; i < count; i++)
        {
          // Now remove the current char pos, ensuring that we are not longer than the string
          int tabCount = CountNumberOfTabs(curString, true);

          if ( curCol + curColPage - (tabCount * 3) < (signed int)curString.length() )
          {
            curString.erase(curString.begin() + curCol + curColPage - (tabCount * 3));
            reCalcFileSize = true;
          }
          else
          {
            // concatenate the two strings together as we are at the end. (only if there IS another line)
            if (curLine + curLinePage + 1 - (tabCount * 3) < totalLines)
            {
              string &curString = fileContents[curLine + curLinePage - (tabCount * 3)];
              string &nextString = fileContents[curLine + curLinePage + 1 - (tabCount * 3)];
              curString.append(nextString.begin(), nextString.end());
              fileContents.erase(fileContents.begin() + curLine + curLinePage + 1 - (tabCount * 3));
              //fileContents.resize(fileContents.size() - 1, string("asd"));
            }
          }
        }
      }
    }
  }
}

void CXxPad::DeleteLinesAtCursor(int count)
{
  for (int i = 0; i < count; i++)
  {
    if ( fileContents.begin() + curLine + curLinePage <= fileContents.end() )
      fileContents.erase(fileContents.begin() + curLine + curLinePage);
  }
  reCalcFileSize = true;
}

void CXxPad::InsertCurrentCharacterFromKeypadAtCursor()
{
  string curChar;
  curChar.insert(curChar.begin(), m_KeyPadWindow->curChar);
  InsertStringAtCursor(curChar);
}

void CXxPad::InsertLineAtCursor()
{
  strings::iterator line = fileContents.begin() + curLine + curLinePage;
  if (line == fileContents.end())
  {
    fileContents.insert(fileContents.begin() + curLine + curLinePage, "");
  }
  else
  {
    string &curString = fileContents[curLine + curLinePage];
    string newLine;

    newLine = curString.substr(curCol + curColPage, curString.length());
    if (newLine != "" || curCol + curColPage > 0)
    {
      curString.resize(curCol + curColPage);
      fileContents.insert(fileContents.begin() + curLine + curLinePage + 1, newLine);
    }
    else
      fileContents.insert(fileContents.begin() + curLine + curLinePage, "");
  }

  MoveCursorToStartOfLine();
  MoveCursorDown();
  reCalcFileSize = true;
}

void CXxPad::DrawRectWithBorder( FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2, DWORD dwStartColor, DWORD dwEndColor, DWORD dwBorderColor )
{
  DrawRect( x1, y1,x2, y2, dwStartColor, dwEndColor );
  DrawRectOutline( x1, y1, x2, y2, dwBorderColor );
}

int CXxPad::GetCurrentLineLength()
{
  strings::iterator line = fileContents.begin() + curLine + curLinePage;
  if (line == fileContents.end())
    return 0;
  
  // if we return line->length(); then we only get the character count, which does not include
  // actual length caused by having tabs.  We must count the tabs, and add tabwidth to it.
  int tabCount = CountNumberOfTabs((*line));

  if (tabCount > 0)
    return line->length() + (tabCount * 3);
  else
    return line->length();
}

int CXxPad::CountNumberOfTabs(string sline, bool onlyToCurPos)
{
  int tabCount = 0;

  for (unsigned int i = 1; i < sline.length(); i++)
  {
    if (onlyToCurPos && (i >= curCol + curColPage - (tabCount * 3) ) )
      break;
    if (sline[i] == '\t')
      tabCount++;
  }

  return tabCount;
}

bool CXxPad::IsCharacterOfType(int position, char character)
{
  strings::iterator line = fileContents.begin() + curLine + curLinePage;
  if (line == fileContents.end())
    return false;

  string sline;
  sline = (*line);

  if (sline[position] == character)
    return true;

  return false;
}

void CXxPad::ResetMenuSystem()
{
  curMode = MODE_NORMAL;
  m_MenuSystem->ResetMenu();
}

void CXxPad::ProcessMenuFeedback(int feedbackID)
{
  if (feedbackID == 0)
    return;

  // Process our id's.
  switch (feedbackID)
  {
    case MNU_FILE_NEW:
    {
      // clear buffer and reset mode
      ResetWorkspace();
      break;
    }
    case MNU_FILE_OPEN:
    {
      // Open
      curMode = Mode::MODE_FILESELECT;
      m_FileBrowser->ResetBrowser();
      break;
    }
    case MNU_FILE_SAVE:
    {
      // Save to current filename.  if no filename, then prompt.
      // call save as if no filename
      if (strlen(currentFilename) == 0)
      {
        // Save to a new filename
        curMode = Mode::MODE_FILESAVEAS;
        m_FileBrowser->ResetBrowser();
      }
      else
      {
        // Save to this path.
        SaveFile(currentFilename);
        curMode = Mode::MODE_NORMAL;
      }


      break;
    }
    case MNU_FILE_SAVEAS:
    {
      // Save to a new filename
      curMode = Mode::MODE_FILESAVEAS;
      m_FileBrowser->ResetBrowser();
      break;
    }
    case MNU_FILE_EXIT:
    {
      // Exit
      LD_LAUNCH_DASHBOARD LaunchData = { XLD_LAUNCH_DASHBOARD_MAIN_MENU };
      XLaunchNewImage( NULL, (LAUNCH_DATA*)&LaunchData );
      break;
    }
    case MNU_OPTIONS_DPADCONTROL:
    {
      MENUSUBITEM* pSubItem = m_MenuSystem->GetSubItemFromId(MNU_OPTIONS_DPADCONTROL);
      if (controlMode == CONTROL_DPAD)
      {
        controlMode = CONTROL_LEFTSTICK;
        m_KeyPadWindow->controlMode = CXKeyPadWindow::CONTROL_DPAD;
        pSubItem->checked = false;
      }
      else
      {
        controlMode = CONTROL_DPAD;
        m_KeyPadWindow->controlMode = CXKeyPadWindow::CONTROL_LEFTSTICK;
        pSubItem->checked = true;
      }
      curMode = Mode::MODE_NORMAL;
      break;
    }
    case MNU_OPTIONS_SETTINGS:
    {
      curMode = Mode::MODE_SETTINGS;
      m_SettingsWindow->ResetSettings();
      break;
    }
    default:
      curMode = Mode::MODE_NORMAL;
  }

  ignoreInput = true;

  m_MenuSystem->ResetMenu();
}

void CXxPad::ValidateCursorHorizPos()
{
  // Ensure that the current cursor x pos is valid.
  // If it is not, the current X pos is recorded and 
  // existing is corrected.  the recorded is reset when you use left and right.

  // First of all get the current line.
  int curLineLen = GetCurrentLineLength();

  curCol = curColVirt;

  if (curCol + curColPage > curLineLen)
  {
    // record the last col
    curColVirt = curCol;
    if (curLineLen <= 44)
    {
      curCol = curLineLen;
      curColPage = 0;
    }
    else
    {
      if (curLineLen - curColPage > 44)
      {
        curCol = 22;
        curColPage = curLineLen - curCol;
      }
      else
      {
        curCol = curLineLen - curColPage;
      }
    }
  }
  //int curLine;
  //int curCol;
  //int curColVirt;
  //int curLinePage;

}


//-----------------------------------------------------------------------------
// Name: RenderTile
// Desc: Renders the texture at the given rect.  
//-----------------------------------------------------------------------------
VOID CXxPad::RenderImage( const D3DXVECTOR4& rc, const LPDIRECT3DTEXTURE8 pTile, FLOAT fAlpha, FLOAT fTopWarp, FLOAT fBottomWarp ) const
{

    // Set up the vertices (notice the pixel centers are shifted by -0.5f to
    // line them up with the texel centers). The texture coordinates assume
    // a linear texture will be used.


    struct VERTEX { D3DXVECTOR4 p; FLOAT tu, tv; };
    VERTEX v[4];
    v[0].p = D3DXVECTOR4( rc.x - 0.5f + fTopWarp,    rc.y - 0.5f,  0.0f, 0.0f );  v[0].tu = 0;   v[0].tv = 0;
    v[1].p = D3DXVECTOR4( rc.z - 0.5f - fTopWarp,    rc.y - 0.5f,  0.0f, 0.0f );  v[1].tu = rc.z - rc.x; v[1].tv = 0;
    v[2].p = D3DXVECTOR4( rc.z - 0.5f - fBottomWarp,    rc.w - 0.5f,  0.0f, 0.0f );  v[2].tu = rc.z - rc.x; v[2].tv = rc.w - rc.y;
    v[3].p = D3DXVECTOR4( rc.x - 0.5f + fBottomWarp,    rc.w - 0.5f,  0.0f, 0.0f );  v[3].tu = 0;   v[3].tv = rc.w - rc.y;

    // Set state to render the image
    m_pd3dDevice->SetTexture( 0, pTile );
    m_pd3dDevice->SetPixelShader( NULL );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, /*D3DTA_TEXTURE*/D3DTA_TFACTOR );
    m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_DISABLE );
    m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSU,  D3DTADDRESS_CLAMP );
    m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSV,  D3DTADDRESS_CLAMP );
    m_pd3dDevice->SetRenderState( D3DRS_TEXTUREFACTOR, (DWORD)(255.0f*fAlpha)<<24L );
    m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
    m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
    m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
    m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_FOGENABLE,    FALSE );
    m_pd3dDevice->SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE );

    // Render the quad
    m_pd3dDevice->SetVertexShader( D3DFVF_XYZRHW|D3DFVF_TEX1 );
    m_pd3dDevice->DrawPrimitiveUP( D3DPT_QUADLIST, 1, v, sizeof(v[0]) );
}

void CXxPad::calcFileSize()
{
  // Iterator is used to loop through the vector.
  strings::iterator theIterator;
  
  filesize = 0;
  for (theIterator = fileContents.begin(); theIterator != fileContents.end(); theIterator++)
  {
    if (theIterator != fileContents.begin())
      filesize += 2;    // for the crlf
    filesize += (*theIterator).size();
    reCalcFileSize = false;
  }

  totalLines = fileContents.size();
}