#include <Windows.h>
#include <vector>  // for rain objects
#include <conio.h>  // for keyboard input
#include <time.h>  // for timer process
#include <fstream>  // for load/save record
#include <algorithm> // for sort record


#define SCREEN_LEFT  20
#define SCREEN_RIGHT 60
#define SCREEN_WIDTH (SCREEN_RIGHT - SCREEN_LEFT)
#define PATH_RECORD  "Record"


using namespace std;




void MoveTo(int x, int y)
{
     COORD cur = { x, y };
     SetConsoleCursorPosition(
     GetStdHandle(STD_OUTPUT_HANDLE), cur);
}


int Compare(const void* a, const void* b)
{
     return (*(int*)a < *(int*)b);
}




COORD myPos;
vector<COORD> vRainPos;
int stage, playtime;
int rainLen, rainElapse;




void init_game()
{
     myPos.X = SCREEN_WIDTH / 2;
     myPos.Y = 24;
     vRainPos.clear();


     stage = 1;
     playtime = 0;
     rainLen = 1;
     rainElapse = 100;
}


void next_stage()
{
     myPos.X = SCREEN_WIDTH / 2;
     myPos.Y = 24;
     vRainPos.clear();


     stage++;
     // rainLen++;
     rainElapse *= 0.8;
}


void rain_occur()
{
     COORD coord = { (rand() % SCREEN_WIDTH), 0 };
     vRainPos.push_back(coord);
}


void rain_down()
{
     for (int i = 0; i < vRainPos.size(); i++)
     {
          vRainPos[i].Y++;

          if (vRainPos[i].Y > 24)
               vRainPos.erase(vRainPos.begin() + i);
     }
}


bool is_death()
{
     for (int i = 0; i < vRainPos.size(); i++)
     {
          if ((myPos.X == vRainPos[i].X) &&
               (myPos.Y <= vRainPos[i].Y && myPos.Y > vRainPos[i].Y - rainLen))
                   return true;
     }
     return false;
}


int update_record(int record[32])
{
     FILE* file;
     int   nElem;


     // Open record file (create if not exists)
     if (fopen_s(&file, PATH_RECORD, "r+") != 0)
          fopen_s(&file, PATH_RECORD, "w+");


     // Load record from file
     nElem = fread(record, sizeof(int), 5, file);


     // Push new record and sort
     record[nElem] = playtime;
     nElem++;
     qsort(record, nElem, sizeof(int), Compare);


     // Save updated record to file
     rewind(file);
     fwrite(record, sizeof(int), min(nElem, 5), file);
     fclose(file);

     return min(nElem, 5);
}


void onPaint()
{
     // Clear screen
     system("cls");
 
     // Display me
     MoveTo(SCREEN_LEFT + myPos.X, 24);
     _putch('@');


     // Display rains
     for (int i = 0; i < vRainPos.size(); i++)
     {
          for (int len = 0; len < rainLen; len++)
          {
               MoveTo(SCREEN_LEFT + vRainPos[i].X, vRainPos[i].Y - len);
               _putch('*');
          }
     } 

   
     // Display screen wall
     for (int y = 0; y < 25; y++)
     {
          MoveTo(SCREEN_LEFT, y);
          _putch('I');
          MoveTo(SCREEN_RIGHT, y);
          _putch('I');
     }


     // Display stage number
     MoveTo(2, 1);
     printf("[%d stage]", stage);
 
     // Display play time
     MoveTo(2, 3);
     printf("[d : d]", playtime/60, playtime`);

}


void onKeyboard()
{
     switch (tolower(_getch())) {
     case 'd':
          if (myPos.X < SCREEN_WIDTH - 1)
               myPos.X++;
           break;
     case 'a':
          if (myPos.X > 0)
               myPos.X--;
      break;
     }    
}


int main()
{
     clock_t clocks[32]; // for timer process

     srand(GetTickCount());
 
     while (true)
     {
            init_game();

      for (int i = 0; i < 32; i++)
           clocks[i] = clock();


  while (!is_death())
  {
       // Process keyboard command
       if (_kbhit())
       {
            onKeyboard();
            onPaint();
       }
       // Process rain occur
       if (clock() > clocks[1] + rainElapse)
       {
            clocks[1] = clock();
            rain_occur();
            onPaint();
       }
       // Process rain down
       if (clock() > clocks[2] + rainElapse)
       {
            clocks[2] = clock();
            rain_down();
            onPaint();
       }
       // Process stage up
       if (clock() > clocks[3] + 15000)
       {
            clocks[3] = clock();
            next_stage();
            onPaint();
       }
       // Update play time
       playtime = (clock() - clocks[0]) / 1000;
  }
  
  // Get newely updated record
  int record[32], nElem;
  nElem = update_record(record);
  
  // Display retry message box with record list
  char text[256] = { 0 }, format[32];
  for (int i = 0; i < nElem; i++)
  {
       sprintf_s(format ,"%d.  d : d \n", i+1, record[i]/60, record[i]`);
       strcat_s(text, format);
  }
  sprintf_s(format, "You : d : d \n", playtime/60, playtime`);
  strcat_s(text, format);

  if (MessageBoxA(NULL, text, "게임 오버", MB_RETRYCANCEL) == IDCANCEL)
       break;
 }


 return 0;

}