#include <string>
#include <iostream>
#include <sstream>
#include <fstream>

#include "arg_func.h"
#include "func.h"
#include "win.h"
#include "func.h"
#include "buffer.h"
#include "cryptography.h"

void print_args()
{
	printf_s(" Usage:\n\n");
	printf_s(" password_manager.exe [flags]\n\n");
	printf_s(" Flags:\n\n");
	printf_s(" -h                   print this message\n");
	printf_s(" <label>              get password of this label can use GLOB\n");
	printf_s(" -g <label>           generate password of this label (or update if exists)\n");
	printf_s(" -i <label>           input password for this label (or update if exists)\n");
	printf_s(" -d <label>           delete password of this label\n");
	printf_s(" -s <label>           show password for this label\n");
	printf_s(" -u <label>           update username for this label\n");
	printf_s(" -n <label>           update label name\n");
	printf_s(" -l                   list all labels\n");
	printf_s(" -p                   print all passwords\n");
	printf_s(" -c                   change master password\n");
	printf_s(" -f <folder path>     select save folder\n");
}

Arg get_args(int argc, char** argv, char** label) {
	if (argc < 2)
	{
		print_args();
		return Arg::Error;
	}

	if (!strcmp("-h", argv[1]))
	{
		print_args();
		return Arg::Error;
	}

	if (!strcmp("-l", argv[1])) return Arg::List;
	if (!strcmp("-p", argv[1])) return Arg::Print_all_p;
	if (!strcmp("-c", argv[1])) return Arg::Change;

	if (argc < 3)
	{
		*label = argv[1];
		return Arg::Get;
	}

	*label = argv[2];

	if (!strcmp("-g", argv[1])) return Arg::Generate;
	if (!strcmp("-d", argv[1])) return Arg::Delete;
	if (!strcmp("-i", argv[1]))	return Arg::Input;
	if (!strcmp("-s", argv[1])) return Arg::Show;
	if (!strcmp("-u", argv[1])) return Arg::Username;
	if (!strcmp("-n", argv[1])) return Arg::Name;
	if (!strcmp("-f", argv[1])) return Arg::File;

	return Arg::Error;
}

std::optional<LoginInfoPointer> arg_get(Buffer& decrypted_buffer, const char* label)
{
	int pass = find_logininfo_in_buffer(decrypted_buffer, label);
	if (pass < 0) {
		printf_s("Password not found\n");
		return {};
	}
	return get_logininfo_pointer_from_buffer(decrypted_buffer, pass);
}

std::optional<LoginInfoPointer> arg_new_password(Buffer& decrypted_buffer, Buffer& encrypted_buffer, const char* label, Cryptography& crypto, bool generate)
{
	int pass = find_logininfo_in_buffer(decrypted_buffer, label);
	std::string username = "";
	std::string name = label;
	std::string password = "";

	if (pass >= 0) {
		LoginInfoPointer lip = get_logininfo_pointer_from_buffer(decrypted_buffer, pass);
		username = lip.username;
		name = lip.label;
		delete_logininfo_from_buffer(decrypted_buffer, pass);
	}

	printf_s("New password for %s: \n", name.c_str());

	if (pass < 0)
	{
		printf_s("Input username: ");
		std::getline(std::cin, username);
	}

	if (generate)
	{
		int default_length = 15;
		printf_s("Input password length [%d]: ", default_length);
		std::string input;
		int length = default_length;

		std::getline(std::cin, input);
		if (!input.empty())
		{
			std::istringstream iss(input);
			if (!(iss >> length)) length = default_length;
		}

		generate_password(password, length);
	}
	else
	{
		printf_s("Input new password: \n");
		password = get_user_password();
		if (password.empty())
		{
			printf_s("error getting password\n");
			return {};
		}
	}

	add_logininfo_to_buffer(decrypted_buffer, name.c_str(), username.c_str(), password.c_str());
	crypto.encrypt(&decrypted_buffer, &encrypted_buffer);
	encrypted_buffer.save_to_file();
	Index* index = (Index*)decrypted_buffer.buffer;
	return get_logininfo_pointer_from_buffer(decrypted_buffer, index->count - 1);
}

void arg_username(Buffer& decrypted_buffer, Buffer& encrypted_buffer, const char* label, Cryptography& crypto)
{
	int pass = find_logininfo_in_buffer(decrypted_buffer, label);
	if (pass < 0)
	{
		printf_s("LoginInfo not found\n");
		return;
	}

	LoginInfoPointer lip = get_logininfo_pointer_from_buffer(decrypted_buffer, pass);
	std::string password = lip.password;
	std::string name = lip.label;

	printf_s("Input username for %s: ", name.c_str());
	std::string username;
	std::getline(std::cin, username);

	delete_logininfo_from_buffer(decrypted_buffer, pass);
	add_logininfo_to_buffer(decrypted_buffer, name.c_str(), username.c_str(), password.c_str());
	crypto.encrypt(&decrypted_buffer, &encrypted_buffer);
	encrypted_buffer.save_to_file();
}

void arg_label_name(Buffer& decrypted_buffer, Buffer& encrypted_buffer, const char* label, Cryptography& crypto)
{
	int pass = find_logininfo_in_buffer(decrypted_buffer, label);

	if (pass < 0)
	{
		printf_s("LoginInfo not found\n");
		return;
	}

	LoginInfoPointer lip = get_logininfo_pointer_from_buffer(decrypted_buffer, pass);
	std::string password = lip.password;
	std::string username = lip.username;

	printf_s("Input new label name for %s:", lip.label);
	std::string name;
	std::getline(std::cin, name);

	delete_logininfo_from_buffer(decrypted_buffer, pass);
	add_logininfo_to_buffer(decrypted_buffer, name.c_str(), username.c_str(), password.c_str());
	crypto.encrypt(&decrypted_buffer, &encrypted_buffer);
	encrypted_buffer.save_to_file();
}

void arg_list(Buffer& decrypted_buffer)
{
	Index* index = (Index*)decrypted_buffer.buffer;
	LoginInfo* pass = (LoginInfo*)(decrypted_buffer.buffer + sizeof(Index));

	for (size_t i = 0; i < index->count; i++)
	{
		printf_s("label: %s\n", decrypted_buffer.buffer + pass[i].label + index->offset);
	}
}

void arg_delete(Buffer& decrypted_buffer, Buffer& encrypted_buffer, const char* label_to_del, Cryptography& crypto)
{
	printf_s("Deleting password for %s\n", label_to_del);
	int pass = find_logininfo_in_buffer(decrypted_buffer, label_to_del);
	if (pass < 0)
	{
		printf_s("Password not found\n");
		return;
	}

	delete_logininfo_from_buffer(decrypted_buffer, pass);

	crypto.encrypt(&decrypted_buffer, &encrypted_buffer);
	encrypted_buffer.save_to_file();
	printf_s("Password deleted\n");
}

void arg_print_all_p(Buffer& decrypted_buffer, std::string& user_pass)
{
	printf_s("Input main password for confirmation:");
	std::string new_string = get_user_password();
	if (new_string.empty())
	{
		printf_s("Error getting password\n");
		return;
	}
	if (new_string != user_pass)
	{
		printf_s("Wrong password\n");
		return;
	}

	Index* index = (Index*)decrypted_buffer.buffer;
	LoginInfo* pass = (LoginInfo*)(decrypted_buffer.buffer + sizeof(Index));

	printf_s("\n");
	for (size_t i = 0; i < index->count; i++)
	{
		printf_s("label: %-10s\t\t", decrypted_buffer.buffer + pass[i].label + index->offset);
		printf_s("username: %-10s\t\t", decrypted_buffer.buffer + pass[i].username + index->offset);
		printf_s("password: %s\n", decrypted_buffer.buffer + pass[i].password + index->offset);
	}
}

void arg_change(Buffer& decrypted_buffer, Buffer& encrypted_buffer, std::string& user_pass, Cryptography& crypto)
{
	printf_s("Input main password for confirmation:");

	std::string new_string = get_user_password();
	if (new_string.empty())
	{
		printf_s("Error getting password\n");
		return;
	}

	if (new_string != user_pass)
	{
		printf_s("Passwords don't match\n");
		return;
	}

	printf_s("Input new password:");
	new_string = get_user_password();
	if (new_string.empty())
	{
		printf_s("Error getting password\n");
		return;
	}

	printf_s("Input new password again:");
	user_pass = get_user_password();
	if (user_pass.empty())
	{
		printf_s("Error getting password\n");
		return;
	}

	if (new_string != user_pass)
	{
		printf_s("Passwords don't match\n");
		return;
	}

	crypto.generate_key_and_iv_from_password(new_string.c_str(), new_string.size());
	crypto.encrypt(&decrypted_buffer, &encrypted_buffer);
	encrypted_buffer.save_to_file();
	printf_s("Password changed\n");
}

void arg_show(Buffer& decrypted_buffer, const char* label) {

	int pass = find_logininfo_in_buffer(decrypted_buffer, label);

	if (pass < 0)
		printf_s("Password not found\n");
	else
	{
		LoginInfoPointer lip = get_logininfo_pointer_from_buffer(decrypted_buffer, pass);
		printf_s("Username: %s\n", lip.username);
		printf_s("Password: %s\n", lip.password);
	}
}

void arg_file(Buffer& decrypted_buffer, Buffer& encrypted_buffer, const char* label, Cryptography& crypto, std::string& save_location_path) 
{
	std::string save(label);
	save.append("\\passwords.bin");

	std::ofstream file(save_location_path, std::ios::binary);
	file << save << std::endl;
	file.close();

	if (decrypted_buffer.taken <= sizeof(Index)) return;

	crypto.encrypt(&decrypted_buffer, &encrypted_buffer);
	encrypted_buffer.save_to_file(save);
}