#include "LibraryItem.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>

/* TOOLS */

bool operator==(std::string& str1, const char* str2) {
	for (int i = 0; str2[i]; ++i)
		if (str1[i] != str2[i])
			return false;
	return true;
}

bool contain(std::string str1, std::string str2) {
	for (int i = 0; i < str1.length(); ++i)
		if (str1[i] >= 'A' && str1[i] <= 'Z')
			str1[i] += ' ';
	for (int i = 0; i < str2.length(); ++i)
		if (str2[i] >= 'A' && str2[i] <= 'Z')
			str2[i] += ' ';
	for (int i = 0; i < str1.length(); ++i) {
		for (int j = 0; j < str2.length(); ++j)
			if (str1[i + j] != str2[j])
				goto There;
		return true;
	There:;
	}
	return false;
}

/* ENUMS */

enum e_Type {
	e_Book, e_MusicCD
};

/* CLASSES */

class LibraryItemBase : public LibraryItem {
public:
	e_Type Type;
	bool Status = true;

	bool isOnLoan() {
		return Status == false;
	}

	bool borrow() {
		return Status == true;
	}

	bool takeBack() {
		return Status == false;
	}
};

class Book : public LibraryItemBase {
public:
	int Unique_id;
	std::string Title;
	std::string Author;

	Book(int Unique_id, std::string Title, std::string Author) {
		this->Type = e_Book;
		this->Unique_id = Unique_id;
		this->Title = Title;
		this->Author = Author;
	}
	
	bool search(std::string searchStr) {
		bool cond = false;
		cond += contain(((Book*)this)->Title, searchStr);
		cond += contain(((Book*)this)->Author, searchStr);
		return cond;
	}

	std::string toString() {
		return std::string("ID: " + std::to_string(Unique_id) + " TITLE: " + Title + " AUTHOR: " + Author + "\n");
	}

};

class MusicCD : public LibraryItemBase {
public:
	int Unique_id;
	std::string Title;
	std::string Artist;

	MusicCD(int Unique_id, std::string Title, std::string Author) {
		this->Type = e_MusicCD;
		this->Unique_id = Unique_id;
		this->Title = Title;
		this->Artist = Author;
	}
	
	bool search(std::string searchStr) {
		bool cond = false;
		cond += contain(((MusicCD*)this)->Title, searchStr);
		cond += contain(((MusicCD*)this)->Artist, searchStr);
		return cond;
	}

	std::string toString() {
		return std::string("ID: " + std::to_string(Unique_id) + " TITLE: " + Title + " ARTIST: " + Artist + "\n");
	}

};

class LibraryCatalog {
public:
	std::vector<LibraryItem*> Items;
	
	void Read_The_File(std::ifstream& File) {

		e_Type Type;
		int Unique_id;
		std::string Title;
		std::string Artist_Auther;

		std::string str;

		while (std::getline(File, str)) {
			std::istringstream Line(str);
			if (!(Line >> str)) continue;
			if (str == "//") continue;

			Type = str == "BOOK" ? e_Book : e_MusicCD;

			Unique_id = 0;
			Title.clear();
			Artist_Auther.clear();

			Line >> Unique_id;

			while (Line >> str) {
				if (str == "|") {
					break;
				}
				else {
					Title += str + ' ';
				}
			}
			while (Line >> str) {
				Artist_Auther += str + ' ';
			}
			Title.pop_back();
			Artist_Auther.pop_back();

			if (Type == e_Book) {
				Items.push_back(new Book(Unique_id, Title, Artist_Auther));
			}
			else {
				Items.push_back(new MusicCD(Unique_id, Title, Artist_Auther));
			}
		}
	}
	void Print_Item(LibraryItem* Item) {
		if (((LibraryItemBase*)Item)->Type == e_Book) {
			std::cout << ((Book*)Item)->toString();
		}
		else {
			std::cout << ((MusicCD*)Item)->toString();
		}
	}

	/// User Choice Functions
	void All_Items(){
		for (int i = 0; i < Items.size(); ++i)
				Print_Item(Items[i]);
	}
	void Available_Items() {
		for (int i = 0; i < Items.size(); ++i)
			if (Items[i]->borrow())
				Print_Item(Items[i]);
	}
	void Only_Books() {
		for (int i = 0; i < Items.size(); ++i)
			if (((LibraryItemBase*)Items[i])->Type == e_Book)
				Print_Item(Items[i]);
	}
	void Only_MusicCD() {
		for (int i = 0; i < Items.size(); ++i)
			if (((LibraryItemBase*)Items[i])->Type == e_MusicCD)
				Print_Item(Items[i]);
	}
	void Search_By_Key_Word() {
		std::string search_key;
		std::cout << "Please enter your search: ";
		std::cin >> search_key;
		while (getchar() != '\n');

		for (int i = 0; i < Items.size(); ++i)
			if (((LibraryItemBase*)Items[i])->Type == e_Book && ((Book*)Items[i])->search(search_key)
				|| ((LibraryItemBase*)Items[i])->Type == e_MusicCD && ((MusicCD*)Items[i])->search(search_key))
				Print_Item(Items[i]);
	}
	void Check_Out() {
		int search_id;
		bool cond = true;

		std::cout << "Please enter the item's id: ";
		std::cin >> search_id;
		while (getchar() != '\n');

		for (int i = 0; i < Items.size(); ++i)
			if (((LibraryItemBase*)Items[i])->Type == e_Book && ((Book*)Items[i])->Unique_id == search_id
				|| ((LibraryItemBase*)Items[i])->Type == e_MusicCD && ((MusicCD*)Items[i])->Unique_id == search_id) {
				if (((LibraryItemBase*)Items[i])->borrow()) {
					((LibraryItemBase*)Items[i])->Status = false;
					std::cout << "Item is successfully checked out\n";
				}
				else
					std::cout << "Item is not available to check out\n";
				cond = false;
				break;
			}
		if (cond)
			std::cout << "Item with ID: " << search_id << " is not found.\n";
	}
	void Return() {
		int search_id;
		bool cond = true;

		std::cout << "Please enter the item's id: ";
		std::cin >> search_id;
		while (getchar() != '\n');
		
		for (int i = 0; i < Items.size(); ++i)
			if (((LibraryItemBase*)Items[i])->Type == e_Book && ((Book*)Items[i])->Unique_id == search_id
				|| ((LibraryItemBase*)Items[i])->Type == e_MusicCD && ((MusicCD*)Items[i])->Unique_id == search_id) {
				if (((LibraryItemBase*)Items[i])->takeBack()) {
					((LibraryItemBase*)Items[i])->Status = true;
					std::cout << "Item is successfully returned\n";
				}
				else
					std::cout << "Item is not available to return\n";
				cond = false;
				break;
			}
		if (cond)
			std::cout << "Item with ID: " << search_id << " is not found.\n";
	}
};

/* MAIN */

int main(int argc, char* argv[]) {

	/// Declare The Variables
	std::ifstream File;
	LibraryCatalog Items;
	int Choice;

	/// Check if The User Entered 2 Arguments
	if (argc < 2) {
		std::cout << "Error: messing the file name\n";
		return 0;
	}

	/// Open The File
	File.open(argv[1]);

	/// Check if The File Opened S
	if (!File.is_open()) {
		std::cout << "Error: there is no file called " << argv[1] << " in this directory" << std::endl;
		return 0;
	}

	/// Read The File
	Items.Read_The_File(File);

	/// Start The Program
	do {
		std::cout << "\nLibrary:\n"
					 "  1. list all items\n"
					 "  2. list available items\n"
					 "  3. list only books\n"
					 "  4. list only music CDs\n"
					 "  5. search\n"
					 "  6. check out\n"
					 "  7. return\n"
					 "  8. Exit the program\n\n";

		Choice = 0;
		do {
			/// This Loop Will Keep Run Until The User Enter A Valid Number 1-8
			std::cout << "Enter your selection : ";
			std::cin >> Choice;
			while (getchar() != '\n');

			if (Choice < 1 || Choice > 8)
				std::cout << "Error: Please Enter a Number From The List 1-8";

		} while (Choice < 1 || Choice > 8);

		/// Choose The Right Operation Based On The User Choice
		switch (Choice) {
		case 1: // Print All The Items
			Items.All_Items();
			break;
		case 2: // Print All The Available Items
			Items.Available_Items();
			break;
		case 3: // Print All The Items That Are Books
			Items.Only_Books();
			break;
		case 4: // Print All The Items That Are MusicCD
			Items.Only_MusicCD();
			break;
		case 5: // Print All The Items that Have The Key Word in Ether The Title or The Auther_Artist
			Items.Search_By_Key_Word();
			break;
		case 6: // Check Out an Item if it Does Exist
			Items.Check_Out();
			break;
		case 7:  // Return an Item if it Does Exist
			Items.Return();
			break;
		case 8: // Print The Exit Message
			std::cout << "\n\nGood Bye\n\n";
			break;
		}
	} while (Choice != 8);

	/// Close The File
	File.close();

	/// End The Program
	return 1;
}