#include "FileBrowser.h"
#include "Resource.h"
#include "keyboard.h"
#include <string>

extern UNICODE_STRING DeviceNameC;
extern UNICODE_STRING DeviceNameD;
extern UNICODE_STRING DeviceNameE;
extern UNICODE_STRING DeviceNameF;
extern UNICODE_STRING DeviceNameX;
extern UNICODE_STRING DeviceNameY;
extern UNICODE_STRING DeviceNameZ;

CXFileBrowser::CXFileBrowser(CXxPad *_parent)
{
  parent = _parent;
}

HRESULT CXFileBrowser::Initialize()
{
  curFileLine = 0;
  curDrive = 'C';
  strcpy(curPath, "\\");
  parent->ChangeDrive(&DeviceNameC);

  // Create texture for the image to persist
  m_pMainWindow = NULL;
  m_pMainWindow = parent->m_xprResource.GetTexture( resource_FileBrowser_OFFSET );

  BrowseMode = MODE_FILE;
  curSelDrive = 0;
  curFilePage = 0;
  curCol = 0;

  holdingADown = false;
  ignoreInput = true;

  ResetBrowser();

  return S_OK;
}

HRESULT CXFileBrowser::Render()
{
  float curBottomWarpVal;
  float curTopWarpVal;
  float curAlphaVal;
  float modValue;
  const float windowExpand = 0.6f;
  const float animSpeed = 0.05f;

  if (opening)
  {
    windowOpenAmount += animSpeed;
    windowOpenAmount = min(windowOpenAmount, 1.0f);
  }
  else
  {
    windowOpenAmount -= animSpeed;
    windowOpenAmount = max(windowOpenAmount, 0.0f);
    if (windowOpenAmount == 0.0f)
    {
      parent->ignoreInput = true;
      parent->curMode = parent->MODE_NORMAL;
      if (strlen(fileToOpen) != 0)
        parent->OpenFile(fileToOpen);
      parent->ResetMenuSystem();
    }
  }
  modValue = min(windowOpenAmount, windowExpand) * (1.0f / windowExpand);
  curBottomWarpVal = 200.0f - (200.0f * modValue);
  curTopWarpVal = 100.0f - (100.0f * modValue);
  curAlphaVal = 0.9f * windowOpenAmount;

  D3DXVECTOR4 rect (106, 116, 522, 396);
  parent->RenderImage (rect, m_pMainWindow, curAlphaVal, curTopWarpVal, curBottomWarpVal);
  
  if (windowOpenAmount >= windowExpand)
  {
    char blah[MAX_PATH];
    const char* pBlah = blah;
    sprintf(blah, "%c:%s", curDrive, curPath);
    WCHAR wcCurDrive[MAX_PATH];
    mbsrtowcs(wcCurDrive, &pBlah , MAX_PATH, NULL);
    parent->m_Font.DrawText(114, 140, 0xfffff600, wcCurDrive);


    //===========================================================================
    // Begine the rendering of all the file entries.
    // Render all dirs first, then files
    //===========================================================================
    float curY = 165;
    int curCount = 0;
    fileItem::iterator fileIterator;
  
    for (fileIterator = fileItems.begin() + curFilePage; fileIterator != fileItems.end(); fileIterator++)
    {
      if (curCount >8)
        break;
    
      char curString[MAX_PATH];
      memset(curString, '\0', MAX_PATH);
      if ((*fileIterator).isDirectory)
      {
        strcpy(curString, "[");
        strcat(curString, (*fileIterator).strPath);
        strcat(curString, "]");
      }
      else
        strcat(curString, (*fileIterator).strPath);

      WCHAR wcfileItem[MAX_PATH];
      const char* pmenuItem = curString;
      mbsrtowcs(wcfileItem, &pmenuItem, MAX_PATH, NULL);
 	    parent->m_Font.DrawText(114, curY, 0xff000000, wcfileItem);
      curY += 25;

      curCount++;
    }

  
    if (curCount > 0)
    {
      // Render the file selector
      curY = 165;
      curY+= (curFileLine * 25) ;

      parent->DrawRectWithBorder( 110, curY, 517, curY + 25, SEMITRANS_BLUE, SEMITRANS_BLUE, COLOUR_BLUE);
    }

  
    // Render whatever else is mode special.
    switch (BrowseMode)
    {
      case MODE_FILENAME:
      {
        // Render the Filename entering window
        parent->DrawRectWithBorder( 150, 200, 480, 260, KEYPAD_BACK, KEYPAD_BACK, KEYPAD_OUTLINE);
        parent->m_Font.DrawText( 155, 205, 0xfffff600, L"Enter Filename:");
        WCHAR wcfileName[MAX_PATH];
        const char* pfileName = selFilename;
        mbsrtowcs(wcfileName, &pfileName, MAX_PATH, NULL);
        parent->m_editFont.DrawText( 155, 230, 0xffffffff, wcfileName);
        //parent->DrawRectWithBorder( 204 + ((FLOAT)curSelDrive * 32), 230, 204 + ((FLOAT)curSelDrive * 32) + 32, 255, SEMITRANS_BLUE, SEMITRANS_BLUE, COLOUR_BLUE);

        // Render the character selector
        float curY = 229;
        float curX = 154;
        curX+= (curCol * 12);
        parent->DrawRectWithBorder( curX, curY, curX + 12, curY + 18, SEMITRANS_LIGHTBLUE, SEMITRANS_LIGHTBLUE, COLOUR_BLUE);
        
        parent->RenderKeypadWindow();
        break;
      }
      case MODE_DRIVE:
      {
        // Render the Drive selection window
        parent->DrawRectWithBorder( 200, 200, 430, 260, KEYPAD_BACK, KEYPAD_BACK, KEYPAD_OUTLINE);
        parent->m_Font.DrawText( 205, 205, 0xfffff600, L"Select Drive:");
        parent->m_Font.DrawText( 205, 230, 0xffffffff, L"C:\\ D:\\ E:\\ F:\\ X:\\ Y:\\ Z:\\");
        parent->DrawRectWithBorder( 204 + ((FLOAT)curSelDrive * 32), 230, 204 + ((FLOAT)curSelDrive * 32) + 32, 255, SEMITRANS_BLUE, SEMITRANS_BLUE, COLOUR_BLUE);
        break;
      }
    }
  }  
  return S_OK;
}

HRESULT CXFileBrowser::Cleanup()
{
  return S_OK;
}

void CXFileBrowser::InsertCharacterFromKeypad()
{
  string curChar;
  curChar.insert(curChar.begin(), parent->m_KeyPadWindow->curChar);
  InsertStringAtCursor(curChar);
}

void CXFileBrowser::InsertStringAtCursor(string stringToInsert)
{
  string curString;
  curString.append(selFilename);

  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() )
    curString.insert(curString.begin() + curCol, *theIterator);

    strcpy(selFilename, curString.c_str());
    MoveCursorRight();
  }
}

void CXFileBrowser::DeleteCharactersAtCursor(int count)
{
  // Delete the current character from the current string.
  // Figure out the current item
  string curString;
  curString.append(selFilename);

  // if the line is blank, just remove the entire line.
  if (curString.length() != 0)
  {
    for (int i = 0; i < count; i++)
    {
      // Now remove the current char pos, ensuring that we are not longer than the string
      if ( curCol < (signed int)curString.length() )
      {
        curString.erase(curString.begin() + curCol);
      }
    }
  }

  strcpy(selFilename, curString.c_str());

}

void CXFileBrowser::ProcessKeyboard()
{
  // Get the keystroke
  if (BrowseMode == MODE_FILENAME)
  {
    X_KEYSTROKE ks = XBInput_GetKeyboardInput();
    lastKBChar = ks.VirtualKey;
    CHAR cInputKey = lastKBChar;
    if( cInputKey != '\0' )
    {
      int lastCol = 0;
      int lastLine = 0;
      switch( cInputKey )
      {
        case KEY_HOME:
          MoveCursorToStartOfLine();
          break;
        case KEY_END:
          MoveCursorToEndOfLine();
          break;
        case KEY_BACKSPACE:
          lastCol = curCol;
          MoveCursorLeft();
          if (lastCol == curCol)
            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);
      }
    }
  }
}

void CXFileBrowser::MoveCursorToStartOfLine()
{
  curCol = 0;
}

void CXFileBrowser::MoveCursorToEndOfLine()
{
  curCol = strlen(selFilename);
}


HRESULT CXFileBrowser::FrameMove()
{
  switch (BrowseMode)
  {
    case MODE_FILENAME:
    {
      ProcessKeyboard();

      if( parent->GetGamePad()->bPressedAnalogButtons[XINPUT_GAMEPAD_B] || parent->GetGamePad()->wPressedButtons & XINPUT_GAMEPAD_BACK || lastKBChar == KEY_ESC)
      {
        BrowseMode = MODE_FILE;
      }

      parent->m_KeyPadWindow->FrameMove();
 
      // Move the keypad window
      parent->MoveKeypadWindow();
      
      if ( BeginCursorLeftRepeat() )
        MoveCursorLeft();
    
      // Only move Cursor Right if we need to.
      if ( BeginCursorRightRepeat() )
        MoveCursorRight();

      if ( BeginButtonARepeat() )
        InsertCharacterFromKeypad();

      if ( BeginButtonXRepeat() )
        DeleteCharactersAtCursor(1);

      break;
    }

    case MODE_FILE:
    {
      if( parent->GetGamePad()->bPressedAnalogButtons[XINPUT_GAMEPAD_B] || parent->GetGamePad()->wPressedButtons & XINPUT_GAMEPAD_BACK || parent->lastKBChar == KEY_ESC)
      {
        //parent->curMode = parent->Mode::MODE_NORMAL;
        //parent->ResetMenuSystem();
        opening = false;
      }

      if( parent->GetGamePad()->bPressedAnalogButtons[XINPUT_GAMEPAD_Y] || parent->lastKBChar == KEY_TAB)
      {
        // Drive select
        BrowseMode = MODE_DRIVE;
      }

      if( parent->GetGamePad()->bPressedAnalogButtons[XINPUT_GAMEPAD_A] || parent->lastKBChar == KEY_ENTER)
      {
        fileItem::iterator curFileItem = fileItems.begin() + curFileLine + curFilePage;
        // If this is a directory, browse it, if its a file open it
        if (curFileLine + curFilePage < (int)fileItems.size() )
        {
          if ((*curFileItem).isDirectory)
          {
            // Browse this.
            if (strcmp((*curFileItem).strPath, "..") == 0)
            {
              char curPathTMP[MAX_PATH];
              memset(curPathTMP, '\0', MAX_PATH);
              // need to chop \\ off end
              strncpy(curPathTMP, curPath, strlen(curPath) - 1);
              char *pdest = NULL;
              pdest = strrchr( curPathTMP, '\\');
              memset(curPath, '\0', MAX_PATH);
              strncpy(curPath, curPathTMP, strlen(curPathTMP) - strlen(pdest));
              strcat(curPath, "\\");
              curFileLine = 0;
              curFilePage = 0;
            }
            else
            {
              strcat(curPath, (*curFileItem).strPath);
              strcat(curPath, "\\");
              curFileLine = 0;
              curFilePage = 0;
            }

      
            ReadFileListing();

          }
          else
          {
            // Open the file
            char curPathTMP[MAX_PATH];
            memset(curPathTMP, '\0', MAX_PATH);
            strcpy(curPathTMP, "D:");
            strcat(curPathTMP, curPath);
            strcat(curPathTMP, (*curFileItem).strPath);

            strcpy(fileToOpen, curPathTMP);

            // are we saving as? if so show filename box.
            if (parent->curMode == CXxPad::Mode::MODE_FILESAVEAS)
            {
              memset(selFilename, 0, MAX_PATH);
              strcpy(selFilename, (*curFileItem).strPath);
              BrowseMode = MODE_FILENAME;
              curCol = 0;
              parent->ignoreInput = true;
            }
            else
            {
              opening = false;
              windowOpenAmount = 1.0f;
            }
          }
        }
      }

      // Only move Cursor Right if we need to.
      if ( BeginCursorUpRepeat() )
        MoveCursorUp();
      
      // Only move Cursor Right if we need to.
      if ( BeginCursorDownRepeat() )
        MoveCursorDown();

      break;
    }
    case MODE_DRIVE:
    {
      if( parent->GetGamePad()->bPressedAnalogButtons[XINPUT_GAMEPAD_B] || parent->GetGamePad()->wPressedButtons & XINPUT_GAMEPAD_BACK || parent->GetGamePad()->bPressedAnalogButtons[XINPUT_GAMEPAD_Y]  || parent->lastKBChar == KEY_ESC)
      {
        BrowseMode = MODE_FILE;
      }

      if( parent->GetGamePad()->wPressedButtons & XINPUT_GAMEPAD_DPAD_LEFT || parent->lastKBChar == KEY_LEFT )
      {
        if (curSelDrive >0)
          curSelDrive--;
      }

      if( parent->GetGamePad()->wPressedButtons & XINPUT_GAMEPAD_DPAD_RIGHT || parent->lastKBChar == KEY_RIGHT )
      {
        if (curSelDrive <7)
          curSelDrive++;
      }

      if( parent->GetGamePad()->bPressedAnalogButtons[XINPUT_GAMEPAD_A] || parent->lastKBChar == KEY_ENTER )
      {
        switch (curSelDrive)
        {
          case 0:
          {
            parent->ChangeDrive(&DeviceNameC);
            curDrive = 'C';
            break;
          }
          case 1:
          {
            parent->ChangeDrive(&DeviceNameD);
            curDrive = 'D';
            break;
          }
          case 2:
          {
            parent->ChangeDrive(&DeviceNameE);
            curDrive = 'E';
            break;
          }
          case 3:
          {
            parent->ChangeDrive(&DeviceNameF);
            curDrive = 'F';
            break;
          }
          case 4:
          {
            parent->ChangeDrive(&DeviceNameX);
            curDrive = 'X';
            break;
          }
          case 5:
          {
            parent->ChangeDrive(&DeviceNameY);
            curDrive = 'Y';
            break;
          }
          case 6:
          {
            parent->ChangeDrive(&DeviceNameZ);
            curDrive = 'Z';
            break;
          }
        }

        memset(curPath, 0, MAX_PATH);
        strcpy(curPath, "\\");
        curFileLine = 0;
        BrowseMode = MODE_FILE;
        ReadFileListing();
        // Drive select        "C:\\ D:\\ E:\\ F:\\ X:\\ Y:\\ Z:\\"
        
      }

      
      break;
    }
  }
  

  return S_OK;
}

bool CXFileBrowser::BeginCursorUpRepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;
  if( parent->GetGamePad()->wLastButtons & XINPUT_GAMEPAD_DPAD_UP || parent->lastKBChar == KEY_UP )
  {
    if (!movingUp)
    {
      movingUp=true;
      starttime=timeGetTime();
      delay=200;
      return true;
    }
    else
    {
      // is pressed here
      if (timeGetTime()>starttime+delay)
      {
        starttime=timeGetTime();
        delay=50;
        return true;
      }
    }
  }
  else
  {
    movingUp=false;
  }
  return false;
}

bool CXFileBrowser::BeginCursorDownRepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;
  if( parent->GetGamePad()->wLastButtons & XINPUT_GAMEPAD_DPAD_DOWN || parent->lastKBChar == KEY_DOWN )
  {
    if (!movingDown)
    {
      movingDown=true;
      starttime=timeGetTime();
      delay=200;
      return true;
    }
    else
    {
      // is pressed here
      if (timeGetTime()>starttime+delay)
      {
        starttime=timeGetTime();
        delay=50;
        return true;
      }
    }
  }
  else
  {
    movingDown=false;
  }
  
  return false;
}

bool CXFileBrowser::BeginCursorLeftRepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;
  int theKeyState = false;
  if (parent->controlMode == CXxPad::CntrlMode::CONTROL_LEFTSTICK)
    theKeyState = parent->GetGamePad()->fX1 < -0.3;
  if (parent->controlMode == CXxPad::CntrlMode::CONTROL_DPAD)
    theKeyState = parent->GetGamePad()->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 CXFileBrowser::BeginCursorRightRepeat()
{
  static DWORD starttime=0;
  static DWORD delay=0;
  int theKeyState = false;
  if (parent->controlMode == CXxPad::CntrlMode::CONTROL_LEFTSTICK)
    theKeyState = parent->GetGamePad()->fX1 > 0.3;
  if (parent->controlMode == CXxPad::CntrlMode::CONTROL_DPAD)
    theKeyState = parent->GetGamePad()->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;
}

void CXFileBrowser::MoveCursorUp(int count)
{
  if (curFileLine <3 && curFilePage > 0)
    curFilePage--;
  else if (curFileLine >0)
    curFileLine--;
}

void CXFileBrowser::MoveCursorDown(int count)
{
  if (curFileLine > 5 && curFileLine + curFilePage < (int)fileItems.size() - 5)
    curFilePage++;
  else if (curFileLine < 8 && curFileLine < (int)fileItems.size() - 1)
    curFileLine++;

}

void CXFileBrowser::MoveCursorLeft(int count)
{
  for (int i = 0; i < count; i++)
  {
    if (curCol > 0)
      curCol--;
  }
}

void CXFileBrowser::MoveCursorRight(int count)
{
  for (int i = 0; i < count; i++)
  {
    if (curCol < 25 && curCol < strlen(selFilename))
      curCol++;
  }

}

void CXFileBrowser::ReadFileListing()
{
  fileItems.clear();

  // if we are not in the root dir, then allow us an option to go down a dir
  if (strcmp(curPath , "\\") != 0)
  {
    FILELISTING fl;
    fl.isDirectory = true;
    strcpy(fl.strPath, "..");
    fileItems.push_back(fl);
  }

  char strFind[MAX_PATH] = "";
  strcat(strFind, "D:");
  strcat(strFind, curPath);
  strcat(strFind, "*.");
  AddToSearch(strFind);

  strcpy(strFind, "D:");
  strcat(strFind, curPath);
  strcat(strFind, "*.txt");
  AddToSearch(strFind);

  strcpy(strFind, "D:");
  strcat(strFind, curPath);
  strcat(strFind, "*.ini");
  AddToSearch(strFind);

  strcpy(strFind, "D:");
  strcat(strFind, curPath);
  strcat(strFind, "*.nfo");
  AddToSearch(strFind);

  strcpy(strFind, "D:");
  strcat(strFind, curPath);
  strcat(strFind, "*.log");
  AddToSearch(strFind);

  strcpy(strFind, "D:");
  strcat(strFind, curPath);
  strcat(strFind, "*.asc");
  AddToSearch(strFind);
  
  //sort(fileItems.begin(), fileItem.end());
}

void CXFileBrowser::AddToSearch(char strFind[MAX_PATH])
{
  WIN32_FIND_DATA wfd;
  HANDLE hFind;
  // Start the find and check for failure.
  hFind = FindFirstFile( strFind, &wfd );

  if( INVALID_HANDLE_VALUE == hFind )
  {
      //m_Font.DrawText(114, curY, 0xff000000, "FindFirstFile failed." );
  }
  else
  {
      // Display each file and ask for the next.
      do
      {
        FILELISTING fl;
        strcpy(fl.strPath, wfd.cFileName);
        if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
          fl.isDirectory = true;
        else
          fl.isDirectory = false;
        fileItems.push_back(fl);
      } while( FindNextFile( hFind, &wfd ) );

      // Close the find handle.
     FindClose( hFind );
  }  
}


void CXFileBrowser::ResetBrowser()
{
  ReadFileListing();
  windowOpenAmount = 0.0f;
  opening = true;
  memset(fileToOpen, 0, MAX_PATH);
}


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

  //if( m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_DOWN )
  //if (m_DefaultGamepad.fY1 < -0.3)
  if (parent->GetGamePad()->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 - parent->GetGamePad()->bAnalogButtons[XINPUT_GAMEPAD_A];
        return true;
      }
    }
  }
  else
  {
    holdingADown=false;
  }
  
  return false;
}

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

  //if( m_DefaultGamepad.wLastButtons & XINPUT_GAMEPAD_DPAD_DOWN )
  //if (m_DefaultGamepad.fY1 < -0.3)
  if (parent->GetGamePad()->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 - parent->GetGamePad()->bAnalogButtons[XINPUT_GAMEPAD_X];
        return true;
      }
    }
  }
  else
  {
    holdingXDown=false;
  }
  
  return false;
}