/////////////////////////////////////////////////////////////////////////////
// Name:        classlist.cpp
// Purpose:     ClassListDialog implementation
// Author:      Francesco Montorsi
// Modified by: 
// Created:     03/06/2007 14:49:55
// RCS-ID:      $Id$
// Copyright:   (c) 2007 Francesco Montorsi
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////


// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
 
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include "classlist.h"

#if !wxUSE_EXTENDED_RTTI
    #error This sample requires XTI (eXtended RTTI) enabled
#endif

// IMPLEMENT_DYNAMIC_CLASS( ClassListDialog, wxDialog )  -- see the header
BEGIN_EVENT_TABLE( ClassListDialog, wxDialog )
    EVT_LISTBOX( ID_LISTBOX, ClassListDialog::OnListboxSelected )
    EVT_TREE_SEL_CHANGED( ID_TREECTRL, ClassListDialog::OnTreectrlSelChanged )
    EVT_CHOICEBOOK_PAGE_CHANGED( ID_LISTMODE, ClassListDialog::OnChoiceBookPageChange )

    EVT_CHECKBOX( ID_SHOW_ONLY_XTI, ClassListDialog::OnShowOnlyXTICheckbox )
    EVT_CHECKBOX( ID_SHOW_PROPERTIES_RECURSIVELY, ClassListDialog::OnShowRecursiveInfoCheckbox )
END_EVENT_TABLE()

// defined later
wxString DumpClassInfo(const wxClassInfo*, bool recursive);


// ----------------------------------------------------------------------------
// ClassListDialog
// ----------------------------------------------------------------------------

ClassListDialog::ClassListDialog()
{
    Init();
}

ClassListDialog::ClassListDialog( wxWindow* parent, wxWindowID id, 
                                      const wxString& caption, const wxPoint& pos, 
                                      const wxSize& size, long style )
{
    Init();
    Create(parent, id, caption, pos, size, style);
}

bool ClassListDialog::Create( wxWindow* parent, wxWindowID id, const wxString& caption, 
                                const wxPoint& pos, const wxSize& size, long style )
{
    SetExtraStyle(wxWS_EX_BLOCK_EVENTS);
    wxDialog::Create( parent, id, caption, pos, size, style );

    CreateControls();
    if (GetSizer())
    {
        GetSizer()->SetSizeHints(this);
    }
    Centre();

    return true;
}

ClassListDialog::~ClassListDialog()
{
}

void ClassListDialog::Init()
{
    m_pClassCountText = NULL;
    m_pRawListBox = NULL;
    m_pParentTreeCtrl = NULL;
    m_pSizeListBox = NULL;
    m_pTextCtrl = NULL;
}

void ClassListDialog::CreateControls()
{
    wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
    this->SetSizer(itemBoxSizer2);

    wxStaticText* itemStaticText3 = new wxStaticText( this, wxID_STATIC, _("This is the list of wxWidgets classes registered in the XTI system.\nNote that not all wxWidgets classes are registered nor all registered classes are completely _described_ using XTI metadata."), wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer2->Add(itemStaticText3, 0, wxALIGN_LEFT|wxALL, 5);

    // filters
    wxBoxSizer* filters = new wxBoxSizer(wxHORIZONTAL);
    itemBoxSizer2->Add(filters, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5);
    filters->Add(new wxCheckBox(this, ID_SHOW_ONLY_XTI, 
                                wxT("Show only classes with eXtended infos")));
    filters->AddSpacer(10);
    filters->Add(new wxCheckBox(this, ID_SHOW_PROPERTIES_RECURSIVELY,
                                wxT("Show properties of parent classes")));

    // show how many have we filtered out
    m_pClassCountText = new wxStaticText( this, wxID_STATIC, 
                wxT("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"),
                wxDefaultPosition, wxDefaultSize, 0 );
    m_pClassCountText->SetFont(wxFont(8, wxSWISS, wxNORMAL, wxBOLD, false, wxT("Tahoma")));
    itemBoxSizer2->Add(m_pClassCountText, 0, wxALIGN_LEFT|wxLEFT|wxRIGHT|wxBOTTOM, 5);

    wxBoxSizer* itemBoxSizer5 = new wxBoxSizer(wxHORIZONTAL);
    itemBoxSizer2->Add(itemBoxSizer5, 1, wxGROW, 5);

    m_pChoiceBook = new wxChoicebook( this, ID_LISTMODE, wxDefaultPosition, wxDefaultSize, wxCHB_DEFAULT );

    // raw-list page
    wxPanel* itemPanel7 = new wxPanel( m_pChoiceBook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
    wxBoxSizer* itemBoxSizer8 = new wxBoxSizer(wxHORIZONTAL);
    itemPanel7->SetSizer(itemBoxSizer8);

    wxArrayString m_pRawListBoxStrings;
    m_pRawListBox = new wxListBox( itemPanel7, ID_LISTBOX, wxDefaultPosition, wxDefaultSize, m_pRawListBoxStrings, wxLB_SINGLE );
    itemBoxSizer8->Add(m_pRawListBox, 1, wxGROW, 5);

    m_pChoiceBook->AddPage(itemPanel7, _("Raw list"));

    // by-size page
    wxPanel* itemPanel13 = new wxPanel( m_pChoiceBook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxTAB_TRAVERSAL );
    wxBoxSizer* itemBoxSizer14 = new wxBoxSizer(wxHORIZONTAL);
    itemPanel13->SetSizer(itemBoxSizer14);

    wxArrayString m_pSizeListBoxStrings;
    m_pSizeListBox = new wxListBox( itemPanel13, ID_LISTBOX, wxDefaultPosition, wxDefaultSize, m_pSizeListBoxStrings, wxLB_SINGLE );
    itemBoxSizer14->Add(m_pSizeListBox, 1, wxGROW, 5);

    m_pChoiceBook->AddPage(itemPanel13, _("Classes by size"));

    // tree page
    wxPanel* itemPanel10 = new wxPanel( m_pChoiceBook, ID_PANEL, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
    wxBoxSizer* itemBoxSizer11 = new wxBoxSizer(wxVERTICAL);
    itemPanel10->SetSizer(itemBoxSizer11);

    m_pParentTreeCtrl = new wxTreeCtrl( itemPanel10, ID_TREECTRL, wxDefaultPosition, wxSize(100, 100), wxTR_HAS_BUTTONS |wxTR_SINGLE );
    itemBoxSizer11->Add(m_pParentTreeCtrl, 1, wxGROW, 5);

    m_pChoiceBook->AddPage(itemPanel10, _("Classes by parent"));


    itemBoxSizer5->Add(m_pChoiceBook, 0, wxGROW|wxALL, 5);

    m_pTextCtrl = new wxTextCtrl( this, ID_TEXTCTRL, wxT(""), wxDefaultPosition, wxSize(500, -1), wxTE_MULTILINE|wxTE_READONLY );
    itemBoxSizer5->Add(m_pTextCtrl, 3, wxGROW|wxALL, 5);

    wxStdDialogButtonSizer* itemStdDialogButtonSizer17 = new wxStdDialogButtonSizer;

    itemBoxSizer2->Add(itemStdDialogButtonSizer17, 0, wxGROW|wxALL, 5);
    wxButton* itemButton18 = new wxButton( this, wxID_OK, _("&OK"), wxDefaultPosition, wxDefaultSize, 0 );
    itemStdDialogButtonSizer17->AddButton(itemButton18);

    wxButton* itemButton19 = new wxButton( this, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxDefaultSize, 0 );
    itemStdDialogButtonSizer17->AddButton(itemButton19);

    itemStdDialogButtonSizer17->Realize();

    InitControls();
}

int ClassListDialog::AddClassesWithParent(const wxClassInfo *parent, const wxTreeItemId &id)
{
    const wxClassInfo *ci = wxClassInfo::GetFirst();
    int count = 0;
    while (ci)
    {
        // is this class derived from the given parent?
        if (wxString(ci->GetBaseClassName1()) == parent->GetClassName() ||
            wxString(ci->GetBaseClassName2()) == parent->GetClassName())
        {
            wxTreeItemId child = m_pParentTreeCtrl->AppendItem(id, ci->GetClassName());
            
            // update the name of this child with the count of the children classes
            int ret = AddClassesWithParent(ci, child);
            m_pParentTreeCtrl->SetItemText(child,
                                     m_pParentTreeCtrl->GetItemText(child) +
                                     wxString::Format(wxT(" [%d]"), ret));
            count += ret+1;
        }
        
        ci = ci->GetNext();
    }

    // reorder all the children we've just added
    m_pParentTreeCtrl->SortChildren(id);

    return count;
}

int GetSizeOfClass(const wxString &cn)
{
    const wxClassInfo *ci = wxClassInfo::FindClass(cn);
    if (ci)
        return ci->GetSize();
    return 0;
}

int CompareClassSizes(const wxString &class1, const wxString &class2)
{
    return GetSizeOfClass(class1) - GetSizeOfClass(class2);
}

void ClassListDialog::InitControls()
{
    // create a wxArrayString with the names of all classes:
    const wxClassInfo *ci = wxClassInfo::GetFirst();
    wxArrayString arr;
    while (ci)
    {
        arr.Add(ci->GetClassName());
        ci = ci->GetNext();
    }

    arr.Sort();     // sort alphabetically

    // now add it to the raw-mode listbox
    for (unsigned int i=0; i<arr.GetCount(); i++)
        if (!IsToDiscard(arr[i]))
            m_pRawListBox->Append(arr[i]);
    m_nCount = m_pRawListBox->GetCount();

    // sort again using size as sortkey
    arr.Sort((wxArrayString::CompareFunction)CompareClassSizes);

    // now add it to the size-mode listbox
    for (unsigned int i=0; i<arr.GetCount(); i++)
        if (!IsToDiscard(arr[i]))
            m_pSizeListBox->Append(arr[i]);

    // add root item to parent-mode treectrl
    wxTreeItemId id = m_pParentTreeCtrl->AddRoot(wxT("wxObject"));

    // recursively add all leaves to the treectrl
    int count = AddClassesWithParent(CLASSINFO(wxObject), id);
    m_pParentTreeCtrl->SetItemText(id, m_pParentTreeCtrl->GetItemText(id) +
                                 wxString::Format(wxT(" [%d]"), count));

    // initially expand the root item
    m_pParentTreeCtrl->Expand(id);

    m_nTotalCount = arr.GetCount();
    UpdateFilterText();

    // don't leave blank the XTI info display
    m_pChoiceBook->ChangeSelection(0);
    m_pRawListBox->Select(0);
    UpdateClassInfo(m_pRawListBox->GetStringSelection());
}

bool ClassListDialog::IsToDiscard(const wxString &classname) const
{
    wxCheckBox *cb = wx_static_cast(wxCheckBox*, FindWindow(ID_SHOW_ONLY_XTI));
    if (!cb || !cb->IsChecked())
        return false;

    // check if this class has XTI infos
    wxClassInfo *info = wxClassInfo::FindClass(classname);
    if (!info)
        return false;
    if (info->GetFirstProperty() != NULL || info->GetFirstHandler() != NULL)
        return false;       // has XTI info
    return true;            // no XTI info
}

void ClassListDialog::UpdateFilterText()
{
    // tell the user how many registered classes are present and
    // how many are we showing
    m_pClassCountText->SetLabel(
        wxString::Format(
            wxT("Showing %d classes on a total of %d registered classes in wxXTI."), 
            m_nCount, m_nTotalCount));
}

void ClassListDialog::UpdateClassInfo(const wxString &itemName)
{
    wxString classname = itemName.BeforeFirst(wxT(' '));
    wxCheckBox *cb = wx_static_cast(wxCheckBox*, FindWindow(ID_SHOW_PROPERTIES_RECURSIVELY));

    m_pTextCtrl->SetValue(
        DumpClassInfo(wxClassInfo::FindClass(classname), cb->IsChecked()));
}


// ----------------------------------------------------------------------------
// ClassListDialog - event handlers
// ----------------------------------------------------------------------------

void ClassListDialog::OnShowOnlyXTICheckbox( wxCommandEvent& WXUNUSED(event) )
{
    m_pRawListBox->Clear();
    m_pParentTreeCtrl->DeleteAllItems();
    m_pSizeListBox->Clear();

    InitControls();
}

void ClassListDialog::OnShowRecursiveInfoCheckbox( wxCommandEvent& WXUNUSED(event) )
{
    m_pRawListBox->Clear();
    m_pParentTreeCtrl->DeleteAllItems();
    m_pSizeListBox->Clear();

    InitControls();
}

void ClassListDialog::OnListboxSelected( wxCommandEvent& event )
{
    UpdateClassInfo(event.GetString());
}

void ClassListDialog::OnTreectrlSelChanged( wxTreeEvent& event )
{
    UpdateClassInfo(m_pParentTreeCtrl->GetItemText(event.GetItem()));
}

void ClassListDialog::OnChoiceBookPageChange( wxChoicebookEvent& event )
{
    switch (event.GetSelection())
    {
    case 0:
        if (m_pRawListBox->GetCount())
        {
            m_pRawListBox->Select(0);
            UpdateClassInfo(m_pRawListBox->GetStringSelection());
        }
        break;
    case 1:
        if (m_pSizeListBox->GetCount())
        {
            m_pSizeListBox->Select(0);
            UpdateClassInfo(m_pSizeListBox->GetStringSelection());
        }
        break;
    case 2:
        {
            wxTreeItemId root = m_pParentTreeCtrl->GetRootItem();
            if (root.IsOk())
            {
                m_pParentTreeCtrl->SelectItem(root);
                UpdateClassInfo(m_pParentTreeCtrl->GetItemText(root));
            }
        }
        break;
    }
}



// ----------------------------------------------------------------------------
// dump functions
// ----------------------------------------------------------------------------

wxString DumpStr(const wxString &str)
{
    if (str.empty())
        return wxT("none");
    return str;
}

wxString DumpTypeInfo(const wxTypeInfo *ti)
{
    if (!ti)
        return wxT("none");

    return DumpStr(ti->GetTypeName());
}

wxString DumpPropertyAccessor(const wxPropertyAccessor *acc, int indent)
{
    wxString ind = wxT("\n") + wxString(indent, wxT(' '));
    wxString infostr;

    if (!acc)
        return ind + wxT("no property accessors");
    
    if (acc->HasSetter())
        infostr << ind << wxT("setter name: ") << acc->GetSetterName();
    if (acc->HasCollectionGetter())
        infostr << ind << wxT("collection getter name: ") << acc->GetCollectionGetterName();
    if (acc->HasGetter())
        infostr << ind << wxT("getter name: ") << acc->GetGetterName();
    if (acc->HasAdder())
        infostr << ind << wxT("adder name: ") << acc->GetAdderName();

    return infostr;
}

wxString DumpPropertyInfo(const wxPropertyInfo *prop, int indent)
{
    wxString ind = wxT("\n") + wxString(indent, wxT(' '));
    wxString infostr;

    if (!prop)
        return ind + wxT("none");

    infostr << ind << wxT("flags: ");
    if (prop->GetFlags() & wxPROP_DEPRECATED)
        infostr << wxT("wxPROP_DEPRECATED,");
    if (prop->GetFlags() & wxPROP_OBJECT_GRAPH)
        infostr << wxT("wxPROP_OBJECT_GRAPH,");
    if (prop->GetFlags() & wxPROP_ENUM_STORE_LONG)
        infostr << wxT("wxPROP_ENUM_STORE_LONG,");
    if (prop->GetFlags() & wxPROP_DONT_STREAM)
        infostr << wxT("wxPROP_DONT_STREAM,");

    if (prop->GetFlags() == 0)
        infostr << wxT("none");
    else
        infostr.RemoveLast();       // remove last comma

    infostr << ind << wxT("help string: ") << DumpStr(prop->GetHelpString());
    infostr << ind << wxT("group string: ") << DumpStr(prop->GetGroupString());

    infostr << ind << wxT("collection element type: ") << DumpTypeInfo(prop->GetCollectionElementTypeInfo());
    infostr << ind << wxT("type: ") << DumpTypeInfo(prop->GetTypeInfo());

    infostr << ind << wxT("default value: ") << DumpStr(wxAnyGetAsString(prop->GetDefaultValue()));
    infostr << DumpPropertyAccessor(prop->GetAccessor(), indent+1);

    return infostr;
}

wxString DumpHandlerInfo(const wxHandlerInfo *phdlr, int indent)
{
    wxString ind = wxT("\n") + wxString(indent, wxT(' '));
    wxString infostr;

    if (!phdlr)
        return ind + wxT("none");

    infostr << ind << wxT("event class: ") << 
        (phdlr->GetEventClassInfo() ? phdlr->GetEventClassInfo()->GetClassName() : wxT("none"));

    return infostr;
}

int DumpProperties(const wxClassInfo *info, wxString& infostr, bool recursive)
{
    const wxPropertyInfo *prop;
    int pcount;
    for (prop = info->GetFirstProperty(), pcount = 0;
         prop;
         prop = prop->GetNext(), pcount++)
    {
        infostr << wxT("\n\n  [") << pcount+1 << wxT("] Property: ") << prop->GetName();
        infostr << DumpPropertyInfo(prop, 4);
    }

    if (pcount == 0)
        infostr << wxT("\n None");

    if (recursive)
    {
        const wxClassInfo **parent = info->GetParents();
        wxString str;

        for (int i=0; parent[i] != NULL; i++)
        {
            int ppcount = DumpProperties(parent[i], str, recursive);
            if (ppcount)
            {
                pcount += ppcount;
                infostr << wxT("\n\n  ") << parent[i]->GetClassName() << wxT(" PARENT'S PROPERTIES:");
                infostr << str;
            }
        }
    }

    return pcount;
}

int DumpHandlers(const wxClassInfo *info, wxString& infostr, bool recursive)
{
    const wxHandlerInfo *h;
    int hcount;
    for (h = info->GetFirstHandler(), hcount = 0;
         h;
         h = h->GetNext(), hcount++)
    {
        infostr << wxT("\n\n  [") << hcount+1 << wxT("] Handler: ") << h->GetName();
        infostr << DumpHandlerInfo(h, 4);
    }

    if (hcount == 0)
        infostr << wxT("\n None");

    if (recursive)
    {
        const wxClassInfo **parent = info->GetParents();
        wxString str;

        for (int i=0; parent[i] != NULL; i++)
        {
            int hhcount = DumpHandlers(parent[i], str, recursive);
            if (hhcount)
            {
                hcount += hhcount;
                infostr << wxT("\n\n  ") << parent[i]->GetClassName() << wxT(" PARENT'S HANDLERS:");
                infostr << str;
            }
        }
    }

    return hcount;
}

wxString DumpClassInfo(const wxClassInfo *info, bool recursive)
{
    wxString infostr;

    if (!info)
        return wxEmptyString;

    // basic stuff:

    infostr << wxT("\n BASIC RTTI INFO ABOUT ") << info->GetClassName();
    infostr << wxT("\n =================================================");
    infostr << wxT("\n  Base class #1: ") << DumpStr(info->GetBaseClassName1());
    infostr << wxT("\n  Base class #2: ") << DumpStr(info->GetBaseClassName2());
    infostr << wxT("\n  Include file: ") << DumpStr(info->GetIncludeName());
    infostr << wxT("\n  Size: ") << info->GetSize();
    infostr << wxT("\n  Dynamic: ") << (info->IsDynamic() ? wxT("true") : wxT("false"));


    // advanced stuff:

    infostr << wxT("\n\n\n ADVANCED RTTI INFO ABOUT ") << info->GetClassName();
    infostr << wxT("\n =================================================\n");
    infostr << wxT("\n PROPERTIES");
    infostr << wxT("\n -----------------------------------------");
    int pcount = DumpProperties(info, infostr, recursive);
    infostr << wxT("\n\n HANDLERS");
    infostr << wxT("\n -----------------------------------------");
    int hcount = DumpHandlers(info, infostr, recursive);

    if (pcount+hcount == 0)
        infostr << wxT("\n\n no advanced info\n");
    else
    {
        infostr << wxT("\n\n Total count of properties: ") << pcount;
        infostr << wxT("\n Total count of handlers: ") << hcount << wxT("\n");
    }

    return infostr;
}
