// OptTree.cpp : implementation file
//

#include "stdafx.h"
#include "afxpriv.h"
#include "mainfrm.h"
#include "WgsCnf.h"
#include "CnfDoc.h"
#include "modifier.h"
#include "OptTree.h"
#include "HelpView.h"
#include "OptEdit.h"
#include "CnfAsrt.h"
#include "edtoff.h"
#include "regutil.h"
#include "levels.h"

#define FILREV "$Revision: $"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

const int UnModOption = 0;
const int ModOption = 1;
const int UnModGroup = 2;
const int ModGroup = 3;

extern CWgsCnfApp theApp;

/////////////////////////////////////////////////////////////////////////////
// OptionTree

IMPLEMENT_DYNCREATE(OptionTree, CTreeView)


OptionTree::OptionTree()
{
     lastPos = NULL;
     m_expanded = FALSE;
     m_selected = NULL;
     m_first = NULL;
     m_ourFont = NULL;
     m_report.SetTreeView( this );
}

OptionTree::~OptionTree()
{
     if (m_ourFont != NULL)
     {
          LOGFONT   lf;
          m_ourFont->GetLogFont( &lf );

          SaveLogfont( "settings", "TreeViewFontInfo", lf );

          delete m_ourFont;
          m_ourFont = NULL;
     }
}


BEGIN_MESSAGE_MAP(OptionTree, CTreeView)
     //{{AFX_MSG_MAP(OptionTree)
     ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnSelchanged)
     ON_WM_CTLCOLOR_REFLECT()
     ON_NOTIFY_REFLECT(TVN_SELCHANGING, OnSelchanging)
     ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnItemexpanded)
     ON_WM_DESTROY()
	ON_COMMAND(ID_FILE_PRINT, OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, OnFilePrintPreview)
	ON_COMMAND(IDD_TEXTREPORT, OnTextreport)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// OptionTree drawing

void OptionTree::OnDraw(CDC* pDC)
{
     m_report.Draw( pDC );
     return;
}

/////////////////////////////////////////////////////////////////////////////
// OptionTree diagnostics

#ifdef _DEBUG
void OptionTree::AssertValid() const
{
     CTreeView::AssertValid();
}

void OptionTree::Dump(CDumpContext& dc) const
{
     CTreeView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// OptionTree message handlers

void OptionTree::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
     // TODO: Add your specialized code here and/or call the base class
     Modifier *theMod = static_cast< Modifier * >( pHint );
     CTreeCtrl &tree = GetTreeCtrl();
     
     switch (lHint)
     {
          case NewDocument:
               OnInitialUpdate();
               break;

          case ItemModified:
               {
                    TreeData ourData;

                    CString key = theMod->itemKey;
                    CnfDoc *doc = (CnfDoc *)GetDocument();

                    BOOL      lookRes = doc->Lookup( key, ourData );
                    CnfAssert( lookRes );

                    BOOL      updateParent = FALSE;

                    if (! ourData.Modified())
                    {
                         tree.SetItemState( ourData.treePtr, TVIS_BOLD, TVIS_BOLD );
                         ++ourData.modCount;
                         doc->Set( key, ourData );

                         tree.SetItemImage( ourData.treePtr, ModOption, ModOption );

                         updateParent = TRUE;
                    }

                    if (updateParent)
                    {
                         BOOL      foundParent = FALSE;

                         while (ourData.parentKey != "")
                         {
                              key = ourData.parentKey;

                              if (doc->Lookup( ourData.parentKey, ourData ))
                              {
                                   if (ourData.opPtr == NULL)
                                   {
                                        foundParent = TRUE;
                                        break;
                                   }
                              }
                              else
                                   break;
                         }

                         if (foundParent)
                         {
     //                         tree.SetItemText( ourData.treePtr, ourData.header );
                              if (! ourData.Modified())
                              {
                                   tree.SetItemState( ourData.treePtr, TVIS_BOLD, TVIS_BOLD );
                                   tree.SetItemImage( ourData.treePtr, ModGroup, ModGroup );
                              }

                              ++ourData.modCount;
                              doc->Set( key, ourData );
                         }
                    }
               }
               break;

          case ItemUnModified:
               {
                    CString key = theMod->itemKey;
                    CnfAssert( key.GetLength() > 0 );
                    CnfDoc *doc = (CnfDoc *)GetDocument();
                    CnfAssert( doc != NULL );

                    TreeData ourData;
                    
                    BOOL lookRes = doc->Lookup( key, ourData );
                    CnfAssert( lookRes );

                    BOOL      changed = ourData.Modified();

                    if (changed)
                    {
                         CnfAssert( ourData.modCount > 0 );
                         --ourData.modCount;
                         doc->Set( key, ourData );
                    }

                    if (changed)
                    {
                         tree.SetItemState( ourData.treePtr, 0, TVIS_BOLD );
                         tree.SetItemImage( ourData.treePtr, UnModOption, UnModOption );

                         BOOL      foundParent = FALSE;

                         while (ourData.parentKey != "")
                         {
                              key = ourData.parentKey;

                              if (doc->Lookup( key, ourData ))
                              {
                                   if (ourData.opPtr == NULL)
                                   {
                                        foundParent = TRUE;
                                        break;
                                   }
                              }
                              else
                                   break;
                         }

                         if (foundParent)
                         {
                              CnfAssert( ourData.modCount > 0 );
                              --ourData.modCount;
                              doc->Set( key, ourData );
                         
                              if (! ourData.Modified())
                              {
                                   tree.SetItemState( ourData.treePtr, 0, TVIS_BOLD );
                                   tree.SetItemImage( ourData.treePtr, UnModGroup, UnModGroup );
                              }
                         }
                    }
               }
               break;

          case ItemAdded:
               {
                    TreeData &ourData = *theMod->treeData;
                    CnfDoc *doc = (CnfDoc *)GetDocument();

                    UINT mask = TVIF_STATE | TVIF_TEXT | TVIF_PARAM;
                    UINT state = m_expanded ? TVIS_EXPANDED : 0;
                    UINT stateMask = TVIS_EXPANDED; // | TVIS_FOCUSED;

                    HTREEITEM parentNode = TVI_ROOT;

                    TreeData parentData;

                    if (ourData.parentKey != "")
                    {
                         BOOL      lookRes = doc->Lookup( ourData.parentKey, parentData );
                         CnfAssert( lookRes );

                         parentNode = parentData.treePtr;
                    }

                    CString   &saveKey = theMod->itemKey;

                    if (lastPos == NULL)
                         lastPos = strList.AddHead( saveKey );
                    else
                         lastPos = strList.InsertAfter( lastPos, saveKey );

                    ourData.treePtr = tree.InsertItem( mask, ourData.header, 0, 0, 
                                                               state, stateMask,
                                                               (LPARAM)lastPos, parentNode,
                                                               TVI_LAST
                                                             );

                    doc->Set( theMod->itemKey, ourData );

                    CMainFrame *mfm = GetFrame();

                    CnfAssert( mfm != NULL );

                    mfm->UpdateLoadGraph( theMod->sofar, theMod->totalBytes,
                                           theMod->itemCount
                                         );

                    if (m_selected == NULL)
                    {
                         m_selected = ourData.treePtr;
                         tree.SelectItem( ourData.treePtr );
                    }

                    if (m_first == NULL)
                    {
                         m_first = ourData.treePtr;
                    }

                    if (ourData.opPtr == NULL)
                    {
                         tree.SetItemImage( ourData.treePtr, UnModGroup, UnModGroup );
                    }
                    else
                    {
                         tree.SetItemImage( ourData.treePtr, UnModOption, UnModOption );
                    }

//                    HTREEITEM cur = m_selected;

//                    if (cur == NULL) 
//                    {
//                         cur = tree.GetFirstVisibleItem();
//                    }

//                    if (cur != NULL)
//                    {
//                         tree.EnsureVisible( cur );
//                    }
               }
               break;

          case VisibleChange:
               if (theMod->treeData->treePtr != NULL)
               {
                    if (tree.GetSelectedItem() == theMod->treeData->treePtr)
                    {
                         if (m_oldItem == NULL) 
                         {
                              m_oldItem = m_first;
                         }

                         tree.SelectItem( m_oldItem );
                    }

                    BOOL      deleted = tree.DeleteItem( theMod->treeData->treePtr );
                    CnfAssert( deleted );
               }
               break;

          case NextVisible:
               {
                    HTREEITEM cur = tree.GetSelectedItem();

                    if (cur != NULL)
                    {
                         HTREEITEM next = tree.GetNextVisibleItem( cur );

                         if (next != NULL)
                         {
                              SelectItem( next );
                              CnfDoc *doc = static_cast< CnfDoc * >( GetDocument() );

                              CnfAssert( doc != NULL );

                              doc->UpdateAllViews( this, FocusOnEdit );
                         }
                    }
               }
               break;

          default:
               CTreeView::OnUpdate( pSender, lHint, pHint );
     }

     return;
}

void OptionTree::OnInitialUpdate() 
{
     CTreeView::OnInitialUpdate();

     static BOOL         first = TRUE;

     if (first)
     {
          CTreeCtrl &tree = GetTreeCtrl();

          CFont *theFont = tree.GetFont();

          if (theFont != NULL)
          {
               LOGFONT lf;

               theFont->GetLogFont( &lf );

               OptionEditView::SetFontChars( lf );
               HelpView::SetFontChars( lf );

               first = FALSE;
          }
     }
     
     // TODO: Add your specialized code here and/or call the base class
     
     CTreeCtrl &tree = CTreeView::GetTreeCtrl();

     HWND hwnd = tree.m_hWnd;
     
     LONG  style = GetWindowLong( hwnd, GWL_STYLE );
     SetWindowLong( hwnd, GWL_STYLE, 
                     style |
                   TVS_HASLINES |
                   TVS_LINESATROOT |
                   TVS_HASBUTTONS |
                   TVS_SHOWSELALWAYS |
                   TVS_DISABLEDRAGDROP 
                 );

     tree.DeleteAllItems();

     CnfDoc *doc = (CnfDoc *)GetDocument();

     UINT theCount = tree.GetCount();

     if (theCount < 0)
          CWnd::MessageBox( "Shit." );
//   else if (theCount == 0)
//   {
//        TreeData ourData;
//
//        ourData.fullText = "Nothing";
//        ourData.header   = "Nothing";
//        ourData.indentLevel = 0;
//        ourData.parentKey = 0;
//
//        ourData.treePtr = tree.InsertItem( TVIF_STATE | TVIF_TEXT | TVIF_PARAM, 
//                                                   ourData.header, 0, 0, 
//                                            TVIS_EXPANDED, 
//                                                   TVIS_EXPANDED | TVIS_SELECTED, // | TVIS_FOCUSED,
//                                                   1, TVI_ROOT,
//                                                   TVI_LAST
//                                                 );
//
//        doc->m_doc.SetAt( count, ourData );
//
//        first = ourData.treePtr;
//   }

//   if (first != NULL)
//        tree.Expand( first, TVE_EXPAND );

     OnUpdate( NULL, 0, NULL );

     return;
}

void OptionTree::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult) 
{
     NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
     // TODO: Add your control notification handler code here

     CnfAssert( pNMTreeView != NULL );

     TV_ITEM &newItem = pNMTreeView->itemNew;
     TV_ITEM &oldItem = pNMTreeView->itemOld;

     m_selected = newItem.hItem;

     if (oldItem.hItem != NULL)
     {
          m_oldItem = oldItem.hItem;
     }

     CnfDoc *doc = (CnfDoc *)GetDocument();

     CnfAssert( doc != NULL );

     POSITION p = reinterpret_cast< POSITION >( newItem.lParam );
     const CString &key = strList.GetAt( p );

     CString oldKey;

     p = reinterpret_cast< POSITION >( oldItem.lParam );
     if (p != NULL)
          oldKey = strList.GetAt( p );

     TreeData ourData;

     BOOL      lookRes = doc->Lookup( key, ourData );

     CnfAssert( lookRes );

     doc->m_selected = key;

     CMainFrame *mfm = GetFrame();

     if ((ourData.opPtr == NULL) &&
         ((level > 0) || (ourData.parentKey.GetLength() > 0))
        )
     {
          mfm->SetHeaderStatus( ourData.filename + " - " + ourData.header );
     }
     else
     {
          mfm->SetHeaderStatus( ourData.header );
     }

     Modifier theMod;

     theMod.itemKey = key;
     theMod.treeData = &ourData;
     theMod.oldKey = oldKey;

     doc->UpdateAllViews( this, SelectionChanged, &theMod );
     
     *pResult = 0;
}



CSplitterWnd *OptionTree::GetSplitter() const
{
     return( STATIC_DOWNCAST( CSplitterWnd, GetParent() ) );

}

CMainFrame *OptionTree::GetFrame() const
{
     const CWnd *wnd = this;

     while (! wnd->IsKindOf( RUNTIME_CLASS( CMainFrame ) ))
     {
          wnd = wnd->GetParent();

          CnfAssert( wnd != NULL );
     }

     return( (CMainFrame *)wnd );
}


HBRUSH OptionTree::CtlColor(CDC* pDC, UINT nCtlColor) 
{
//   HBRUSH brush = (HBRUSH)GetStockObject( LTGRAY_BRUSH );

//   return( brush );
     
     // TODO: Return a non-NULL brush if the parent's handler should not be called
     return NULL;
}

BOOL OptionTree::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) 
{
     // TODO: Add your specialized code here and/or call the base class
     
     BOOL result = CWnd::Create(lpszClassName, lpszWindowName, 
                                 dwStyle | TVS_SHOWSELALWAYS, 
                                 rect, pParentWnd, nID, pContext
                                );

     if (result)
     {
          m_imageList.Create( IDB_BITMAP2, 16, 1, RGB( 255, 255, 255 ) );

          CBitmap map2;
          CBitmap map3;
          CBitmap map4;
          BOOL     bitmapLoaded = map2.LoadBitmap( IDB_BITMAP1 );
          CnfAssert( bitmapLoaded );
          bitmapLoaded = map3.LoadBitmap( IDB_UNMODGROUP );
          CnfAssert( bitmapLoaded );
          bitmapLoaded = map4.LoadBitmap( IDB_MODGROUP );
          CnfAssert( bitmapLoaded );

          m_imageList.Add( &map2, RGB( 255, 255, 255 ) );
          m_imageList.Add( &map3, RGB( 255, 255, 255 ) );
          m_imageList.Add( &map4, RGB( 255, 255, 255 ) );

          CTreeCtrl &tree = CTreeView::GetTreeCtrl();
          tree.SetImageList( &m_imageList, TVSIL_NORMAL );

          LOGFONT   lf;

          if (ReadLogfont( "settings", "TreeViewFontInfo", lf ))
          {
               SetFontChars( lf, TRUE );

               CTreeCtrl &tree = GetTreeCtrl();
               tree.SetFont( m_ourFont, TRUE );
          }
     }

     return( result );
}


void OptionTree::OnSelchanging(NMHDR* pNMHDR, LRESULT* pResult) 
{
     if (ExternalEditorActive())
     {
          *pResult = 1;
          return;
     }

     CnfDoc *doc = (CnfDoc *)GetDocument();

     CnfAssert( doc != NULL );

     BOOL      valid = TRUE;

     if (doc != NULL)
     {
          valid = doc->ValidateCurrentItem();

          if (! valid)
          {
               MessageBeep( MB_ICONASTERISK );
          }
     }

     if (valid && (doc != NULL))
     {
          CTreeCtrl &tree = GetTreeCtrl();
          NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
          CnfAssert( pNMTreeView != NULL );

          TV_ITEM &newItem = pNMTreeView->itemNew;
          
          DWORD     info = tree.GetItemData( newItem.hItem );

          POSITION p = reinterpret_cast< POSITION >( info );
          const CString &key = strList.GetAt( p );
          
          if (! doc->IsVisible( key ))
          {
               valid = FALSE;

               TV_ITEM &oldItem = pNMTreeView->itemOld;
               info = tree.GetItemData( oldItem.hItem );

               p = reinterpret_cast< POSITION >( info );
               const CString &oldKey = strList.GetAt( p );
          
               TreeData data;

               if (doc->Lookup( oldKey, data ))
               {
                    Modifier mod;

                    mod.oldKey = mod.itemKey = oldKey;
                    mod.treeData = &data;

                    doc->UpdateAllViews( this, VisibleChange, &mod );
               }
          }
     }

     *pResult = valid ? 0 : 1;

     return;
}

HTREEITEM OptionTree::AddItem( int itemKey, const CString &itemText, HTREEITEM itemParent )
{
     return( NULL );
}

void OptionTree::DeleteTree( HTREEITEM item )
{
     CTreeCtrl &tree = GetTreeCtrl();

     HTREEITEM thisChild = tree.GetChildItem( item );

     while (thisChild != NULL)
     {
          if (tree.ItemHasChildren( thisChild ))
               DeleteTree( thisChild );
          
          CString *iData = (CString *)tree.GetItemData( thisChild );

          delete iData;

          thisChild = tree.GetNextSiblingItem( item );
     }
}


HTREEITEM OptionTree::NextItem( HTREEITEM curItem )
{
     CTreeCtrl &tree = GetTreeCtrl();

     HTREEITEM next = NULL;
     HTREEITEM sib = NULL;

     if (tree.ItemHasChildren( curItem ))
     {
          next = tree.GetChildItem( curItem );
     }
     else if ((sib = tree.GetNextSiblingItem( curItem )) != NULL)
     {
          next = sib;
     }
     else
     {
          HTREEITEM parent = tree.GetParentItem( curItem );

          while ((parent != NULL) && (parent != TVI_ROOT))
          {
               next = tree.GetNextSiblingItem( parent );
               if (next != NULL)
               {
                    break;
               }
               else
               {
                    parent = tree.GetParentItem( parent );
               }
          }
     }
     
     return( next );
}

HTREEITEM OptionTree::PrevItem( HTREEITEM curItem )
{
     CTreeCtrl &tree = GetTreeCtrl();

     HTREEITEM prev = tree.GetPrevSiblingItem( curItem );

     if (prev == NULL)
     {
          prev = tree.GetParentItem( curItem );

          if (prev == TVI_ROOT)
          {
               prev = NULL;
          }
     }
     else
     {
          while (tree.ItemHasChildren( prev ))
          {
               prev = tree.GetChildItem( prev );

               CnfAssert( prev != NULL );

               HTREEITEM sib = tree.GetNextSiblingItem( prev );

               while (sib != NULL)
               {
                    prev = sib;
                    sib = tree.GetNextSiblingItem( prev );
               }
          }
     }

     return( prev );
}


HTREEITEM OptionTree::GetSelectedItem() const
{
     return( GetTreeCtrl().GetSelectedItem() );
}


BOOL OptionTree::GetTreeData( HTREEITEM item, TreeData &treeData ) const
{
     CTreeCtrl &tree = GetTreeCtrl();

     DWORD     info = tree.GetItemData( item );

     POSITION p = reinterpret_cast< POSITION >( info );
     const CString &key = strList.GetAt( p );

     const CnfDoc *doc = (CnfDoc *)GetDocument();

     BOOL      lookRes = doc->Lookup( key, treeData );
     
     return( lookRes );
}

void OptionTree::SelectItem( HTREEITEM item )
{
     CTreeCtrl &tree = GetTreeCtrl();

     tree.SelectItem( item );
}

void OptionTree::ExpandAll( BOOL expanded )
{
     m_expanded = expanded;

     if (expanded)
     {
          CTreeCtrl &tree = GetTreeCtrl();

          HTREEITEM sel = tree.GetSelectedItem();
          if (sel == NULL)
          {
               sel = m_first;
          }

//          HTREEITEM cur = tree.GetFirstVisibleItem();
          HTREEITEM cur = m_first;

          while (cur != NULL)
          {
               tree.Expand( cur, TVE_EXPAND );

               cur = NextItem( cur );
          }

          tree.Select( sel, TVGN_FIRSTVISIBLE );
     }
}

BOOL OptionTree::Expanded() const
{
     return( m_expanded );
}

void OptionTree::OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult) 
{
     NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
     // TODO: Add your control notification handler code here

     if (pNMTreeView->action == TVE_COLLAPSE)
     {
          if (m_expanded)
          {
               m_expanded = FALSE;

               CMainFrame *frame = GetFrame();
               CnfAssert( frame != NULL );

               frame->SetExpandAll( FALSE );
          }
     }
     
     *pResult = 0;
}


void OptionTree::CollapseAll()
{
     m_expanded = FALSE;

     CTreeCtrl &tree = GetTreeCtrl();

//     HTREEITEM first = tree.GetFirstVisibleItem();
     HTREEITEM cur = m_first;

     tree.SelectItem( m_first );

     while (cur != NULL)
     {
          tree.Expand( cur, TVE_COLLAPSE );

          cur = NextItem( cur );
     }

     tree.Select( m_first, TVGN_FIRSTVISIBLE );
}

void OptionTree::SetFontChars( const LOGFONT &lf, BOOL replaceOld)
{
     if (replaceOld && (m_ourFont != NULL))
     {
          delete m_ourFont;
          m_ourFont = NULL;
     }

     if (m_ourFont == NULL)
     {
          m_ourFont = new CFont;
          m_ourFont->CreateFontIndirect( &lf );
     }
}

BOOL OptionTree::PreTranslateMessage(MSG* pMsg) 
{
     if ((pMsg->message == WM_CONTEXTMENU) ||
         (pMsg->message == WM_RBUTTONDOWN)
        )
     {
          EditFont();
          return( TRUE );
     }
     
     return CTreeView::PreTranslateMessage(pMsg);
}

void OptionTree::EditFont()
{
     CTreeCtrl &tree = GetTreeCtrl();
     CFont    *font = tree.GetFont();

     if (font == NULL)
     {
          font = m_ourFont;
     }

     CnfAssert( font != NULL );

     LOGFONT   lf;
     font->GetLogFont( &lf );

     CFontDialog dlg( &lf, CF_SCREENFONTS, NULL, this );

     if (dlg.DoModal() == IDOK)
     {
          SetFontChars( lf, TRUE );
          tree.SetFont( m_ourFont, TRUE );
     }
}

BOOL OptionTree::OnPreparePrinting(CPrintInfo* pInfo) 
{
     return( DoPreparePrinting( pInfo ) );
}

void OptionTree::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo) 
{
     m_report.SetDoc( static_cast< CnfDoc * >(GetDocument()) );
     m_report.Begin( pDC, pInfo );
}

void OptionTree::OnPrint(CDC* pDC, CPrintInfo* pInfo) 
{
     m_report.PrintPage( pDC, pInfo );
}

void OptionTree::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) 
{
	CTreeView::OnPrepareDC(pDC, pInfo);

     pInfo->m_bContinuePrinting = ! m_report.PastEnd( pInfo );
}


void OptionTree::Print()
{
     OnFilePrint();
}

void OptionTree::PrintPreview()
{
     OnFilePrintPreview();
}

int OptionTree::GetCount()
{
     return( GetTreeCtrl().GetCount() );
}

CString OptionTree::GetItemText( HTREEITEM item )
{
     CnfAssert( item != NULL );
     return( GetTreeCtrl().GetItemText( item ) );
}

HTREEITEM OptionTree::GetFirst()
{
     return( m_first );
}


void OptionTree::OnTextreport() 
{
     TextReport();
}

void OptionTree::TextReport( const char *filename )
{
     m_report.ReportToFile( filename );
}
