#define _CRT_SECURE_NO_WARNINGS
#include <conio.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define WEEK	"日 月 火 水 木 金 土"

// プロトタイプ宣言
void	dispcal(int year, int month);
void	subcal(int year, int month, int col);
void	strfmt(void* dest, char* fmt, int i);
void	normalize(int* pyear, int* pmonth);
int	scanym(char* buf, int* pyear, int* pmonth);

// グローバル変数
char disp[8][80];

int main(int argc, char* argv[])
{
	struct tm*	ptm;
	time_t		timer;
	char		buf[8];
	int		year;
	int		month;
	int		ch;
	int		flag;
	int		i;

	time(&timer);
	ptm = localtime(&timer);
	year = ptm->tm_year + 1900;
	month = ptm->tm_mon + 1;
	if (argc == 2) {
		scanym(argv[1], &year, &month);
	}
	while (1) {
		dispcal(year, month);
		printf("ESCキーで終了\n");
		i = 0;
		do {
			ch = _getch();
			flag = 0;
			switch (ch) {
			case 0x0d:	// Enter
				buf[i] = '\0';
				if (scanym(buf, &year, &month)) {
					dispcal(year, month);
					printf("年月指定に誤りがあります\n");
					_getch();
				}
				break;
			case 0x1b:	// ESC
				return 0;
			case 0x48:	// up
				month -= 3;
				break;
			case 0x49:	// PageUp
				year--;
				break;
			case 0x4b:	// left
				month--;
				break;
			case 0x4d:	// right
				month++;
				break;
			case 0x50:	// down
				month += 3;
				break;
			case 0x51:	// PageDown
				year++;
				break;
			case 0xe0:	// 読み飛ばし
			default:
				flag = 1;
			}
			if (isdigit(ch) || ch == '/') {
				if (i < 7) {
					printf("%c", ch);
					buf[i++] = ch;
				}
			}
		} while (flag);
		normalize(&year, &month);
	}
	return 0;
}

// 3ヶ月分のカレンダーを表示
void dispcal(int year, int month)
{
	int		i;

	for (i = 0; i < 8; i++) {
		memset(disp[i], ' ', 79);
		disp[i][79] = '\0';
	}
	system("cls");
	subcal(year, month - 1, 0);
	subcal(year, month, 24);
	subcal(year, month + 1, 24 * 2);
	for (i = 0; i < 8; i++) {
		printf("%s\n", disp[i]);
	}
}

// 1ヶ月分のカレンダーを作成
void subcal(int year, int month, int col)
{
	struct tm	tm;
	struct tm*	ptm;
	time_t		timer;
	int		line;
	int		i;

	normalize(&year, &month);
	strfmt(disp[0] + col, "%d", year);
	strfmt(disp[0] + col + 8, "%d月", month);
	memcpy(disp[1] + col, WEEK, strlen(WEEK));

	memset(&tm, 0, sizeof tm);
	tm.tm_year	= year - 1900;
	tm.tm_mon	= month - 1;
	tm.tm_mday	= 1;
	timer = mktime(&tm);
	line = 2;
	for (i = 1; i <= 31; i++) {
		ptm = localtime(&timer);
		if (ptm->tm_mon != month - 1) {
			break;
		}
		if (ptm->tm_wday == 0 && 1 < i) {
			line++;
		}
		strfmt(disp[line] + col + ptm->tm_wday * 3, "%2d", i);
		timer += 24 * 60 * 60;
	}
}

// 整数を指定書式でメモリにコピー
void strfmt(void* dest, char* fmt, int i)
{
	char		buf[8];

	sprintf(buf, fmt, i);
	memcpy(dest, buf, strlen(buf));
}

// 年月を正常化
void normalize(int* pyear, int* pmonth)
{
	if (*pmonth < 1) {
		*pyear--;
		*pmonth += 12;
	}
	if (12 < *pmonth) {
		*pyear++;
		*pmonth -= 12;
	}
}

// 年月を解釈
int scanym(char* buf, int* pyear, int* pmonth)
{
	struct tm*	ptm;
	time_t		timer;
	int		year;
	int		month;

	switch (sscanf(buf, "%d/%d", &year, &month)) {
	case 1:
		if (year < 100) {	// MM
			time(&timer);
			ptm	= localtime(&timer);
			month	= year;
			year	= ptm->tm_year + 1900;
		} else {		// YYYYMM
			month = year % 100;
			year /= 100;
		}
		break;
	case 2:
		if (year < 100) {	// YY/MM
			year += (year < 75) ? 2000 : 1900;
		}
		break;
	default:
		return -1;
	}

	if (year < 1975 || 3000 < year) {
		return -1;
	}
	if (month < 1 || 12 < month) {
		return -1;
	}
	*pyear	= year;
	*pmonth	= month;
	return 0;
}
