//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "MainForm.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TfrmMain *frmMain;
//---------------------------------------------------------------------------

/*
	Constructor
*/
__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner)
{
}
//---------------------------------------------------------------------------

/*
	Given the name of a file on the disk, retrieve information from it
	(e.g. date/time, size, etc.)
*/
TfrmMain::FileInfo TfrmMain::GetFileInfo(const AnsiString &Filename)
{
	FileInfo finfo;
	TSearchRec rec; // For FindFirst

		// Separate the full name into parts
	finfo.name = ExtractFileName(Filename);
	finfo.path = ExtractFilePath(Filename);
	finfo.extension = ExtractFileExt(Filename);

		// Returns 0 on success TODO: need handle failures at some point
	if (!FindFirst(Filename, faAnyFile, rec))
	{
			// File size (64-bit integer)
		finfo.size = rec.Size;

			// Convert file date format to a "better" format
			// Causes W8111 (deprecated)
		finfo.date = FileDateToDateTime(rec.Time);

			// File or Directory? Directories won't display a size (which is 0)
		if (rec.Attr & faDirectory)
			finfo.isfolder = true;
		else
			finfo.isfolder = false;
	}
	return finfo;
}

/*
	Given a filename (Drive:/Path/Filename.ext), add it to the list view
*/
void TfrmMain::AddFile(const char *Filename)
{
		// First, extract all of the file's information (size, date, etc.)
	FileInfo finfo = GetFileInfo(Filename);

		// 'Add' creates a new, empty item in the list view
	TListItem *item = lstFiles->Items->Add();

		// Set each "field" in the list view
	item->Caption = finfo.name;
	item->SubItems->Add(finfo.size);
	item->SubItems->Add(finfo.extension);
	item->SubItems->Add(finfo.date);
	item->SubItems->Add(finfo.path);

		// Files and folders have different icons
	if (finfo.isfolder)
		item->ImageIndex = 1;
	else
		item->ImageIndex = 0;
}

/*
	Callback when comparing the first field in the list view
*/
void __fastcall TfrmMain::CompareTextFields(TObject *Sender, TListItem* Item1, TListItem* Item2, int Data, int& Compare)
{
	Compare = CompareText(Item1->Caption, Item2->Caption);
}

/*
	Callback when comparing the Size field
*/
void __fastcall TfrmMain::CompareIntegerFields(TObject *Sender, TListItem* Item1, TListItem* Item2, int Data, int& Compare)
{
	Compare = StrToInt(Item2->SubItems->Strings[0]) < StrToInt(Item1->SubItems->Strings[0]);
}

/*
	Callback when comparing the Date field
*/
void __fastcall TfrmMain::CompareDateFields(TObject *Sender, TListItem* Item1, TListItem* Item2, int Data, int& Compare)
{
	Compare = StrToDateTime(Item2->SubItems->Strings[2]) < StrToDateTime(Item1->SubItems->Strings[2]);
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Event Handlers
///////////////////////////////////////////////////////////////////////////////
/*
	Handler for the form create event.
*/
void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
	lstFiles->Align = alClient;

		// Enable files to be dropped onto the form (a drop target)
	DragAcceptFiles(this->Handle, true);
}
//---------------------------------------------------------------------------

/*
	Handler for the form destroy event.
*/
void __fastcall TfrmMain::FormDestroy(TObject *Sender)
{
		// Disable the form as a drop target
	DragAcceptFiles(this->Handle, false);
}
//---------------------------------------------------------------------------

/*
	Handles the event of clicking on one of the column headers in the list view.
	We need to sort on that column
*/
void __fastcall TfrmMain::lstFilesColumnClick(TObject *Sender, TListColumn *Column)
{
		// Set comparison callback based on the column that was clicked
	if (Column->Index == 0)
		lstFiles->OnCompare = CompareTextFields;
	else if (Column->Index == 1)
		lstFiles->OnCompare = CompareIntegerFields;
	else if (Column->Index == 3)
		lstFiles->OnCompare = CompareDateFields;
	else
		return;

		// enum TSortType { stNone, stData, stText, stBoth };
	lstFiles->SortType = Comctrls::stData; // Set sort type
	lstFiles->AlphaSort();                 // Perform the sort
	lstFiles->SortType = Comctrls::stNone; // Reset sort to None
}
//---------------------------------------------------------------------------

/*
	This event handler handles *all* of the buttons on the toolbar. It
	checks their Tag to see which one invoked it, so the proper view
	can be set.
*/
void __fastcall TfrmMain::btnChangeViewClick(TObject *Sender)
{
		// TODO: need to deal with the (albeit, unlikely) case that Sender
		//       is not a TButton*
	TButton *button = (TButton *)Sender;

		// Now set the appropriate view
	if (button->Tag == 0)
		lstFiles->ViewStyle = vsReport;
	else if (button->Tag == 1)
		lstFiles->ViewStyle = vsList;
	else if (button->Tag == 2)
		lstFiles->ViewStyle = vsSmallIcon;
	else if (button->Tag == 3)
		lstFiles->ViewStyle = vsIcon;
}
//---------------------------------------------------------------------------

/*
	When files are dropped onto the form, this handles the event.
*/
void __fastcall TfrmMain::WMDropFiles(TWMDropFiles &Message)
{
	int num_files, filename_length;
	char file_name[MAX_PATH + 1];

		// Refer to the Win SDK documentation about the details
		// of DragQueryFile and the HDROP structure

		// Get the number of files that are being dropped on the app
	num_files = DragQueryFile((HDROP)Message.Drop, -1, NULL, 0);
	if (num_files < 1)
		return;

		// Retrieve each filename, one at a time
	for(int i = 0; i < num_files; i++)
	{
			// Get the length of the i'th filename
		filename_length = DragQueryFile((HDROP)Message.Drop, i, NULL, 0);

			// Retrieve the i'th filename from the HDROP structure
		DragQueryFile((HDROP)Message.Drop, i, file_name, filename_length + 1);

			// Add the file to the list
		AddFile(file_name);
	}
}

/*
	When the items are dropped, this function handles that event.
	Source is the component where the items are coming from and Sender
	is the component being dropped onto. Currently, we're only handling
	the case when we drag and drop on the same list view.
*/
void __fastcall TfrmMain::lstFilesDragDrop(TObject *Sender, TObject *Source, int X, int Y)
{
		// We are dropping within our own list view
	if (Sender == Source)
	{
		TListView* lv = (TListView *)Sender;
		TListItem* currentItem, *nextItem, *dragItem, *dropItem;

			// This is the item that we are dropping on
		dropItem = lv->GetItemAt(X, Y);

			// This is the item that is selected. There may be more than one
			// highlighted, but only one is the "selected" or "active" item.
		currentItem = lv->Selected;

			// Loop through all of the highlighted items and drop them
		while (currentItem != NULL)
		{
				// From the help:
				// TListItem *GetNextItem(StartItem, Direction, States)
				// Call GetNextItem to find the next list item after StartItem in the
				// direction given by the Direction parameter. Only items in the state
				// indicated by the States parameter are considered.
			nextItem = lv->GetNextItem(currentItem, sdAll, TItemStates() << isSelected) ;

				// If drop item is non-NULL, it's a valid position in the list and we
				// will insert there. If it's NULL, then we are below the last item in
				// the list, so we will Add to the end.
			if (dropItem)
				dragItem = lv->Items->Insert(dropItem->Index);
			else
				dragItem = lv->Items->Add();

				// Copy the data from the source to the new item
			dragItem->Assign(currentItem);

				// Free the old one (don't want two of them in the list)
			delete currentItem;

				// Move to the next one that is highlighted (if any)
			currentItem = nextItem;
		}
	}
}
//---------------------------------------------------------------------------

/*
	When a drag operation is over the list view, this code decides if the
	contents can be dropped. If this function returns true, then the mouse
	pointer changes to indicate it's a drop target. If it returns false,
	then the mouse pointer changes to indicate you can't drop here.
*/
void __fastcall TfrmMain::lstFilesDragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept)
{
	Accept = false;

		// Only accept drops from our control.
	if (Sender != lstFiles)
		return;

	TListView *lv = (TListView *)Sender;

		// Get the item that the mouse is over
	TListItem *item = lv->GetItemAt(X, Y);

		// Not going to allow moving an item onto itself. Go ahead, remove this
		// check and see what happens...
	if (item && item->Selected)
		return;

		// Passed the checks, so we will allow it to be dropped.
	Accept = true;
}
//---------------------------------------------------------------------------




