#include "Folder.h"
#include "SplitStr.h"

#include <algorithm>
#include <fstream>
#include <iostream>
#include <stack>
#include <string>
#include <vector>

void DebugOutput(File *file)
{
	static int indent = -1;
	++indent;
	for(auto i = 0; i < indent; ++i)
		std::cout << " ";

	Folder *folder = dynamic_cast<Folder*>(file);
	if(folder)
	{
		std::cout << "Contents of folder: " << folder->Filename()
		          << ", size: " << folder->Size()
		          << std::endl;
		for(File *file : folder->Files())
			DebugOutput(file);
	}
	else
		std::cout << "- " << file->Filename()
		          << ", size: " << file->Size()
		          << std::endl;
	--indent;
}

unsigned long PartOne(File *file, unsigned long &grand_total)
{
	unsigned long rtn = 0;
	Folder *folder = dynamic_cast<Folder*>(file);
	if(folder)
	{
		for(File *file : folder->Files())
			rtn += PartOne(file, grand_total);
		if(rtn <= 100000)
		{
			grand_total += rtn;
			std::cout << "Folder \"" << folder->Filename() << "\" has a size of " << rtn
			          << std::endl;
		}
	}
	else
		rtn += file->Size();
	return rtn;
}

void PartTwo(File *file, unsigned long at_least, File *&smallest)
{
	Folder *folder = dynamic_cast<Folder*>(file);
	if(folder)
	{
		if(folder->Size() >= at_least && folder->Size() < smallest->Size())
		{
			std::cout << "New smallest size of " << folder->Size() << " by folder \""
			          << folder->Filename() << "\"." << std::endl;
			smallest = file;
		}

		for(File *file : folder->Files())
			PartTwo(file, at_least, smallest);
	}

	return;
}

void Parse(Folder &root)
{
	std::ifstream ifs("data.txt");
	if(!ifs.is_open())
	{
		std::cerr << "Missing data.txt." << std::endl;
		exit(-1);
	}

	std::stack<Folder*> dir_stack;
	dir_stack.push(&root);

	// Step 1: Parse:
	for(std::string line; std::getline(ifs, line); )
	{
		if(line == "")
			continue;

		if(line.starts_with("$ ls"))
			continue;
		else if(line.starts_with("$ cd"))
		{
			auto to = line.substr(5);
			if(to == "/")
			{
				while(dir_stack.size() > 1)
					dir_stack.pop();
			}
			else if(to == "..")
			{
				if(dir_stack.size() > 1)
					dir_stack.pop();
			}
			else
			{
				auto existing = std::find_if(dir_stack.top()->Files().begin(),
			                     dir_stack.top()->Files().end(),
			                     [&to](const File *f){
				                     return f->Filename() == to;
			                     });
				if(existing != dir_stack.top()->Files().end()) // found!
				{
					// Check to see if it's a folder, first:
					Folder *folder = dynamic_cast<Folder*>(*existing);
					if(folder)
						dir_stack.push(folder);
					else
					{
						std::cerr << (*existing)->Filename() << " is a file, not a folder."
						          << std::endl;
						exit(-3);
					}
				}
				else
					std::cerr << "could not navigate to \"" << to << "\" from \""
					          << dir_stack.top()->Filename() << "\"." << std::endl;
			}
		}
		else if(line.starts_with("dir "))
		{
			dir_stack.top()->AddFolder(line.substr(4));
		}
		else
		{
			auto values = SplitStr(line, " ");
			dir_stack.top()->AddFile(values[1], std::atol(values[0].c_str()));
		}
	}
}

int main()
{
	Folder root("/");
	Parse(root);

	// Step 1.5: Debug output:
	DebugOutput(&root);

	unsigned long grand_total = 0;
	PartOne(&root, grand_total);

	std::cout << "Grand Total (part 1): " << grand_total << std::endl;

	const unsigned long max_space = 70000000;
	const unsigned long space_needed = 30000000;
	const unsigned long currently_used = root.Size();
	const unsigned long currently_unused = max_space - currently_used;
	const unsigned long amt_to_remove = space_needed - currently_unused;

	File *file_to_remove = &root;
	PartTwo(&root, amt_to_remove, file_to_remove);

	std::cout << "Remove \"" << file_to_remove->Filename() << "\" to free up "
	          << file_to_remove->Size() << " which is the smallest amount >= "
	          << amt_to_remove << " (part 2)." << std::endl;

	return 0;
}