// json.c
// Copyright : 2023-02-06 Yutaka Sawada
// License : GPL

#ifndef _UNICODE
#define _UNICODE
#endif
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600	// Windows Vista or later
#endif

#include <stdio.h>

#include <windows.h>

#include "common2.h"

/*
{
"SelectedFile":"Full path of selected recovery file",
"BaseDirectory":"Full path of base directory of source files",
"RecoveryFile":[
"Name of recovery file1",
"Name of recovery file2",
"Name of recovery file3"
],
"SourceFile":[
"Name of source file1",
"Name of source file2",
"Name of source file3"
],
"FoundFile":[
"Name of found file1",
"Name of found file2"
],
"ExternalFile":[
"Name of external file1",
"Name of external file2"
],
"DamagedFile":[
"Name of damaged file1",
"Name of damaged file2"
],
"AppendedFile":[
"Name of appended file1",
"Name of appended file2"
],
"MissingFile":[
"Name of missing file1",
"Name of missing file2"
],
"MisnamedFile":{
"Correct name of misnamed file1":"Wrong name",
"Correct name of misnamed file2":"Wrong name"
}
}
*/
static FILE *fp_json = NULL;	// JSONファイル用

static wchar_t *list3_buf = NULL;	// 検出ファイルの名前リスト
static int list3_len, list3_max;

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

// JSONファイルを開く
void json_open(void)
{
	wchar_t path[MAX_LEN];
	int len;

	if (fp_json != NULL)
		return;	// 既に開いてる場合はそのまま

	wcscpy(path, ini_path);
	wcscat(path, offset_file_name(recovery_file));
	wcscat(path, L".json");
	//printf("json_path = %ls\n", path);

	// 既に存在する場合は上書きして作り直す
	fp_json = _wfopen(path, L"wt, ccs=UTF-8");
	if (fp_json == NULL)
		return;

	// 基礎情報を書き込む
	rewind(fp_json);	// Remove BOM of UTF-8
	copy_without_prefix(path, recovery_file);
	unix_directory(path);
	if (fwprintf(fp_json, L"{\n\"SelectedFile\":\"%s\"", path) < 0){
		fclose(fp_json);
		fp_json = NULL;
		return;
	}
	len = copy_without_prefix(path, base_dir);
	path[len - 1] = 0;	// 末尾の「\」を取り除く
	unix_directory(path);
	if (fwprintf(fp_json, L",\n\"BaseDirectory\":\"%s\"", path) < 0){
		fclose(fp_json);
		fp_json = NULL;
		return;
	}
}

// JSONファイルを閉じる
void json_close(void)
{
	if (fp_json == NULL)
		return;	// 既に閉じてる場合はそのまま

	fputws(L"\n}\n", fp_json);
	fclose(fp_json);
	fp_json = NULL;
}

// ファイル一覧
void json_file_list(file_ctx_r *files)
{
	wchar_t path[MAX_LEN];
	int rv, i;

	if (fp_json == NULL)
		return;	// 開いてない場合は何もしない

	// パケットを含む (Good or Damaged) リカバリ・ファイルのリスト
	// 全くパケットを含まない (Useless) ファイルは除く
	if (fputws(L",\n\"RecoveryFile\":[", fp_json) < 0){
		fclose(fp_json);
		fp_json = NULL;
		return;
	}
	i = 0;
	while (i < recv2_len){
		if (compare_directory(recovery_file, recv2_buf + i) == 0){
			// 指定されたリカバリ・ファイルと同じ場所ならファイル名のみ
			wcscpy(path, offset_file_name(recv2_buf + i));
		} else {
			// 異なる場所（External File）ならフルパス
			copy_without_prefix(path, recv2_buf + i);
			unix_directory(path);
		}
		if (i == 0){
			rv = fwprintf(fp_json, L"\n\"%s\"", path);
		} else {
			rv = fwprintf(fp_json, L",\n\"%s\"", path);
		}
		if (rv < 0){
			fclose(fp_json);
			fp_json = NULL;
			return;
		}

		while (recv2_buf[i] != 0)
			i++;
		i++;
	}
	if (fputws(L"\n]", fp_json) < 0){
		fclose(fp_json);
		fp_json = NULL;
		return;
	}

	// ソース・ファイルのリスト
	if (fputws(L",\n\"SourceFile\":[", fp_json) < 0){
		fclose(fp_json);
		fp_json = NULL;
		return;
	}
	for (i = 0; i < file_num; i++){
		wcscpy(path, list_buf + files[i].name);
		unix_directory(path);
		if (i == 0){
			rv = fwprintf(fp_json, L"\n\"%s\"", path);
		} else {
			rv = fwprintf(fp_json, L",\n\"%s\"", path);
		}
		if (rv < 0){
			fclose(fp_json);
			fp_json = NULL;
			return;
		}
	}
	if (fputws(L"\n]", fp_json) < 0){
		fclose(fp_json);
		fp_json = NULL;
		return;
	}
}

// ファイルの状態
void json_file_state(file_ctx_r *files)
{
	wchar_t path[MAX_LEN], path2[MAX_LEN];
	int rv, i, flag_item;

	if (fp_json == NULL)
		return;	// 開いてない場合は何もしない

	// 破損ファイル
	flag_item = 0;
	for (i = 0; i < file_num; i++){
		if (files[i].state & 2){
			wcscpy(path, list_buf + files[i].name);
			unix_directory(path);
			if (flag_item == 0){
				flag_item = 1;
				if (fputws(L",\n\"DamagedFile\":[", fp_json) < 0){
					fclose(fp_json);
					fp_json = NULL;
					return;
				}
				rv = fwprintf(fp_json, L"\n\"%s\"", path);
			} else {
				rv = fwprintf(fp_json, L",\n\"%s\"", path);
			}
			if (rv < 0){
				fclose(fp_json);
				fp_json = NULL;
				return;
			}
		}
	}
	if (flag_item != 0){
		if (fputws(L"\n]", fp_json) < 0){
			fclose(fp_json);
			fp_json = NULL;
			return;
		}
	}

	// 追加されたファイル
	flag_item = 0;
	for (i = 0; i < file_num; i++){
		if (files[i].state & 16){
			wcscpy(path, list_buf + files[i].name);
			unix_directory(path);
			if (flag_item == 0){
				flag_item = 1;
				if (fputws(L",\n\"AppendedFile\":[", fp_json) < 0){
					fclose(fp_json);
					fp_json = NULL;
					return;
				}
				rv = fwprintf(fp_json, L"\n\"%s\"", path);
			} else {
				rv = fwprintf(fp_json, L",\n\"%s\"", path);
			}
			if (rv < 0){
				fclose(fp_json);
				fp_json = NULL;
				return;
			}
		}
	}
	if (flag_item != 0){
		if (fputws(L"\n]", fp_json) < 0){
			fclose(fp_json);
			fp_json = NULL;
			return;
		}
	}

	// 消失ファイル
	flag_item = 0;
	for (i = 0; i < file_num; i++){
		if (files[i].state & 1){
			wcscpy(path, list_buf + files[i].name);
			unix_directory(path);
			if (flag_item == 0){
				flag_item = 1;
				if (fputws(L",\n\"MissingFile\":[", fp_json) < 0){
					fclose(fp_json);
					fp_json = NULL;
					return;
				}
				rv = fwprintf(fp_json, L"\n\"%s\"", path);
			} else {
				rv = fwprintf(fp_json, L",\n\"%s\"", path);
			}
			if (rv < 0){
				fclose(fp_json);
				fp_json = NULL;
				return;
			}
		}
	}
	if (flag_item != 0){
		if (fputws(L"\n]", fp_json) < 0){
			fclose(fp_json);
			fp_json = NULL;
			return;
		}
	}

	// 別名ファイルは、"本来の名前":"間違った名前" というセットにする
	flag_item = 0;
	for (i = 0; i < file_num; i++){
		if (files[i].state & 32){
			// ソースファイルの本来の名前
			wcscpy(path, list_buf + files[i].name);
			unix_directory(path);
			// 別名
			wcscpy(path2, list_buf + files[i].name2);
			unix_directory(path2);
			if (flag_item == 0){
				flag_item = 1;
				if (fputws(L",\n\"MisnamedFile\":{", fp_json) < 0){
					fclose(fp_json);
					fp_json = NULL;
					return;
				}
				rv = fwprintf(fp_json, L"\n\"%s\":\"%s\"", path, path2);
			} else {
				rv = fwprintf(fp_json, L",\n\"%s\":\"%s\"", path, path2);
			}
			if (rv < 0){
				fclose(fp_json);
				fp_json = NULL;
				return;
			}
		}
	}
	if (flag_item != 0){
		if (fputws(L"\n}", fp_json) < 0){
			fclose(fp_json);
			fp_json = NULL;
			return;
		}
	}
}

// 検出されたファイル名を保持する
void json_add_found(wchar_t *filename, int flag_external)
{
	wchar_t *tmp_p;
	int len;

	if (fp_json == NULL)
		return;	// 開いてない場合は何もしない

	len = (int)wcslen(filename) + 1;

	if (list3_buf == NULL){
		list3_max = ALLOC_LEN;
		list3_len = 0;
		list3_buf = malloc(list3_max * 2);
		if (list3_buf == NULL){
			fclose(fp_json);
			fp_json = NULL;
			return;
		}
	} else if (list3_len + len >= list3_max){	// 領域が足りなくなるなら拡張する
		list3_max += ALLOC_LEN;
		tmp_p = (wchar_t *)realloc(list3_buf, list3_max * 2);
		if (tmp_p == NULL){
			free(list3_buf);
			list3_buf = NULL;
			fclose(fp_json);
			fp_json = NULL;
			return;
		} else {
			list3_buf = tmp_p;
		}
	}

	if (flag_external != 0){
		list3_buf[list3_len] = 'e';
	} else {
		list3_buf[list3_len] = 'f';
	}
	wcscpy(list3_buf + list3_len + 1, filename);
	list3_len += len + 1;
}

// 検出されたファイル名を書き込む
void json_save_found(void)
{
	wchar_t path[MAX_LEN];
	int rv, i, flag_item;

	if (fp_json == NULL)
		return;	// 開いてない場合は何もしない
	if (list3_buf == NULL)
		return;	// 検出しなかった場合は何もしない

/*{
FILE *fp;
fp = fopen("list3.txt", "wb");
fwrite(list3_buf, 2, list3_len, fp);
fclose(fp);
}*/

	// ブロックを検出したファイル
	flag_item = 0;
	i = 0;
	while (i < list3_len){
		if (list3_buf[i] == 'f'){
			wcscpy(path, list3_buf + i + 1);
			unix_directory(path);
			if (flag_item == 0){
				flag_item = 1;
				if (fputws(L",\n\"FoundFile\":[", fp_json) < 0){
					free(list3_buf);
					list3_buf = NULL;
					fclose(fp_json);
					fp_json = NULL;
					return;
				}
				rv = fwprintf(fp_json, L"\n\"%s\"", path);
			} else {
				rv = fwprintf(fp_json, L",\n\"%s\"", path);
			}
			if (rv < 0){
				free(list3_buf);
				list3_buf = NULL;
				fclose(fp_json);
				fp_json = NULL;
				return;
			}
		}

		while (list3_buf[i] != 0)
			i++;
		i++;
	}
	if (flag_item != 0){
		if (fputws(L"\n]", fp_json) < 0){
			free(list3_buf);
			list3_buf = NULL;
			fclose(fp_json);
			fp_json = NULL;
			return;
		}
	}

	// ブロックを検出した外部ファイル
	flag_item = 0;
	i = 0;
	while (i < list3_len){
		if (list3_buf[i] == 'e'){
			copy_without_prefix(path, list3_buf + i + 1);
			unix_directory(path);
			if (flag_item == 0){
				flag_item = 1;
				if (fputws(L",\n\"ExternalFile\":[", fp_json) < 0){
					free(list3_buf);
					list3_buf = NULL;
					fclose(fp_json);
					fp_json = NULL;
					return;
				}
				rv = fwprintf(fp_json, L"\n\"%s\"", path);
			} else {
				rv = fwprintf(fp_json, L",\n\"%s\"", path);
			}
			if (rv < 0){
				free(list3_buf);
				list3_buf = NULL;
				fclose(fp_json);
				fp_json = NULL;
				return;
			}
		}

		while (list3_buf[i] != 0)
			i++;
		i++;
	}
	if (flag_item != 0){
		if (fputws(L"\n]", fp_json) < 0){
			free(list3_buf);
			list3_buf = NULL;
			fclose(fp_json);
			fp_json = NULL;
			return;
		}
	}

	// 最後にメモリーを開放する
	free(list3_buf);
	list3_buf = NULL;
}
