Project

[JSON을 이용한 도서 관리 프로그램]

chyam_eun 2025. 1. 20. 22:23

이전에 텍스트파일 읽기를 통해 도서관리 프로그램을 만들었습니다. 이번에는 이 코드를 JSON을 이용하여 수정해주었습니다!

 

아래의 사이트를 참조하여 코드를 수정해주었습니다.

https://dojang.io/mod/page/view.php?id=729

 

C 언어 코딩 도장: 84.0 실전 예제: GitHub의 소스 코드 사용하기

이번에는 GitHub에 있는 소스 코드를 사용하여 JSON 문서를 파싱해보겠습니다. GitHub에는 수 많은 JSON 파서가 있지만 그중에서 parson을 사용해보겠습니다. 웹 브라우저에서 다음 주소에 접속하여 소

dojang.io

https://github.com/kgabis/parson

 

GitHub - kgabis/parson: Lightweight JSON library written in C.

Lightweight JSON library written in C. Contribute to kgabis/parson development by creating an account on GitHub.

github.com

위의 소스코드를 통해 JSON파서를 사용하였습니다.

UI는 동일하게 제작해주었습니다.

전체 도서 목록을 나열해주고, 여기서는 도서명을 변경하거나 삭제할 수 있습니다.

추천도서는 검색이 5회이상인 도서들을 출력해주었고,

검색은 작가명 혹은 도서명으로 검색이 가능합니다.

대출과 반납은 도서 목록을 불러와 대출 가능여부를 보여준 뒤 대출/반납을 할수있습니다. 


먼저 헤더나 미리 정의할것들은 아래와 같습니다. UP, DOWN, ENTER 옆의 숫자는 방향키를 누르면 나오는 숫자입니다. JSON의 사용을 위해 깃허브에서 다운받은 parson.h도 추가해줍니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "parson.h"

#define UP 72
#define DOWN 80
#define ENTER 13

그리고 커서 이동을 위해 함수를 작성해줍니다. 이를통해 방향키와 엔터키를 사용하여 목록을 선택할 수 있습니다. GotoXY는 커서를 x,y위치로 옮기는것입니다.

HideCursor는 깜빡이는 커서를 안보이게 해줍니다.

void GotoXY(int x, int y) { //커서이동함수
    COORD Cur = { x,y };
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Cur);
}

void HideCursor() {
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cursorInfo;

    GetConsoleCursorInfo(hConsole, &cursorInfo); // 현재 커서 정보를 가져옴
    cursorInfo.bVisible = FALSE;                // 커서를 숨김
    SetConsoleCursorInfo(hConsole, &cursorInfo); // 변경된 커서 정보 설정
}

 

그다음으로 함수 원형들을 선언해줍니다.

void Menu(); //전체 메뉴
int SelectMenu(); //메뉴 선택하기
void AddBook(JSON_Array* array, JSON_Value* rootValue); //도서 추가
void SearchBooks(JSON_Array* array, JSON_Value* rootValue); //도서 검색
void SearchAuthor(JSON_Array* array, JSON_Value* rootValue); //작가명으로 검색
void SearchBookname(JSON_Array* array, JSON_Value* rootValue); //도서명으로 검색
void Rent(JSON_Array* array, JSON_Value* rootValue); //대출/반납
void PrintBook(JSON_Array* array, JSON_Value* rootValue); // 파일에서 책 읽어오기
void RecommendBook(JSON_Array* array, JSON_Value* rootValue); //추천 도서 목록
void UpdateBook(JSON_Array* array, JSON_Value* rootValue, size_t size); //편집

메인함수는 아래와 같습니다.

int main() {
    JSON_Value* rootValue = json_parse_file("Book.json"); // JSON파일을 읽어서 파싱
    JSON_Object* rootObject = json_value_get_object(rootValue); // 위를 통해 object 포인터 얻기
    if (rootValue == NULL) { # 파일이 비어있으면
        printf("도서 목록이 비어있습니다.\n");
        return 1;
    }
    JSON_Array* array = json_value_get_array(rootValue); // JSON의 배열들을 불러옵니다
    if (array == NULL) { // 배열이 비어있다면
        printf("도서 목록이 비어있습니다.\n");
        json_value_free(rootValue); // 동적 메모리 해제
        return 1;
    }
    int selectedMenu;
    HideCursor();
    while (1) {
        selectedMenu = SelectMenu(); // 메뉴 선택
        system("cls");
        switch (selectedMenu) {
        case 1:
            PrintBook(array, rootValue); // 전체 도서 출력
            break;
        case 2:
            RecommendBook(array, rootValue); // 추천 도서 출력
            break;
        case 3:
            AddBook(array, rootValue); // 도서 추가
            break;
        case 4:
            SearchBooks(array, rootValue); // 도서 검색
            break;
        case 5:
            Rent(array, rootValue); // 대출/반납
            break;
        case 6:
            //초기화
            rootValue = json_value_init_object();             // JSON_Value 생성 및 초기화
            rootObject = json_value_get_object(rootValue);    // JSON_Value에서 JSON_Object를 얻음
            json_serialize_to_file_pretty(rootValue, "Book.json"); // 사람이 읽기 쉬운 문자열로 만든뒤 파일에 저장
            json_value_free(rootValue); // 동적 메모리 해제
            array = json_value_get_array(rootValue); // 배열에 초기화된 배열 저장하기
            printf("초기화 완료\n");
            break;
        case 7: // 종료
            return 0;
        }
        printf("\n 엔터를 눌러 메뉴로 돌아갑니다");
        getchar();
        system("cls");
    }
    json_value_free(rootValue); // 동적 메모리 해제
    return 0;
}

아래는 전체 도서를 출력해줍니다. 

먼저 배열의 크기를 구해준 뒤 0이면 배열이 없는것이므로 바로 리턴해줍니다.

for문을 통해 배열의 i번째 인덱스를 통해 키가 Title인것과 Author인것의 값을 title과 author으로 저장한 뒤 출력해줍니다.

이를 화살표키를 통해 도서들에 접근하여 편집또는 삭제, 메뉴로 돌아가기를 실행할 수 있습니다.

void PrintBook(JSON_Array* array, JSON_Value* rootValue) {
    size_t array_size = json_array_get_count(array);
    if (array_size == 0) {
        printf("Failed to get JSON array.\n");
        return 1;
    }
    printf("========================도서 목록=========================\n");
    for (size_t i = 0; i < array_size; i++) {
        JSON_Object* obj = json_array_get_object(array, i);
        if (obj != NULL) {
            const char* title = json_object_get_string(obj, "Title");
            const char* author = json_object_get_string(obj, "Author");
            printf("    Title: %s, Author: %s\n", title, author);
            printf("----------------------------------------------------------\n");
        }
    }
    int getC = 1, mY = 1;
    int edit = 0;
    size_t size = 0;
    GotoXY(1, mY);
    printf("▶");
    while (getC != ENTER) {
        if (_kbhit()) {
            getC = _getch();
            switch (getC) {
            case UP:
                if (size > 0) {
                    GotoXY(1, mY);
                    printf("  ");
                    mY -= 2;
                    GotoXY(1, mY);
                    printf("▶");
                    size -= 2;
                }
                break;
            case DOWN:
                if (size < array_size) {
                    GotoXY(1, mY);
                    printf("  ");
                    mY += 2;
                    GotoXY(1, mY);
                    printf("▶");
                    size += 2;
                }
                break;
            case ENTER:
                system("cls");
                printf("편집을 원하시면 1, 삭제를 원하시면 2, 메뉴로 돌아가기를 원하시면 3을 입력해주세요 :");
                scanf("%d", &edit);
                getchar();

                system("cls");
                if (edit == 1) {
                    system("cls");
                    UpdateBook(array, rootValue, size);
                }
                else if (edit == 2) {
                    printf("도서를 삭제합니다.\n");
                    //도서 삭제
                    json_array_remove(array, size / 2);
                    json_serialize_to_file_pretty(rootValue, "Book.json");
                    printf("도서가 삭제되었습니다!\n");
                }
                break;
            }
        }
    }
}

아래는 화살표를 통해 각 메뉴들에 접근하도록 하는 코드입니다. 1보다 크면 위로 갈 수 있고,

7보다 작을때는 아래로 갈 수 있습니다. 세모를 통해 현재 커서의 위치를 알 수 있습니다.

int SelectMenu() {
    Menu();
    int menuSelect = 1; //메뉴 선택
    int getC = 1, mY = 3;

    GotoXY(6, mY);
    printf("▶");
    while (getC != ENTER) {
        if (_kbhit()) { //키보드가 입력된 상태인가?
            getC = _getch(); //누른거 아스키코드로 받음
            switch (getC) {
            case UP:
                if (menuSelect > 1) {
                    GotoXY(6, mY);
                    printf("    ");
                    mY -= 2;
                    GotoXY(6, mY);
                    printf("▶");
                    menuSelect -= 1;
                }
                break;
            case DOWN:
                if (menuSelect < 7) {
                    GotoXY(6, mY);
                    printf("    ");
                    mY += 2;
                    GotoXY(6, mY);
                    printf("▶");
                    menuSelect += 1;
                }
                break;
            }
        }
    }
    return menuSelect;
}

아래는 도서를 추가해주는 코드입니다.

도서명과 작가명을 입력받아 새로운 객체를 생성하여 그곳에 값을 추가해준 뒤 배열에 새 객체를 추가해줍니다.

마지막으로는 JSON파일에 저장해줍니다.

void AddBook(JSON_Array* array, JSON_Value* rootValue) {
    char title[100];
    char author[100];

    printf("추가할 도서 제목을 입력하세요: ");
    fgets(title, sizeof(title), stdin);
    title[strcspn(title, "\n")] = '\0'; // 개행 문자 제거

    printf("추가할 도서 작가명을 입력하세요: ");
    fgets(author, sizeof(author), stdin);
    author[strcspn(author, "\n")] = '\0'; // 개행 문자 제거

    // 새 객체 생성
    JSON_Value* newBookValue = json_value_init_object();
    JSON_Object* newBook = json_value_get_object(newBookValue);

    // 새 객체에 값 추가
    json_object_set_string(newBook, "Title", title);
    json_object_set_string(newBook, "Author", author);

    // 배열에 새 객체 추가
    json_array_append_value(array, newBookValue);

    // 수정된 JSON을 파일에 저장
    json_serialize_to_file_pretty(rootValue, "Book.json");
    printf("도서가 추가되었습니다!\n");
}

아래는 도서명이나 작가명을 편집하는 코드입니다. 

선택한 도서의 인덱스를 가져와서 수정할 이름을 입력받아 값을 변경하여 JSON파일에 저장해줍니다.

void UpdateBook(JSON_Array* array, JSON_Value* rootValue, size_t size) {
    size_t array_size = json_array_get_count(array);
    if (array_size == 0) {
        printf("수정할 도서가 없습니다.\n");
        return;
    }
    size_t index = size / 2;
    // 선택된 도서 가져오기
    JSON_Object* obj = json_array_get_object(array, index);
    if (obj == NULL) {
        printf("도서를 가져오는 데 실패했습니다.\n");
        return;
    }

    // 수정할 키 선택
    printf("제목을 수정하고 싶으면 1, 작가명을 수정하고 싶으면 2,메뉴로 돌아가고 싶으면 3을 눌러주세요 :\n");
    int choice;
    scanf("%d", &choice);
    getchar(); // 입력 버퍼 비우기

    char newValue[100]; // 수정할 새 값
    switch (choice) {
    case 1:
        printf("새로운 제목을 입력하세요: ");
        fgets(newValue, sizeof(newValue), stdin);
        newValue[strcspn(newValue, "\n")] = '\0'; // 개행 문자 제거
        json_object_set_string(obj, "Title", newValue);
        break;
    case 2:
        printf("새로운 작가명을 입력하세요: ");
        fgets(newValue, sizeof(newValue), stdin);
        newValue[strcspn(newValue, "\n")] = '\0'; // 개행 문자 제거
        json_object_set_string(obj, "Author", newValue);
        break;
    case 3:
        printf("수정이 취소되었습니다.\n");
        return;
    default:
        printf("잘못된 선택입니다.\n");
        return;
    }

    // 수정된 JSON을 파일에 저장
    json_serialize_to_file_pretty(rootValue, "Book.json");
    printf("도서 정보가 수정되었습니다!\n");
}

아래는 추천도서 코드입니다. 아직 나오진 않았지만 검색할때마다 Count를 증가시키도록 하였습니다. 이때의 값을 불러와서 5보다 크면 출력되도록 해주었습니다.

void RecommendBook(JSON_Array* array, JSON_Value* rootValue) {
    size_t array_size = json_array_get_count(array);
    if (array_size == 0) {
        printf("도서 목록이 비어있습니다.\n");
        return 1;
    }
    printf("==================== 추천 도서 목록====================\n");
    for (size_t i = 0; i < array_size; i++) {
        JSON_Object* obj = json_array_get_object(array, i);
        if (obj != NULL) {
            const char* title = json_object_get_string(obj, "Title");
            const char* author = json_object_get_string(obj, "Author");
            double cnt = json_object_get_number(obj, "Count");
            if (cnt > 5) {

                printf("    Title: %s, Author: %s\n", title, author);
                printf("-------------------------------------------------------\n");
            }

        }
    }
}

아래는 대출/반납에 관련된 코드입니다.

1을 누르면 대출, 2를 누르면 반납을 할 수 있습니다. 이때 기본 Rent 값은 0이고, 이 값이 0일때 대출이 가능합니다. 대출할때 Rent가 1이면 대출 불가능이라 표시됩니다.

Rent 값을 변경하고 바로 JSON 파일에 저장해주어야합니다.

void Rent(JSON_Array* array, JSON_Value* rootValue) {
    size_t array_size = json_array_get_count(array);
    if (array_size == 0) {
        printf("대출/반납 가능한 도서가 없습니다.\n");
        return 1;
    }
    int getC = 1, mY = 2;
    int want = 0;
    int size = 0;
    printf("도서 대출을 원하시면 1, 반납을 원하시면 2를 입력해주세요 :");
    scanf("%d", &want);
    getchar();
    if (want == 2) {
        int able = 0;
        printf("=======================도서 목록============================\n");
        for (size_t i = 0; i < array_size; i++) {
            JSON_Object* obj = json_array_get_object(array, i);
            if (obj != NULL) {
                const char* title = json_object_get_string(obj, "Title");
                const char* author = json_object_get_string(obj, "Author");
                double rent = json_object_get_number(obj, "Rent");
                if (rent == 0.0f) {
                    printf("    Title: %s, Author: %s, 대출 가능\n", title, author);
                    printf("-------------------------------------------------------\n");
                }
                else if (rent == 1.0f) {
                    printf("    Title: %s, Author: %s, 대출 불가능\n", title, author);
                    printf("-------------------------------------------------------\n");
                }

            }
        }
        GotoXY(1, mY);
        printf("▶");
        while (getC != ENTER) {
            if (_kbhit()) {
                getC = _getch();
                switch (getC) {
                case UP:
                    if (size > 0) {
                        GotoXY(1, mY);
                        printf("  ");
                        mY -= 2;
                        GotoXY(1, mY);
                        printf("▶");
                        size -= 2;
                    }
                    break;
                case DOWN:
                    if (size < array_size) {
                        GotoXY(1, mY);
                        printf("  ");
                        mY += 2;
                        GotoXY(1, mY);
                        printf("▶");
                        size += 2;
                    }
                    break;
                case ENTER: //반납
                    system("cls");
                    JSON_Object* obj = json_array_get_object(array, size / 2);
                    double rent = json_object_get_number(obj, "Rent");
                    if (rent == 1.0f) {
                        rent = 0.0f;
                        json_object_set_number(obj, "Rent", rent);
                        printf("반납되었습니다! 감사합니다.\n");
                        array = json_value_get_array(rootValue);
                        json_serialize_to_file_pretty(rootValue, "Book.json");
                        break;
                    }
                    else {
                        printf("대출중인 도서가 아닙니다.\n");
                        break;
                    }

                }
            }
        }
    }
    else if (want == 1) {
        size_t array_size = json_array_get_count(array);
        if (array_size == 0) {
            printf("대출/반납 가능한 도서가 없습니다.\n");
            return 1;
        }

        printf("=======================도서 목록============================\n");
        for (size_t i = 0; i < array_size; i++) {
            JSON_Object* obj = json_array_get_object(array, i);
            if (obj != NULL) {
                const char* title = json_object_get_string(obj, "Title");
                const char* author = json_object_get_string(obj, "Author");
                double rent = json_object_get_number(obj, "Rent");
                if (rent == 0.0f) {
                    printf("    Title: %s, Author: %s, 대출 가능\n", title, author);
                    printf("-------------------------------------------------------\n");
                }
                else if (rent == 1.0f) {
                    printf("    Title: %s, Author: %s, 대출 불가능\n", title, author);
                    printf("-------------------------------------------------------\n");
                }

            }
        }

        GotoXY(1, mY);
        printf("▶");
        GotoXY(1, mY);
        printf("▶");
        while (getC != ENTER) {
            if (_kbhit()) {
                getC = _getch();
                switch (getC) {
                case UP:
                    if (size > 0) {
                        GotoXY(1, mY);
                        printf("  ");
                        mY -= 2;
                        GotoXY(1, mY);
                        printf("▶");
                        size -= 2;
                    }
                    break;
                case DOWN:
                    if (size < array_size) {
                        GotoXY(1, mY);
                        printf("  ");
                        mY += 2;
                        GotoXY(1, mY);
                        printf("▶");
                        size += 2;
                    }
                    break;
                case ENTER: //대출
                    system("cls");
                    JSON_Object* obj = json_array_get_object(array, size / 2);
                    double rent = json_object_get_number(obj, "Rent");
                    if (rent == 1.0f) {
                        printf("이미 대출중인 도서입니다.\n");
                        break;
                    }
                    else {
                        rent = 1.0f;
                        json_object_set_number(obj, "Rent", rent);
                        printf("대출되었습니다!\n");
                        array = json_value_get_array(rootValue);
                        json_serialize_to_file_pretty(rootValue, "Book.json");
                        break;
                    }

                }
            }
        }
    }
    else {
        printf("유효하지 않은 문자입니다.\n");
    }

}

아래는 도서를 검색하는 코드입니다. 커서를 이용하여 1번부터 3번까지 접근가능합니다.

void SearchBooks(JSON_Array* array, JSON_Value* rootValue) {
    printf("    1. 작가명으로 찾기 \n");
    printf("    2. 도서명으로 찾기 \n");
    printf("    3. 메뉴로 나가기 \n");

    int getC = 1, mY = 0;
    int select = 1;
    GotoXY(1, mY);
    printf("▶");
    while (getC != ENTER) {
        if (_kbhit()) {
            getC = _getch();
            switch (getC) {
            case UP:
                if (select > 1) {
                    GotoXY(1, mY);
                    printf("  ");
                    mY -= 1;
                    GotoXY(1, mY);
                    printf("▶");
                    select -= 1;
                }
                break;
            case DOWN:
                if (select < 3) {
                    GotoXY(1, mY);
                    printf("  ");
                    mY += 1;
                    GotoXY(1, mY);
                    printf("▶");
                    select += 1;
                }
                break;
            case ENTER:
                switch (select) {
                case 1:
                    SearchAuthor(array, rootValue);
                    break;
                case 2:
                    SearchBookname(array, rootValue);
                    break;
                }
            }
        }
    }
}

아래는 작가명으로 검색하는 코드입니다. 

입력받은 작가와 같은 작가가 있다면 출력해주고, Count를 증가시킨뒤 저장해줍니다.

void SearchAuthor(JSON_Array* array, JSON_Value* rootValue) {
    system("cls");
    size_t array_size = json_array_get_count(array);
    if (array_size == 0) {
        printf("Failed to get JSON array.\n");
        return 1;
    }
    // 작가명으로 검색할 수 있는 기능 추가
    char bookAuthor[100];
    int found = 0;
    printf("작가명을 입력해주세요 : ");
    fgets(bookAuthor, sizeof(bookAuthor), stdin);
    bookAuthor[strcspn(bookAuthor, "\n")] = '\0';

    for (size_t i = 0; i < array_size; i++) {
        JSON_Object* obj = json_array_get_object(array, i);
        if (obj != NULL) {
            const char* title = json_object_get_string(obj, "Title");
            const char* author = json_object_get_string(obj, "Author");
            double cnt = json_object_get_number(obj, "Count");
            if (strcmp(author, bookAuthor) == 0) {
                printf("----------------------------------------------------------\n");
                printf("    Title: %s, Author: %s\n", title, author);
                printf("----------------------------------------------------------\n");
                cnt = cnt + 1.0f;
                json_object_set_number(obj, "Count", cnt);
                found = 1;
                break;
            }

        }
    }


    if (!found) {
        printf("찾으시는 작가님이 없습니다.\n");
    }
    json_serialize_to_file_pretty(rootValue, "Book.json");
}

아래는 위의 코드에서 도서명으로 바꿔주면 같습니다.

void SearchBookname(JSON_Array* array, JSON_Value* rootValue) {
    system("cls");
    size_t array_size = json_array_get_count(array);
    if (array_size == 0) {
        printf("도서 목록이 비어있습니다.\n");
        return 1;
    }
    char bookName[100];
    int able = 0;
    printf("도서명을 입력해주세요 : ");
    fgets(bookName, sizeof(bookName), stdin);
    bookName[strcspn(bookName, "\n")] = '\0';

    for (size_t i = 0; i < array_size; i++) {
        JSON_Object* obj = json_array_get_object(array, i);
        if (obj != NULL) {
            const char* title = json_object_get_string(obj, "Title");
            const char* author = json_object_get_string(obj, "Author");
            double cnt = json_object_get_number(obj, "Count");
            if (strcmp(title, bookName) == 0) {
                printf("----------------------------------------------------------\n");
                printf("    Title: %s, Author: %s\n", title, author);
                printf("----------------------------------------------------------\n");
                cnt = cnt + 1.0f;
                printf("%lf", cnt);
                json_object_set_number(obj, "Count", cnt);
                able = 1;
            }

        }
    }
    if (!able) {
        printf("찾으시는 도서가 없습니다.\n");
    }
    json_serialize_to_file_pretty(rootValue, "Book.json");
}

아래는 처음에 출력되는 메뉴입니다.

void Menu() {
    printf("********************************************\n");
    printf("              도서 검색 프로그램           \n");
    printf("********************************************\n");
    printf("           1 : 전체 도서 목록              \n");
    printf("--------------------------------------------\n");
    printf("           2 : 추천 도서 목록              \n");
    printf("--------------------------------------------\n");
    printf("           3 : 도서 추가                   \n");
    printf("--------------------------------------------\n");
    printf("           4 : 도서 검색                   \n");
    printf("--------------------------------------------\n");
    printf("           5 : 도서 대출/반납              \n");
    printf("--------------------------------------------\n");
    printf("           6 : 초기화                   \n");
    printf("--------------------------------------------\n");
    printf("           7 : 종료 및 저장                   \n");
    printf("********************************************\n");
}

 


아래는 전체 코드입니다.

더보기

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "parson.h"

#define UP 72
#define DOWN 80
#define ENTER 13

void GotoXY(int x, int y) { //커서이동함수
    COORD Cur = { x,y };
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Cur);
}

void HideCursor() {
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cursorInfo;

    GetConsoleCursorInfo(hConsole, &cursorInfo); // 현재 커서 정보를 가져옴
    cursorInfo.bVisible = FALSE;                // 커서를 숨김
    SetConsoleCursorInfo(hConsole, &cursorInfo); // 변경된 커서 정보 설정
}

void Menu(); //전체 메뉴
int SelectMenu(); //메뉴 선택하기
void AddBook(JSON_Array* array, JSON_Value* rootValue); //도서 추가
void SearchBooks(JSON_Array* array, JSON_Value* rootValue); //도서 검색
void SearchAuthor(JSON_Array* array, JSON_Value* rootValue); //작가명으로 검색
void SearchBookname(JSON_Array* array, JSON_Value* rootValue); //도서명으로 검색
void Rent(JSON_Array* array, JSON_Value* rootValue); //대출/반납
void PrintBook(JSON_Array* array, JSON_Value* rootValue); // 파일에서 책 읽어오기
void RecommendBook(JSON_Array* array, JSON_Value* rootValue); //추천 도서 목록
void UpdateBook(JSON_Array* array, JSON_Value* rootValue, size_t size); //편집

int main() {
    JSON_Value* rootValue = json_parse_file("Book.json");
    JSON_Object* rootObject = json_value_get_object(rootValue);
    if (rootValue == NULL) {
        printf("도서 목록이 비어있습니다.\n");
        return 1;
    }
    JSON_Array* array = json_value_get_array(rootValue);
    if (array == NULL) {
        printf("Failed to get JSON array.\n");
        json_value_free(rootValue);
        return 1;
    }
    int selectedMenu;
    HideCursor();
    while (1) {
        selectedMenu = SelectMenu();
        system("cls");
        switch (selectedMenu) {
        case 1:
            PrintBook(array, rootValue);
            break;
        case 2:
            RecommendBook(array, rootValue);
            break;
        case 3:
            AddBook(array, rootValue);
            break;
        case 4:
            SearchBooks(array, rootValue);
            break;
        case 5:
            Rent(array, rootValue);
            break;
        case 6:
            //초기화
            rootValue = json_value_init_object();             // JSON_Value 생성 및 초기화
            rootObject = json_value_get_object(rootValue);    // JSON_Value에서 JSON_Object를 얻음
            json_serialize_to_file_pretty(rootValue, "Book.json");
            json_value_free(rootValue);
            array = json_value_get_array(rootValue);
            printf("초기화 완료\n");
            break;
        case 7:
            return 0;
        }
        printf("\n 엔터를 눌러 메뉴로 돌아갑니다");
        getchar();
        system("cls");
    }
    json_value_free(rootValue);
    return 0;
}

void PrintBook(JSON_Array* array, JSON_Value* rootValue) {
    size_t array_size = json_array_get_count(array);
    if (array_size == 0) {
        printf("Failed to get JSON array.\n");
        return 1;
    }
    printf("========================도서 목록=========================\n");
    for (size_t i = 0; i < array_size; i++) {
        JSON_Object* obj = json_array_get_object(array, i);
        if (obj != NULL) {
            const char* title = json_object_get_string(obj, "Title");
            const char* author = json_object_get_string(obj, "Author");
            printf("    Title: %s, Author: %s\n", title, author);
            printf("----------------------------------------------------------\n");
        }
    }
    int getC = 1, mY = 1;
    int edit = 0;
    size_t size = 0;
    GotoXY(1, mY);
    printf("▶");
    while (getC != ENTER) {
        if (_kbhit()) {
            getC = _getch();
            switch (getC) {
            case UP:
                if (size > 0) {
                    GotoXY(1, mY);
                    printf("  ");
                    mY -= 2;
                    GotoXY(1, mY);
                    printf("▶");
                    size -= 2;
                }
                break;
            case DOWN:
                if (size < array_size) {
                    GotoXY(1, mY);
                    printf("  ");
                    mY += 2;
                    GotoXY(1, mY);
                    printf("▶");
                    size += 2;
                }
                break;
            case ENTER:
                system("cls");
                printf("편집을 원하시면 1, 삭제를 원하시면 2, 메뉴로 돌아가기를 원하시면 3을 입력해주세요 :");
                scanf("%d", &edit);
                getchar();

                system("cls");
                if (edit == 1) {
                    system("cls");
                    UpdateBook(array, rootValue, size);
                }
                else if (edit == 2) {
                    printf("도서를 삭제합니다.\n");
                    //도서 삭제
                    json_array_remove(array, size / 2);
                    json_serialize_to_file_pretty(rootValue, "Book.json");
                    printf("도서가 삭제되었습니다!\n");
                }
                break;
            }
        }
    }
}

int SelectMenu() {
    Menu();
    int menuSelect = 1; //메뉴 선택
    int getC = 1, mY = 3;

    GotoXY(6, mY);
    printf("▶");
    while (getC != ENTER) {
        if (_kbhit()) { //키보드가 입력된 상태인가?
            getC = _getch(); //누른거 아스키코드로 받음
            switch (getC) {
            case UP:
                if (menuSelect > 1) {
                    GotoXY(6, mY);
                    printf("    ");
                    mY -= 2;
                    GotoXY(6, mY);
                    printf("▶");
                    menuSelect -= 1;
                }
                break;
            case DOWN:
                if (menuSelect < 7) {
                    GotoXY(6, mY);
                    printf("    ");
                    mY += 2;
                    GotoXY(6, mY);
                    printf("▶");
                    menuSelect += 1;
                }
                break;
            }
        }
    }
    return menuSelect;
}

void AddBook(JSON_Array* array, JSON_Value* rootValue) {
    char title[100];
    char author[100];

    printf("추가할 도서 제목을 입력하세요: ");
    fgets(title, sizeof(title), stdin);
    title[strcspn(title, "\n")] = '\0'; // 개행 문자 제거

    printf("추가할 도서 작가명을 입력하세요: ");
    fgets(author, sizeof(author), stdin);
    author[strcspn(author, "\n")] = '\0'; // 개행 문자 제거

    // 새 객체 생성
    JSON_Value* newBookValue = json_value_init_object();
    JSON_Object* newBook = json_value_get_object(newBookValue);

    // 새 객체에 값 추가
    json_object_set_string(newBook, "Title", title);
    json_object_set_string(newBook, "Author", author);

    // 배열에 새 객체 추가
    json_array_append_value(array, newBookValue);

    // 수정된 JSON을 파일에 저장
    json_serialize_to_file_pretty(rootValue, "Book.json");
    printf("도서가 추가되었습니다!\n");
}

void UpdateBook(JSON_Array* array, JSON_Value* rootValue, size_t size) {
    size_t array_size = json_array_get_count(array);
    if (array_size == 0) {
        printf("수정할 도서가 없습니다.\n");
        return;
    }
    size_t index = size / 2;
    // 선택된 도서 가져오기
    JSON_Object* obj = json_array_get_object(array, index);
    if (obj == NULL) {
        printf("도서를 가져오는 데 실패했습니다.\n");
        return;
    }

    // 수정할 키 선택
    printf("제목을 수정하고 싶으면 1, 작가명을 수정하고 싶으면 2,메뉴로 돌아가고 싶으면 3을 눌러주세요 :\n");
    int choice;
    scanf("%d", &choice);
    getchar(); // 입력 버퍼 비우기

    char newValue[100]; // 수정할 새 값
    switch (choice) {
    case 1:
        printf("새로운 제목을 입력하세요: ");
        fgets(newValue, sizeof(newValue), stdin);
        newValue[strcspn(newValue, "\n")] = '\0'; // 개행 문자 제거
        json_object_set_string(obj, "Title", newValue);
        break;
    case 2:
        printf("새로운 작가명을 입력하세요: ");
        fgets(newValue, sizeof(newValue), stdin);
        newValue[strcspn(newValue, "\n")] = '\0'; // 개행 문자 제거
        json_object_set_string(obj, "Author", newValue);
        break;
    case 3:
        printf("수정이 취소되었습니다.\n");
        return;
    default:
        printf("잘못된 선택입니다.\n");
        return;
    }

    // 수정된 JSON을 파일에 저장
    json_serialize_to_file_pretty(rootValue, "Book.json");
    printf("도서 정보가 수정되었습니다!\n");
}

void RecommendBook(JSON_Array* array, JSON_Value* rootValue) {
    size_t array_size = json_array_get_count(array);
    if (array_size == 0) {
        printf("도서 목록이 비어있습니다.\n");
        return 1;
    }
    printf("==================== 추천 도서 목록====================\n");
    for (size_t i = 0; i < array_size; i++) {
        JSON_Object* obj = json_array_get_object(array, i);
        if (obj != NULL) {
            const char* title = json_object_get_string(obj, "Title");
            const char* author = json_object_get_string(obj, "Author");
            double cnt = json_object_get_number(obj, "Count");
            if (cnt > 5) {

                printf("    Title: %s, Author: %s\n", title, author);
                printf("-------------------------------------------------------\n");
            }

        }
    }
}

void Rent(JSON_Array* array, JSON_Value* rootValue) {
    size_t array_size = json_array_get_count(array);
    if (array_size == 0) {
        printf("대출/반납 가능한 도서가 없습니다.\n");
        return 1;
    }
    int getC = 1, mY = 2;
    int want = 0;
    int size = 0;
    printf("도서 대출을 원하시면 1, 반납을 원하시면 2를 입력해주세요 :");
    scanf("%d", &want);
    getchar();
    if (want == 2) {
        int able = 0;
        printf("=======================도서 목록============================\n");
        for (size_t i = 0; i < array_size; i++) {
            JSON_Object* obj = json_array_get_object(array, i);
            if (obj != NULL) {
                const char* title = json_object_get_string(obj, "Title");
                const char* author = json_object_get_string(obj, "Author");
                double rent = json_object_get_number(obj, "Rent");
                if (rent == 0.0f) {
                    printf("    Title: %s, Author: %s, 대출 가능\n", title, author);
                    printf("-------------------------------------------------------\n");
                }
                else if (rent == 1.0f) {
                    printf("    Title: %s, Author: %s, 대출 불가능\n", title, author);
                    printf("-------------------------------------------------------\n");
                }

            }
        }
        GotoXY(1, mY);
        printf("▶");
        while (getC != ENTER) {
            if (_kbhit()) {
                getC = _getch();
                switch (getC) {
                case UP:
                    if (size > 0) {
                        GotoXY(1, mY);
                        printf("  ");
                        mY -= 2;
                        GotoXY(1, mY);
                        printf("▶");
                        size -= 2;
                    }
                    break;
                case DOWN:
                    if (size < array_size) {
                        GotoXY(1, mY);
                        printf("  ");
                        mY += 2;
                        GotoXY(1, mY);
                        printf("▶");
                        size += 2;
                    }
                    break;
                case ENTER: //반납
                    system("cls");
                    JSON_Object* obj = json_array_get_object(array, size / 2);
                    double rent = json_object_get_number(obj, "Rent");
                    if (rent == 1.0f) {
                        rent = 0.0f;
                        json_object_set_number(obj, "Rent", rent);
                        printf("반납되었습니다! 감사합니다.\n");
                        array = json_value_get_array(rootValue);
                        json_serialize_to_file_pretty(rootValue, "Book.json");
                        break;
                    }
                    else {
                        printf("대출중인 도서가 아닙니다.\n");
                        break;
                    }

                }
            }
        }
    }
    else if (want == 1 || want == 2) {
        size_t array_size = json_array_get_count(array);
        if (array_size == 0) {
            printf("대출/반납 가능한 도서가 없습니다.\n");
            return 1;
        }

        printf("=======================도서 목록============================\n");
        for (size_t i = 0; i < array_size; i++) {
            JSON_Object* obj = json_array_get_object(array, i);
            if (obj != NULL) {
                const char* title = json_object_get_string(obj, "Title");
                const char* author = json_object_get_string(obj, "Author");
                double rent = json_object_get_number(obj, "Rent");
                if (rent == 0.0f) {
                    printf("    Title: %s, Author: %s, 대출 가능\n", title, author);
                    printf("-------------------------------------------------------\n");
                }
                else if (rent == 1.0f) {
                    printf("    Title: %s, Author: %s, 대출 불가능\n", title, author);
                    printf("-------------------------------------------------------\n");
                }

            }
        }

        GotoXY(1, mY);
        printf("▶");
        GotoXY(1, mY);
        printf("▶");
        while (getC != ENTER) {
            if (_kbhit()) {
                getC = _getch();
                switch (getC) {
                case UP:
                    if (size > 0) {
                        GotoXY(1, mY);
                        printf("  ");
                        mY -= 2;
                        GotoXY(1, mY);
                        printf("▶");
                        size -= 2;
                    }
                    break;
                case DOWN:
                    if (size < array_size) {
                        GotoXY(1, mY);
                        printf("  ");
                        mY += 2;
                        GotoXY(1, mY);
                        printf("▶");
                        size += 2;
                    }
                    break;
                case ENTER: //대출
                    system("cls");
                    JSON_Object* obj = json_array_get_object(array, size / 2);
                    double rent = json_object_get_number(obj, "Rent");
                    if (rent == 1.0f) {
                        printf("이미 대출중인 도서입니다.\n");
                        break;
                    }
                    else {
                        rent = 1.0f;
                        json_object_set_number(obj, "Rent", rent);
                        printf("대출되었습니다!\n");
                        array = json_value_get_array(rootValue);
                        json_serialize_to_file_pretty(rootValue, "Book.json");
                        break;
                    }

                }
            }
        }
    }
    else {
        printf("유효하지 않은 문자입니다.\n");
    }

}

void SearchBooks(JSON_Array* array, JSON_Value* rootValue) {
    printf("    1. 작가명으로 찾기 \n");
    printf("    2. 도서명으로 찾기 \n");
    printf("    3. 메뉴로 나가기 \n");

    int getC = 1, mY = 0;
    int select = 1;
    GotoXY(1, mY);
    printf("▶");
    while (getC != ENTER) {
        if (_kbhit()) {
            getC = _getch();
            switch (getC) {
            case UP:
                if (select > 1) {
                    GotoXY(1, mY);
                    printf("  ");
                    mY -= 1;
                    GotoXY(1, mY);
                    printf("▶");
                    select -= 1;
                }
                break;
            case DOWN:
                if (select < 3) {
                    GotoXY(1, mY);
                    printf("  ");
                    mY += 1;
                    GotoXY(1, mY);
                    printf("▶");
                    select += 1;
                }
                break;
            case ENTER:
                switch (select) {
                case 1:
                    SearchAuthor(array, rootValue);
                    break;
                case 2:
                    SearchBookname(array, rootValue);
                    break;
                }
            }
        }
    }
}

void SearchAuthor(JSON_Array* array, JSON_Value* rootValue) {
    system("cls");
    size_t array_size = json_array_get_count(array);
    if (array_size == 0) {
        printf("Failed to get JSON array.\n");
        return 1;
    }
    // 작가명으로 검색할 수 있는 기능 추가
    char bookAuthor[100];
    int found = 0;
    printf("작가명을 입력해주세요 : ");
    fgets(bookAuthor, sizeof(bookAuthor), stdin);
    bookAuthor[strcspn(bookAuthor, "\n")] = '\0';

    for (size_t i = 0; i < array_size; i++) {
        JSON_Object* obj = json_array_get_object(array, i);
        if (obj != NULL) {
            const char* title = json_object_get_string(obj, "Title");
            const char* author = json_object_get_string(obj, "Author");
            double cnt = json_object_get_number(obj, "Count");
            if (strcmp(author, bookAuthor) == 0) {
                printf("----------------------------------------------------------\n");
                printf("    Title: %s, Author: %s\n", title, author);
                printf("----------------------------------------------------------\n");
                cnt = cnt + 1.0f;
                json_object_set_number(obj, "Count", cnt);
                found = 1;
                break;
            }

        }
    }


    if (!found) {
        printf("찾으시는 작가님이 없습니다.\n");
    }
    json_serialize_to_file_pretty(rootValue, "Book.json");
}

void SearchBookname(JSON_Array* array, JSON_Value* rootValue) {
    system("cls");
    size_t array_size = json_array_get_count(array);
    if (array_size == 0) {
        printf("도서 목록이 비어있습니다.\n");
        return 1;
    }
    char bookName[100];
    int able = 0;
    printf("도서명을 입력해주세요 : ");
    fgets(bookName, sizeof(bookName), stdin);
    bookName[strcspn(bookName, "\n")] = '\0';

    for (size_t i = 0; i < array_size; i++) {
        JSON_Object* obj = json_array_get_object(array, i);
        if (obj != NULL) {
            const char* title = json_object_get_string(obj, "Title");
            const char* author = json_object_get_string(obj, "Author");
            double cnt = json_object_get_number(obj, "Count");
            if (strcmp(title, bookName) == 0) {
                printf("----------------------------------------------------------\n");
                printf("    Title: %s, Author: %s\n", title, author);
                printf("----------------------------------------------------------\n");
                cnt = cnt + 1.0f;
                printf("%lf", cnt);
                json_object_set_number(obj, "Count", cnt);
                able = 1;
            }

        }
    }
    if (!able) {
        printf("찾으시는 도서가 없습니다.\n");
    }
    json_serialize_to_file_pretty(rootValue, "Book.json");
}

void Menu() {
    printf("********************************************\n");
    printf("              도서 검색 프로그램           \n");
    printf("********************************************\n");
    printf("           1 : 전체 도서 목록              \n");
    printf("--------------------------------------------\n");
    printf("           2 : 추천 도서 목록              \n");
    printf("--------------------------------------------\n");
    printf("           3 : 도서 추가                   \n");
    printf("--------------------------------------------\n");
    printf("           4 : 도서 검색                   \n");
    printf("--------------------------------------------\n");
    printf("           5 : 도서 대출/반납              \n");
    printf("--------------------------------------------\n");
    printf("           6 : 초기화                   \n");
    printf("--------------------------------------------\n");
    printf("           7 : 종료 및 저장                   \n");
    printf("********************************************\n");
}