using namespace std;

class Info
{
	public:
		AnsiString getAttributes(unsigned long);
		static AnsiString PathBuilder(TTreeNode*);
		void addChilds(TTreeNode*, int);
		Info(TTreeView*, TImageList*, TProgressBar*);
		int Items(TTreeNode*);
		static AnsiString Shell(AnsiString, AnsiString);
		static bool Changeable(void*);
		static bool Info::Operation(UINT, AnsiString, AnsiString);
		static unsigned __int64* Info::GetHddSizes(string);
		void setimage(TMemo*);
	private:
		IconsStuff ico;
		TTreeView* view;
		TImageList* ImageList;
		TProgressBar* progress;
    static void IgnoreIrrelevantErrors();
    void TransparentIcons(int, void*, string);
};

//Constructor
Info::Info(TTreeView* TreeView, TImageList* list, TProgressBar* pb/*, TMemo* m*/)
{
	//memo = m;
	view = TreeView;
	ImageList = list;
	progress = pb;
	//get all system icons and create all needed variables
	if(!ico.GetSystemImageList())
	{
		ShowMessage("Couldn't load image list");
		exit(0);
	}

	//Fill imagelist with icons - NULL where no system icon
	//Obviously IconStuff stuff
	ico.AddIcons(ImageList);
}

/*
	Changes information about file attributes as binary string instead of flags
*/
AnsiString Info::getAttributes(unsigned long flags)
{
	String str;
	const struct
	{
		long flag;
		LPCTSTR name;
	}
	FlagNames[] = 	{
					{ FILE_ATTRIBUTE_ARCHIVE, _T("FILE_ATTRIBUTE_ARCHIVE") },
					{ FILE_ATTRIBUTE_COMPRESSED, _T("FILE_ATTRIBUTE_COMPRESSED") },
					{ FILE_ATTRIBUTE_DEVICE, _T("FILE_ATTRIBUTE_DEVICE") },
					{ FILE_ATTRIBUTE_DIRECTORY, _T("FILE_ATTRIBUTE_DIRECTORY") },
					{ FILE_ATTRIBUTE_ENCRYPTED, _T("FILE_ATTRIBUTE_ENCRYPTED") },
					{ FILE_ATTRIBUTE_HIDDEN, _T("FILE_ATTRIBUTE_HIDDEN") },
					{ FILE_ATTRIBUTE_NORMAL, _T("FILE_ATTRIBUTE_NORMAL") },
					{ FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, _T("FILE_ATTRIBUTE_NOT_CONTENT_INDEXED") },
					{ FILE_ATTRIBUTE_OFFLINE, _T("FILE_ATTRIBUTE_OFFLINE") },
					{ FILE_ATTRIBUTE_READONLY, _T("FILE_ATTRIBUTE_READONLY") },
					{ FILE_ATTRIBUTE_REPARSE_POINT, _T("FILE_ATTRIBUTE_REPARSE_POINT") },
					{ FILE_ATTRIBUTE_SPARSE_FILE, _T("FILE_ATTRIBUTE_SPARSE_FILE") },
					{ FILE_ATTRIBUTE_SYSTEM, _T("FILE_ATTRIBUTE_SYSTEM") },
					{ FILE_ATTRIBUTE_TEMPORARY, _T("FILE_ATTRIBUTE_TEMPORARY") },
					{ 0,NULL }
					};
	for (int i=0; FlagNames[i].name; i++)
	{
		if (flags & FlagNames[i].flag)
			str += "1";
		else
			str += "0";
	}
	return str;
}

/*
	Builds path for given node in TTreeView
*/
AnsiString Info::PathBuilder(TTreeNode* first)
{
	vector<AnsiString> part;
	TTreeNode* tmpNode = first;
	while(tmpNode)
	{
		part.push_back(tmpNode->Text);
		tmpNode = tmpNode->Parent;
	}
	AnsiString path = part[part.size()-1];
	for(int i=part.size()-2; i>=0; i--)
		path += part[i] + "\\";

	return path;
}

/*
	Changes irrevelant errors to no errors
  They are ignored because most of these errors are just information about
  irrevelant errors in checking for icons
*/
void Info::IgnoreIrrelevantErrors()
{
	switch (GetLastError())
	{
		case 2:
		case 5:
		case 6:
                case 8: //Happens without bcb installed?
                        //But it's ERROR_NOT_ENOUGH_MEMORY ....
		case 21:
		case 87:
		case 126:
		case 234:
		case 1008:
		case 1812:
		case 1813:
                case 4390: //Reparse point in case of creating virtual drives
                           //subst
		case 15106:
			SetLastError(0);
			break;
	}
}

/*
	Gets amount of items in a folder/volume
*/
int Info::Items(TTreeNode* first)
{
	int out=0;
	AnsiString check = PathBuilder(first);
	check = check + "*";
	WIN32_FIND_DATA* ffd = new WIN32_FIND_DATA;
	HANDLE s;
	s = FindFirstFile(check.c_str(), ffd);
	while(FindNextFile(s, ffd))
	{
		out++;
	}
	FindClose(s);
	SetLastError(0);
	return out-1;
}

/*
	Gets transparent icons and changes them into real ones
*/
void Info::TransparentIcons(int iIcon, void* hIcon, string toCopy)
{
		WORD lpiIcon;
			for(int z=0; z<(int)ico.GetTransparent().size(); z++)
			{
			 	if(ico.GetTransparent()[z] == iIcon)
				{
				 	Graphics::TIcon *tempIcon = new Graphics::TIcon();
				 	tempIcon->Handle = hIcon;
				 	if(tempIcon->Transparent)
				 	{
				 		char *path = new char[toCopy.length()+1];
				 		strcpy(path,toCopy.c_str());
				 		tempIcon->Handle = ExtractAssociatedIcon(NULL, path, &lpiIcon);
				 		delete[]path;
				 	}
				 	ImageList->ReplaceIcon(iIcon, tempIcon);
					GetLastError();
					ico.EraseFromTransparent(z);
					delete tempIcon;
        }
      }
}

//j - starting point of the tree(index)
void Info::addChilds(TTreeNode* first, int j=0)
{
	unsigned int size=1;
	vector<string> vec;
	SHFILEINFO stFileInfo;
	LPTSTR lpIconPath;
	HANDLE fileHandle;

	//Get all volumes and add them as main roots
	if(first == NULL)
	{
                //ShowMessage("przed vm");
		VolumeManagement vm;
                //ShowMessage("przed get");
		vec = vm.GetLogicalDrives_();
                //ShowMessage("po get");
		for(int i=0; i<(int)vec.size(); i++)
			view->Items->Add(NULL, vec[i].c_str());
		size = vec.size();
		//First was NULL so let's fill it now
		first = view->Items->GetFirstNode();
	}

	for(unsigned int i=0; i<size; i++)
	{
		int items = Items(first);
		WIN32_FIND_DATA* FindFileData = new WIN32_FIND_DATA;
		AnsiString search = PathBuilder(first);
		search = search + "*";
		fileHandle = FindFirstFile(search.c_str(), FindFileData);
		/*
    	if doesn't got any parents then have to be main root
			so setup image for it
      also we shouldn't do this if we're refreshing
    */
		if(!first->Parent && j==0)
		{
			SHGetFileInfo(vec[i].c_str(), NULL ,&stFileInfo,sizeof(stFileInfo),SHGFI_SYSICONINDEX);
			first->ImageIndex = stFileInfo.iIcon;
			first->SelectedIndex = stFileInfo.iIcon;

			//Just in case that normal icon would be replaced with .ico file
      TransparentIcons(stFileInfo.iIcon, stFileInfo.hIcon, vec[i]);
		}
		if (fileHandle != INVALID_HANDLE_VALUE)
		{
			int z=0;
			//Ignoring all irrevelant errors
			IgnoreIrrelevantErrors();
			//While no errors and found file add childs
			while(fileHandle != INVALID_HANDLE_VALUE && !GetLastError())
			{
				string tmp2 = FindFileData->cFileName;
				if(tmp2 == "." || tmp2 == "..")
				{
					FindFileData = new WIN32_FIND_DATA;
					FindNextFile(fileHandle, FindFileData);
				}
				else
				{

                                        BOOL full = false;
                                        if(FindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                                        {
                                                WIN32_FIND_DATA* nonEmptyData = new WIN32_FIND_DATA;
                                                string nonEmptySearch = search.c_str();
                                                nonEmptySearch = nonEmptySearch.substr(0,nonEmptySearch.length()-1);
                                                nonEmptySearch = nonEmptySearch + FindFileData->cFileName + "\\*";

                                                HANDLE nonEmptyHandle = FindFirstFile(nonEmptySearch.c_str(), nonEmptyData);
                                                if(nonEmptyHandle != INVALID_HANDLE_VALUE)
                                                {
                                                        int x=0;
                                                        while(nonEmptyHandle != INVALID_HANDLE_VALUE && x < 2)
                                                        {
                                                                FindNextFile(nonEmptyHandle, nonEmptyData);
                                                                string fname = nonEmptyData->cFileName;
                                                                if(fname != "." || fname != "..")
                                                                        x++;
                                                        }
                                                        FindClose(nonEmptyHandle);
                                                        if(x>=2 && (string)nonEmptyData->cFileName != "..")
                                                                full = true;
                                                }
                                        }

					if(first->Parent)
					{
					 	z++;
						int tmp = 100*(double)z/items;
						progress->Position = tmp;
					}
					//Building path to the file
					string tmp = search.c_str();
					tmp = tmp.substr(0, tmp.size()-1) + tmp2;


					//Getting icon index in system table and smallicon if it could be transparent
					SHGetFileInfo(tmp.c_str(), NULL ,&stFileInfo,sizeof(stFileInfo),SHGFI_SYSICONINDEX | SHGFI_ICON | SHGFI_SMALLICON);
					//Checking for transparent icons and replacing them with not transparent
					TransparentIcons(stFileInfo.iIcon, stFileInfo.hIcon, tmp);
					//Adding child node to currently selected main root
					TTreeNode* tmpNode = view->Items->AddChildObject(first, FindFileData->cFileName, FindFileData);
					tmpNode->ImageIndex=stFileInfo.iIcon;
					tmpNode->SelectedIndex=stFileInfo.iIcon;

                                        tmpNode->HasChildren = full;
					//Looking for next file
					FindFileData = new WIN32_FIND_DATA;
					FindNextFile(fileHandle, FindFileData);
					//Ignoring all irrevelant errors
					IgnoreIrrelevantErrors();
				}
			}
			//Close fileHandle or everything will blow up
			FindClose(fileHandle);
		}
		//Sorting all added nodes
		first->AlphaSort(false);
		//Getting next main root
		first = first->getNextSibling();
		delete FindFileData;
	}
}

/*
	Performs all shell operations such as open file or edit
*/
AnsiString Info::Shell(AnsiString filename, AnsiString method)
{
	SHELLEXECUTEINFO infoz;
	memset(&infoz, 0, sizeof(SHELLEXECUTEINFO));
	infoz.cbSize = sizeof(SHELLEXECUTEINFO);
	infoz.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI;
	infoz.lpVerb = method.c_str();
	infoz.lpFile = filename.c_str();
	infoz.nShow = SW_SHOWDEFAULT;
	if(!ShellExecuteEx(&infoz))
	{
		switch(GetLastError())
		{
			case ERROR_NO_ASSOCIATION:
				return "There is no application associated with the specified file name extension";
			case ERROR_ACCESS_DENIED:
				return "Access to the specified file is denied";
			case ERROR_SHARING_VIOLATION:
				return "A sharing violation occurred";
			case ERROR_NOT_ENOUGH_MEMORY:
				return "There is not enough memory to perform the specified action";
			default:
				if(method == "properties")
					return "Couldn't open properties window of the selected element";
				else
					return "Couldn't " + method + " selected element";
		}
	}
  if(method == "edit")
  	return "Editing";
  if(method == "open")
    return "Content opened";
  if(method == "properties")
    return "Properties opened";
  return "";
}

/*
	Checks if file is editable
*/
bool Info::Changeable(void* data)
{
	if(data == NULL)
		return false;
	PWIN32_FIND_DATA TempData = PWIN32_FIND_DATA(data);
	if(TempData->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
		return false;
	return true;
}

/*
	Used for recursive operations such as deleting folders with files inside
*/
bool Info::Operation(UINT operation, AnsiString from, AnsiString to=NULL)
{
	SHFILEOPSTRUCT lpFileOp;
	lpFileOp.hwnd = NULL;
	lpFileOp.fAnyOperationsAborted = false;
	lpFileOp.hNameMappings = NULL;
	lpFileOp.wFunc = operation;
	lpFileOp.pFrom = from.c_str();
	lpFileOp.pTo = to.c_str();
	lpFileOp.fFlags = FOF_NOCONFIRMATION|FOF_SILENT;

	int result = SHFileOperation(&lpFileOp);
	switch (result)
	{
		/*
		case 0x71: The source and destination files are the same file.
		case 0x7E: The destination path is an existing file.
		case 0x80: The destination path is an existing folder.
		*/
		case 0x71:
		case 0x7E:
		case 0x80:

		case 0xB7:
		case 0x81:
		case 0x10000:
		case 0x74:
		case 0x79:
		case 0x78:
		case 0x7C:
		case 0x72:
    //Strange error sometimes occur when trying to delete eg file from dvdrom
    case 0x02:
			return false;
		default:
			return true;
	}
}

/*
	Returns information about free/all bytes on drive
*/
unsigned __int64* Info::GetHddSizes(string disk)
{
	unsigned __int64 FreeBytesAvailable,
									 TotalNumberOfBytes,
									 TotalNumberOfFreeBytes;
	::GetDiskFreeSpaceEx(disk.c_str(),
											(PULARGE_INTEGER)&FreeBytesAvailable,
											(PULARGE_INTEGER)&TotalNumberOfBytes,
											(PULARGE_INTEGER)&TotalNumberOfFreeBytes);
	unsigned __int64* output = new unsigned __int64[3];
	output[0] = FreeBytesAvailable;
	output[1] = TotalNumberOfBytes;
	output[2] = TotalNumberOfFreeBytes;
	if(GetLastError())
		return NULL;
	return output;
}
