#include "customgui_listview.h"
#include "c4d_string.h"
#include "c4d_gui.h"
#include "c4d_general.h"

TreeViewLib *lib_tree = NULL;

static TreeViewLib *CheckLib(LONG offset)
{
	return (TreeViewLib*)CheckLib(CUSTOMGUI_TREEVIEW, offset, (C4DLibrary**)&lib_tree);
}

#define TreeViewGuiCall(b) 		TreeViewLib *lib = CheckLib(LIBOFFSET(TreeViewLib, b)); \
															if (!lib || !lib->b) return; \
															(((iTreeViewCustomGui*)this)->*lib->b)

#define TreeViewGuiCallR(a,b) TreeViewLib *lib = CheckLib(LIBOFFSET(TreeViewLib, b)); \
															if (!lib || !lib->b) return a; \
															return (((iTreeViewCustomGui*)this)->*lib->b)

static TreeViewLib *CheckPrefsLib(LONG offset)
{
	return (TreeViewLib*)CheckLib(CUSTOMGUI_TREEVIEW,offset,(C4DLibrary**)&lib_tree);
}

OBSOLETE TreeViewFunctions::CreateContextMenu(void *root, void *userdata, void *obj, BaseContainer* bc)
{
  GeAssert(FALSE);
	return 0;
}

OBSOLETE TreeViewFunctions::ContextMenu(void *root,void *userdata,void *obj, LONG lCommand)
{
  GeAssert(FALSE);
	return 0;
}

OBSOLETE TreeViewFunctions::GetColumnWidth(void *root,void *userdata,void *obj,LONG col)
{
  GeAssert(FALSE);
	return 0;
}

OBSOLETE TreeViewFunctions::GetHeaderColumnWidth(void *root,void *userdata,LONG col)
{
  GeAssert(FALSE);
	return 0;
}

OBSOLETE TreeViewFunctions::GetLineHeight(void *root,void *userdata,void *obj,LONG col)
{
  GeAssert(FALSE);
	return 0;
}

// version 0 functions
void*  TreeViewF::GetFirst(TreeViewFunctions* pFuncs, void *root,void *userdata)
{ return pFuncs->GetFirst(root, userdata); }

void*  TreeViewF::GetDown(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj)
{ return pFuncs->GetDown(root, userdata, obj); }

void*  TreeViewF::GetNext(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj)
{ return pFuncs->GetNext(root, userdata, obj); }

void*  TreeViewF::GetPred(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj)
{ return pFuncs->GetPred(root, userdata, obj); }

LONG   TreeViewF::GetColumnWidth(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj,LONG col)
{ return pFuncs->GetColumnWidth(root, userdata, obj, col, NULL); }

LONG   TreeViewF::GetHeaderColumnWidth(TreeViewFunctions* pFuncs, void *root,void *userdata, LONG col)
{ return pFuncs->GetHeaderColumnWidth(root, userdata, col, NULL); }

LONG   TreeViewF::GetLineHeight(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj,LONG col)
{ return pFuncs->GetLineHeight(root, userdata, obj, col); }

void   TreeViewF::DrawCell(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj,LONG col,DrawInfo *drawinfo, LONG bgColor)
{ pFuncs->DrawCell(root, userdata, obj, col, drawinfo, bgColor); }

Bool   TreeViewF::DrawHeaderCell(TreeViewFunctions* pFuncs, void *root,void *userdata,LONG col,DrawInfo *drawinfo)
{ return pFuncs->DrawHeaderCell(root, userdata, col, drawinfo); }

Bool   TreeViewF::IsSelected(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj)
{ return pFuncs->IsSelected(root, userdata, obj); }

Bool   TreeViewF::IsOpened(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj)
{ return pFuncs->IsOpened(root, userdata, obj); }

String TreeViewF::GetName(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj)
{ return pFuncs->GetName(root, userdata, obj); }

LONG   TreeViewF::GetId(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj)
{ return pFuncs->GetId(root, userdata, obj); }

LONG   TreeViewF::GetDragType(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj)
{ return pFuncs->GetDragType(root, userdata, obj); }

void   TreeViewF::GetColors(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj, GeData* pNormal, GeData* pSelected)
{ pFuncs->GetColors(root, userdata, obj, pNormal, pSelected); }

LONG   TreeViewF::GetMinHeaderHeight(TreeViewFunctions* pFuncs, void *root,void *userdata)
{ return pFuncs->GetMinHeaderHeight(root, userdata); }

void   TreeViewF::CreateContextMenu(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj, BaseContainer* bc)
{ pFuncs->CreateContextMenuColumn(root, userdata, obj, -1, bc); }

Bool   TreeViewF::ContextMenu(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj, LONG lCommand)
{
  if (lCommand == ID_TREEVIEW_CONTEXT_REMOVE)
  {
    DeletePressed(pFuncs, root, userdata);
    return TRUE;
  }
  return pFuncs->ContextMenuColumn(root, userdata, obj, -1, lCommand);
}

void   TreeViewF::CreateContextMenuColumn(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj, LONG lColumn, BaseContainer* bc)
{ pFuncs->CreateContextMenuColumn(root, userdata, obj, lColumn, bc); }

Bool   TreeViewF::ContextMenuColumn(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj, LONG lColumn, LONG lCommand)
{
  if (lCommand == ID_TREEVIEW_CONTEXT_REMOVE)
  {
    DeletePressed(pFuncs, root, userdata);
    return TRUE;
  }
  return pFuncs->ContextMenuColumn(root, userdata, obj, lColumn, lCommand);
}

Bool   TreeViewF::IsTristate(TreeViewFunctions* pFuncs, void *root, void *userdata)
{ return pFuncs->IsTristate(root, userdata); }

void   TreeViewF::Open(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj,Bool onoff)
{ pFuncs->Open(root, userdata, obj, onoff); }

void   TreeViewF::Select(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj,LONG mode)
{ pFuncs->Select(root, userdata, obj, mode); }

LONG   TreeViewF::AcceptDragObject(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj,LONG dragtype,void *dragobject, Bool &bAllowCopy)
{ return pFuncs->AcceptDragObject(root, userdata, obj, dragtype, dragobject, bAllowCopy); }

void   TreeViewF::InsertObject(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj,LONG dragtype,void *dragobject,LONG insertmode, Bool bCopy)
{ pFuncs->InsertObject(root, userdata, obj, dragtype, dragobject, insertmode, bCopy); }

void   TreeViewF::SetName(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj, const String &str)
{ pFuncs->SetName(root, userdata, obj, str); }

void   TreeViewF::SetDragObject(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj)
{ pFuncs->SetDragObject(root, userdata, obj); }

void   TreeViewF::GenerateDragArray(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj, AtomArray* ar)
{ pFuncs->GenerateDragArray(root, userdata, obj, ar); }

Bool   TreeViewF::MouseDown(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj,LONG col,MouseInfo *mouseinfo, Bool rightButton)
{ return pFuncs->MouseDown(root, userdata, obj, col, mouseinfo, rightButton); }

Bool   TreeViewF::DoubleClick(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj,LONG col,MouseInfo *mouseinfo)
{ return pFuncs->DoubleClick(root, userdata, obj, col, mouseinfo); }

void   TreeViewF::DeletePressed(TreeViewFunctions* pFuncs, void *root, void *userdata)
{ pFuncs->DeletePressed(root, userdata); }

// from version 8.1
void   TreeViewF::SelectionChanged(TreeViewFunctions* pFuncs, void *root, void *userdata)
{ pFuncs->SelectionChanged(root, userdata); }

LONG   TreeViewF::GetHeaderSortArrow(TreeViewFunctions* pFuncs, void *root, void *userdata, LONG lColID)
{ return pFuncs->GetHeaderSortArrow(root, userdata, lColID); }

Bool   TreeViewF::HeaderClick(TreeViewFunctions* pFuncs, void *root, void *userdata, LONG lColID, LONG lChannel, Bool bDbl)
{ return pFuncs->HeaderClick(root, userdata, lColID, lChannel, bDbl); }

Bool   TreeViewF::UserCanResizeCol(TreeViewFunctions* pFuncs, void *root, void *userdata, LONG lColID)
{ return pFuncs->UserCanResizeCol(root, userdata, lColID); }

Bool   TreeViewF::UserCanMoveCol(TreeViewFunctions* pFuncs, void *root, void *userdata, LONG lColID)
{ return pFuncs->UserCanMoveCol(root, userdata, lColID); }

Bool   TreeViewF::ColumnMoved(TreeViewFunctions* pFuncs, void *root, void *userdata, LONG lColID, LONG lInsertBeforeColIndex, LONG* plIndexMap, const BaseContainer* pbcLayout)
{ return pFuncs->ColumnMoved(root, userdata, lColID, lInsertBeforeColIndex, plIndexMap, pbcLayout); }

// from 8.6
LONG   TreeViewF::GetColumnWidthArea(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj,LONG col, GeUserArea* area)
{ return pFuncs->GetColumnWidth(root, userdata, obj, col, area); }

LONG   TreeViewF::GetHeaderColumnWidthArea(TreeViewFunctions* pFuncs, void *root,void *userdata, LONG col, GeUserArea* area)
{ return pFuncs->GetHeaderColumnWidth(root, userdata, col, area); }

LONG   TreeViewF::GetLineHeightArea(TreeViewFunctions* pFuncs, void *root,void *userdata,void *obj,LONG col, GeUserArea* area)
{ return pFuncs->GetLineHeight(root, userdata, obj, col, area); }

// from 9.5
void   TreeViewF::GenerateDragData(TreeViewFunctions* pFuncs, void *root, void *userdata, void* obj, void **dragdata)
{ pFuncs->GenerateDragData(root, userdata, obj, dragdata); }

void   TreeViewF::FreeDragData(TreeViewFunctions* pFuncs, void *root, void *userdata, void *dragdata)
{ pFuncs->FreeDragData(root, userdata, dragdata); }

// may not be a member of TreeViewCustomGui, an internal copy is created
InternalFunctionTable TreeViewInternalFunctionTable;
TreeViewF TreeViewControlFunctions;

Bool TreeViewCustomGui::SetRoot(void *root, TreeViewFunctions *functions, void *userdata)
{
	TreeViewLib *lib = CheckPrefsLib(LIBOFFSET(TreeViewLib,SetRoot));
	if (!lib || !lib->SetRoot) return FALSE;

  InternalFunctionTable* f = &TreeViewInternalFunctionTable;
  ClearMem(f, sizeof(InternalFunctionTable), 0);

  f->GetFirst            = &TreeViewF::GetFirst;
  f->GetDown             = &TreeViewF::GetDown;
  f->GetNext             = &TreeViewF::GetNext;
  f->GetPred             = &TreeViewF::GetPred;
  f->GetColumnWidth      = &TreeViewF::GetColumnWidth;
  f->GetHeaderColumnWidth= &TreeViewF::GetHeaderColumnWidth;
  f->GetLineHeight       = &TreeViewF::GetLineHeight;
  f->DrawCell            = &TreeViewF::DrawCell;
  f->DrawHeaderCell      = &TreeViewF::DrawHeaderCell;
  f->IsSelected          = &TreeViewF::IsSelected;
  f->IsOpened            = &TreeViewF::IsOpened;
  f->GetName             = &TreeViewF::GetName;
  f->GetId               = &TreeViewF::GetId;
  f->GetDragType         = &TreeViewF::GetDragType;
  f->GetColors           = &TreeViewF::GetColors;
  f->GetMinHeaderHeight  = &TreeViewF::GetMinHeaderHeight;
  f->CreateContextMenu   = &TreeViewF::CreateContextMenu;
  f->ContextMenu         = &TreeViewF::ContextMenu;
  f->CreateContextMenuColumn   = &TreeViewF::CreateContextMenuColumn;
  f->ContextMenuColumn         = &TreeViewF::ContextMenuColumn;
  f->IsTristate          = &TreeViewF::IsTristate;
  f->Open                = &TreeViewF::Open;
  f->Select              = &TreeViewF::Select;
  f->AcceptDragObject    = &TreeViewF::AcceptDragObject;
  f->InsertObject        = &TreeViewF::InsertObject;
  f->SetName             = &TreeViewF::SetName;
  f->SetDragObject       = &TreeViewF::SetDragObject;
  f->GenerateDragArray   = &TreeViewF::GenerateDragArray;
  f->MouseDown           = &TreeViewF::MouseDown;
  f->DoubleClick         = &TreeViewF::DoubleClick;
  f->DeletePressed       = &TreeViewF::DeletePressed;
  f->SelectionChanged    = &TreeViewF::SelectionChanged;
  f->GetHeaderSortArrow  = &TreeViewF::GetHeaderSortArrow;
  f->HeaderClick         = &TreeViewF::HeaderClick;
  f->UserCanResizeCol    = &TreeViewF::UserCanResizeCol;
  f->UserCanMoveCol      = &TreeViewF::UserCanMoveCol;
  f->ColumnMoved         = &TreeViewF::ColumnMoved;
  f->GetColumnWidthArea  = &TreeViewF::GetColumnWidthArea;
  f->GetHeaderColumnWidthArea = &TreeViewF::GetHeaderColumnWidthArea;
  f->GetLineHeightArea   = &TreeViewF::GetLineHeightArea;
  f->GenerateDragData    = &TreeViewF::GenerateDragData;
  f->FreeDragData        = &TreeViewF::FreeDragData;

  return (((iTreeViewCustomGui*)this)->*lib->SetRoot)(root, f, userdata, sizeof(InternalFunctionTable), &TreeViewControlFunctions, functions);
}

void TreeViewCustomGui::Refresh()
{
  TreeViewGuiCall(Refresh)();
  return;
}

Bool TreeViewCustomGui::SetLayout(LONG columns,const BaseContainer &data)
{
  TreeViewGuiCallR(FALSE, SetLayout)(columns, data);
}

Bool TreeViewCustomGui::SetHeaderText(LONG lColumnID, const String &str)
{
  TreeViewGuiCallR(FALSE, SetHeaderText)(lColumnID, str);
}

GeUserArea* TreeViewCustomGui::GetTreeViewArea()
{
  TreeViewGuiCallR(NULL, GetTreeViewArea)();
}

GeUserArea* TreeViewCustomGui::GetHeaderArea()
{
  TreeViewGuiCallR(NULL, GetHeaderArea)();
}

LONG TreeViewCustomGui::GetAutoColumnWidth(LONG lColID)
{
  TreeViewGuiCallR(0, GetAutoColumnWidth)(lColID);
}

LONG TreeViewCustomGui::GetColumnWidthUser(LONG lColID)
{
  TreeViewGuiCallR(0, GetColumnWidthUser)(lColID);
}

LONG TreeViewCustomGui::GetRealColumnWidth(LONG lColID)
{
  TreeViewGuiCallR(0, GetRealColumnWidth)(lColID);
}

Bool TreeViewCustomGui::SetColumnWidthUser(LONG lColID, LONG lWidth)
{
  TreeViewGuiCallR(FALSE, SetColumnWidthUser)(lColID, lWidth);
}

Bool TreeViewCustomGui::MakeVisible(void* pObj)
{
  TreeViewGuiCallR(FALSE, MakeVisible)(pObj);
}

Bool TreeViewCustomGui::GetCellPosition(void* pObj, LONG lColID, GeUserArea *&pArea, LONG &x, LONG &y, LONG &w, LONG &h, LONG &lTextOffset)
{
	TreeViewGuiCallR(FALSE, GetCellPosition)(pObj, lColID, pArea, x, y, w, h, lTextOffset);
}

Bool TreeViewCustomGui::IsFocusItem(void* pObj)
{
	TreeViewGuiCallR(FALSE, IsFocusItem)(pObj);
}

Bool TreeViewCustomGui::ShowObject(void* pObj)
{
  TreeViewGuiCallR(FALSE, ShowObject)(pObj);
}

void* TreeViewCustomGui::GetFocusItem()
{
	TreeViewGuiCallR(NULL, GetFocusItem)();
}

void TreeViewCustomGui::SetFocusItem(void* pItem)
{
	TreeViewGuiCall(SetFocusItem)(pItem);
}

const BaseContainer* TreeViewCustomGui::GetLayout()
{
	TreeViewGuiCallR(NULL, GetLayout)();
}


void*	TreeViewFunctions::GetPred(void *root,void *userdata,void *obj)
{
	return NULL;
}

LONG TreeViewFunctions::GetColumnWidth(void *root,void *userdata,void *obj,LONG col, GeUserArea* area)
{
	return 0;
}

LONG TreeViewFunctions::GetHeaderColumnWidth(void *root,void *userdata,LONG col, GeUserArea* area)
{
	return -1;
}

LONG TreeViewFunctions::GetLineHeight(void *root,void *userdata,void *obj,LONG col, GeUserArea* area)
{
	return 0;
}

void TreeViewFunctions::DrawCell(void *root,void *userdata,void *obj,LONG col,DrawInfo *drawinfo, LONG bgColor)
{
}

Bool TreeViewFunctions::DrawHeaderCell(void *root,void *userdata,LONG col,DrawInfo *drawinfo)
{
  // let the tree view draw its header
  return FALSE;
}

void TreeViewFunctions::GetColors(void *root,void *userdata,void *obj, GeData* pNormal, GeData* pSelected)
{
}

LONG TreeViewFunctions::GetMinHeaderHeight(void *root,void *userdata)
{
  return -1;
}

void TreeViewFunctions::Open(void *root,void *userdata,void *obj,Bool onoff)
{
}

void TreeViewFunctions::Select(void *root,void *userdata,void *obj,LONG mode)
{
}

LONG TreeViewFunctions::AcceptDragObject(void *root,void *userdata,void *obj,LONG dragtype,void *dragobject, Bool &bAllowCopy)
{
	return 0;
}

void TreeViewFunctions::InsertObject(void *root,void *userdata,void *obj,LONG dragtype,void *dragobject,LONG insertmode, Bool bCopy)
{
}

void TreeViewFunctions::SetName(void *root,void *userdata,void *obj, const String &str)
{
}

void TreeViewFunctions::SetDragObject(void *root,void *userdata,void *obj)
{
}

void TreeViewFunctions::GenerateDragArray(void *root,void *userdata,void *obj, AtomArray* ar)
{
}

Bool TreeViewFunctions::MouseDown(void *root,void *userdata,void *obj,LONG col,MouseInfo *mouseinfo, Bool rightButton)
{
	return FALSE;
}

Bool TreeViewFunctions::DoubleClick(void *root,void *userdata,void *obj,LONG col,MouseInfo *mouseinfo)
{
	return FALSE;
}

void TreeViewFunctions::DeletePressed(void *root, void *userdata)
{
}

void TreeViewFunctions::SelectionChanged(void *root, void *userdata)
{
}

LONG TreeViewFunctions::GetHeaderSortArrow(void *root, void *userdata, LONG lColID)
{
  return 0;
}

void TreeViewFunctions::CreateContextMenuColumn(void *root, void *userdata, void *obj, LONG lColumn, BaseContainer* bc)
{
}

Bool TreeViewFunctions::ContextMenuColumn(void *root,void *userdata,void *obj, LONG lColumn, LONG lCommand)
{
  return FALSE;
}

Bool TreeViewFunctions::IsTristate(void *root, void *userdata)
{
  return FALSE;
}

Bool TreeViewFunctions::HeaderClick(void *root, void *userdata, LONG lColID, LONG lChannel, Bool bDbl)
{
  return FALSE;
}

Bool TreeViewFunctions::UserCanResizeCol(void *root, void *userdata, LONG lColID)
{
  return TRUE;
}

Bool TreeViewFunctions::UserCanMoveCol(void *root, void *userdata, LONG lColID)
{
  return TRUE;
}

Bool TreeViewFunctions::ColumnMoved(void *root, void *userdata, LONG lColID, LONG lInsertBeforeCol, LONG* plIndexMap, const BaseContainer* pbcLayout)
{
  return TRUE;
}

void TreeViewFunctions::GenerateDragData(void *root, void *userdata, void* obj, void **dragdata)
{
}

void TreeViewFunctions::FreeDragData(void *root, void *userdata, void *dragdata)
{
}

Bool TreeViewFunctions::VisitChildren(void* root, void* obj, VisitChildrenCallback fn, void* userdata, void* data1, void* data2)
{
  Bool b;
  b = TRUE;

  if (obj != root)
  {
    if (!fn(root, obj, userdata, data1, data2, b)) return FALSE;
  }

  void* n;
  if (obj == root)
    n = GetFirst(root, userdata);
  else
    n = GetDown(root, userdata, obj);

  while (n && b)
  {
    if (!VisitChildren(root, n, fn, userdata, data1, data2)) return FALSE;
    n = GetNext(root, userdata, n);
  }

  return TRUE;
}
