// Pierwsza, trywialna implementacja klasy Runner.
// Bez hermetyzacji, konstruktora, rozmiary ekranu zaszyte w kodzie
// w postaci literałów całkowitoliczbowych.
//
// Program stanowi uzupełnienie wykładu z programowania w językach
// C/C++. Koncepcja klasy Runner została omówiona w materiałach wykładowych
//
// Autor: Roman Siminski

// Uwaga! Implementacja tylko dla Windows, obsługa konsoli realizowana za
// pośrednictwem WinApi. Najlepiej kompilować używając MinGW (środowiska
// Code::Blocka, DevC++) lub kompilatorów w środowisku C++ Builder. Proszę
// unikać środowisk VisualC++, CLion, QT, będą stwarzać problemy.

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <ctime>
#include <conio.h>

#if defined(__MINGW32__) || defined(_MSC_VER)
#include <windows.h>
#endif

// Kody klawiszy sterujących kursorem i klawiszy specjalnych
enum KEY_CODES
{
#ifdef __BORLANDC__
	KEY_BLANK = 0x0000,
#elif (defined(__GNUC__) && defined(__MINGW32__)) || defined(_MSC_VER)
	KEY_BLANK = 0x00e0,
#endif
	KEY_UP = 0x4800,
	KEY_DOWN = 0x5000,
	KEY_LEFT = 0x4b00,
	KEY_RIGHT = 0x4d00,
	KEY_ESC = 0x001b
};

// Prototypy funkcji obsługi konsoli
int getKey();
void clearScreen();
void writeCharXY(int x, int y, char c);
void writeStrXY(int x, int y, char s[]);
void writeIntXY(int x, int y, int i);
void writeDoubleXY(int x, int y, double d);
void cursorOff();
void cursorOn();

class Runner
{
  public :
    int  x, y;
    char shape;

    void show();
    void hide();

    void moveUp();
    void moveDown();
    void moveLeft();
    void moveRight();
};

void Runner::show()
{
  writeCharXY( x, y, shape );
}

void Runner::hide()
{
  writeCharXY( x, y, ' ' );
}

void Runner::moveUp()
{
  hide();
  if( y > 1 )
    --y;
  show();
}

void Runner::moveDown()
{
  hide();
  if( y < 24 )
    ++y;
  show();
}

void Runner::moveLeft()
{
  hide();
  if( x > 1 )
    --x;
  show();
}

void Runner::moveRight()
{
  hide();
  if( x < 80 )
    ++x;
  show();
}

int main()
{
  int  key;
  Runner R;

  R.x = 40;
  R.y = 12;
  R.shape = '*';

  clearScreen();
  cursorOff();
  R.show();

  do
  {
    key = getKey();
    switch( key )
    {
      case KEY_UP    : R.moveUp();
                       break;
      case KEY_DOWN  : R.moveDown();
                       break;
      case KEY_LEFT  : R.moveLeft();
                       break;
      case KEY_RIGHT : R.moveRight();
                     break;
    }//switch
  }
  while( key != KEY_ESC );

  clearScreen();
  cursorOn();

  return EXIT_SUCCESS;
}

// Funkcje obsługi konsoli, sprawa techniczna, w sensie merytorycznym nieistotna
int getKey()
{
	int key = _getch();
	return (key == KEY_BLANK) ? _getch() << 8 : key;
}

void clearScreen()
{
#ifdef __BORLANDC__
	clrscr();
#elif (defined(__GNUC__) && defined(__MINGW32__)) || defined(_MSC_VER)
	COORD leftTop = { 0, 0 };
	CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
	int numOfCells = 80 * 25;
	DWORD writtenItems;
	HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
	if (GetConsoleScreenBufferInfo(consoleHandle, &consoleInfo))
		numOfCells = consoleInfo.dwSize.X * consoleInfo.dwSize.Y;

	FillConsoleOutputAttribute(consoleHandle, 0xf,
		numOfCells, leftTop, &writtenItems);
	FillConsoleOutputCharacter(consoleHandle, ' ',
		numOfCells, leftTop, &writtenItems);
#else
#error "Nieobslugiwana platforma"
#endif
}

void writeCharXY(int x, int y, char c) {
#ifdef __BORLANDC__
	gotoxy(x, y);
	putch(c);
#elif (defined(__GNUC__) && defined(__MINGW32__)) || defined(_MSC_VER)
	COORD cursorPos;
	DWORD written;

	cursorPos.X = x - 1;
	cursorPos.Y = y - 1;

	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), cursorPos);
	WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), &c, 1, &written, 0);
#else
#error "Nieobslugiwana platforma"
#endif
}

void writeStrXY(int x, int y, char s[])
{
#ifdef __BORLANDC__
	gotoxy(x, y);
	cputs(s);
#elif (defined(__GNUC__) && defined(__MINGW32__)) || defined(_MSC_VER)
	COORD cursorPos;
	DWORD written;

	cursorPos.X = x - 1;
	cursorPos.Y = y - 1;

	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), cursorPos);
	WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), s, strlen(s), &written, 0);
#else
#error "Nieobslugiwana platforma"
#endif
}

void writeIntXY(int x, int y, int i)
{
	char s[80];
#if defined(_MSC_VER)
	sprintf_s(s, 80, "%d", i);
#else
	sprintf(s, "%d", i);
#endif
	writeStrXY(x, y, s);
}

void writeDoubleXY(int x, int y, double d)
{
	char s[80];
#if defined(_MSC_VER)
	sprintf_s(s, 80, "%g", d);
#else
	sprintf(s, "%g", d);
#endif
	writeStrXY(x, y, s);
}

void cursorOff()
{
#ifdef __BORLANDC__
	_setcursortype(_NOCURSOR);
#elif (defined(__GNUC__) && defined(__MINGW32__)) || defined(_MSC_VER)
	CONSOLE_CURSOR_INFO cursorInfo;
	HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
	GetConsoleCursorInfo(consoleHandle, &cursorInfo);
	cursorInfo.bVisible = false;
	SetConsoleCursorInfo(consoleHandle, &cursorInfo);
#else
#error "Nieobslugiwana platforma"
#endif
}

void cursorOn()
{
#ifdef __BORLANDC__
	_setcursortype(_NORMALCURSOR);
#elif (defined(__GNUC__) && defined(__MINGW32__)) || defined(_MSC_VER)
	CONSOLE_CURSOR_INFO cursorInfo;
	HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
	GetConsoleCursorInfo(consoleHandle, &cursorInfo);
	cursorInfo.bVisible = true;
	SetConsoleCursorInfo(consoleHandle, &cursorInfo);
#else
#error "Nieobslugiwana platforma"
#endif
}

