#include "stdafx.h"
#include <afxpriv.h>
#include "gcommlib.h"
#include "CnfAsrt.h"
#include "Report.h"
#include "OptTree.h"
#include "levels.h"
#include "CnfDoc.h"
#include "resource.h"
#include "cnf.h"
#include "Saving.h"
#include "cstrutil.h"


Report::Report()
{
     m_pfCreated = FALSE;
     m_pastEnd = -1;
     m_treeView = NULL;
     m_doc = NULL;
     m_curPrintItem = m_first = NULL;
     m_tabstop = 0;
}


Report::~Report()
{
}


void Report::Begin( CDC *pDC, CPrintInfo *pInfo )
{
     m_pastEnd = 0;

     pDC->SetMapMode( MM_TEXT );

     if (! m_pfCreated)
     {
          LOGFONT lf;

          CFont *treeFont = m_treeView->GetFont();

          treeFont->GetLogFont( &lf );

          // map to printer font metrics
          HDC hDCFrom = ::GetDC(NULL);
          lf.lfHeight = ::MulDiv(lf.lfHeight, pDC->GetDeviceCaps(LOGPIXELSY),
                  ::GetDeviceCaps(hDCFrom, LOGPIXELSY));
          lf.lfWidth = ::MulDiv(lf.lfWidth, pDC->GetDeviceCaps(LOGPIXELSX),
                  ::GetDeviceCaps(hDCFrom, LOGPIXELSX));
          ::ReleaseDC(NULL, hDCFrom);

          if (lf.lfWeight > FW_NORMAL)
          {
               lf.lfWeight = FW_NORMAL;
          }

          m_printFont.CreateFontIndirect( &lf );

          lf.lfWeight = FW_BOLD;
          m_boldPrintFont.CreateFontIndirect( &lf );

          m_pfCreated = TRUE;
     }

     CFont *oldFont = pDC->SelectObject( &m_printFont );

     m_rowsPerPage = RowsPerPage( pDC );

     m_tabstop = pDC->GetOutputTextExtent( "WWWWWWWW: " ).cx;

     int       count = m_treeView->GetCount();

     m_curPrintItem = m_first;
     SetupDate();

     pDC->SelectObject( oldFont );

     m_curSection.Empty();

     if (level > 0)
     {
          m_levelDesc = GetLevelDesc( NULL, level );
     }
     else
     {
          m_levelDesc.Empty();
     }
}

BOOL Report::PastEnd( CPrintInfo *pInfo )
{
     BOOL      pastEnd = FALSE;

     if (m_pastEnd > 0)
     {
          pastEnd = (pInfo->m_nCurPage >= m_pastEnd);
     }

     return( pastEnd );
}


void Report::Draw( CDC *pDC )
{
     CRect      clipRect;
     pDC->GetClipBox( &clipRect );
     clipRect.left = clipRect.top = 0;
     clipRect.right = pDC->GetDeviceCaps(HORZRES) - 1;
     clipRect.bottom = pDC->GetDeviceCaps(VERTRES) - 1;

     pDC->SetMapMode( MM_TEXT );

     BOOL      firstOnPage = TRUE;

     HTREEITEM cur = m_curPrintItem;
     TEXTMETRIC met;
     pDC->GetOutputTextMetrics( &met );
     int       textHeight = met.tmHeight;
     int       startCount = 0;

     if (IsLevel( cur ))
     {
          m_levelDesc = m_treeView->GetItemText( cur );
          cur = m_treeView->NextItem( cur );
          CnfAssert( cur != NULL );
     }

     Header( pDC, m_levelDesc );
     Footer( pDC );

     if (m_curSection.GetLength() > 0)
     {
          if (! IsSection( cur ))
          {
               BoxText( pDC, m_curSection + " (continued)", 
                        0, GetHeaderHeight( pDC ),
                        clipRect.right, GetHeaderHeight( pDC ) + textHeight - 1
                      );

               ++startCount;
               firstOnPage = FALSE;
          }
     }

     for ( int count = startCount; count < m_rowsPerPage; ++count )
     {
          if (IsLevel( cur ))
          {
               return;
          }
          else if (IsSection( cur ))
          {
               //   don't bother with section header only,
               //   wait for next page
               //
               int       required = 2;

               if (! firstOnPage)
               {
                    ++required;              // add separator line
               }

               if ((count + required) >= m_rowsPerPage)
               {
                    break;
               }

               if (! firstOnPage)
               {
                    ++count;
               }
          }
          else if (GetItemType( cur ) == 'T')
          {
               cur = m_treeView->NextItem( cur );

               if (cur == NULL)
               {
                    m_pastEnd = m_curPage + 1;
                    break;
               }
               else
               {
                    --count;
                    continue;
               }
          }

          PrintRow( cur, count, pDC, textHeight );
          firstOnPage = FALSE;

          CnfAssert( m_treeView != NULL );
          cur = m_treeView->NextItem( cur );

          if (cur == NULL)
          {
               m_pastEnd = m_curPage + 1;
               break;
          }
     }
}

void Report::PrintPage( CDC *pDC, CPrintInfo *pInfo )
{
     CnfAssert( m_treeView != NULL );

     HTREEITEM cur = m_treeView->GetFirst();
     CnfAssert( cur != NULL );

     CString   curSec;

     for ( int count = 1; count < pInfo->m_nCurPage; ++count )
     {
          BOOL      firstOnPage = TRUE;
          int       startCount = 0;

          if (IsLevel( cur ))
          {
               curSec.Empty();
               cur = m_treeView->NextItem( cur );
          }

          if (curSec.GetLength() > 0)
          {
               if (! IsSection( cur ))
               {
                    ++startCount;
                    firstOnPage = FALSE;
               }
          }

          for ( int count = startCount; count < m_rowsPerPage; ++count )
          {
               if (IsLevel( cur ))
               {
                    break;
               }
               else if (IsSection( cur ))
               {
                    //   don't bother with section header only,
                    //   wait for next page
                    //
                    int       required = 2;

                    if (! firstOnPage)
                    {
                         ++required;              // add separator line
                    }

                    if ((count + required) >= m_rowsPerPage)
                    {
                         break;
                    }

                    if (! firstOnPage)
                    {
                         ++count;
                    }
               }
               else if (GetItemType( cur ) == 'T')
               {
                    cur = m_treeView->NextItem( cur );
                    CnfAssert( cur != NULL );
                    --count;
                    continue;
               }

               firstOnPage = FALSE;

               TreeData ourData;

               curSec = m_treeView->GetItemText( cur );

               CnfAssert( m_treeView != NULL );
               cur = m_treeView->NextItem( cur );
               CnfAssert( cur != NULL );
          }
     }

     m_curPrintItem = cur;

     CFont     *oldFont = pDC->SelectObject( &m_printFont );

     pDC->SetMapMode( MM_TEXT );
     pDC->SetViewportOrg( 0, 0 );

     CRect rectClip;
     pDC->GetClipBox( &rectClip );

     int nPageWidth = pDC->GetDeviceCaps(HORZRES);
     int nPageHeight = pDC->GetDeviceCaps(VERTRES);
     rectClip = CRect(0, 0, nPageWidth, nPageHeight );

     CRgn rgn;
     ///CRect rectClip;

     // Function to compute the clipping rectangle regardless of
     // whether we are in print preview mode. This would be the
     // function in your code which computes this rectangle and
     // stores it in rectClip 

     //ComputeClippingRectangle(pDC,&rectClip);

     // Now if we are in print preview mode then the clipping
     // rectangle needs to be adjusted before creating the
     // clipping region
     if (pDC->IsKindOf(RUNTIME_CLASS(CPreviewDC)))
     {
          CPreviewDC *pPrevDC = (CPreviewDC *)pDC;

//          pPrevDC->PrinterDPtoScreenDP(&rectClip.TopLeft());
//          pPrevDC->PrinterDPtoScreenDP(&rectClip.BottomRight());

          // Now offset the result by the viewport origin of
          // the print preview window...

          CPoint ptOrg;
          ::GetViewportOrgEx(pDC->m_hDC,&ptOrg);
//          rectClip += ptOrg;
     }

     // The following two function calls are the ones that
     // select the clipping region into the DC. These would be
     // whatever code you already have to create/select the
     // clipping region 

     rgn.CreateRectRgn(rectClip.left,rectClip.top,
                       rectClip.right,rectClip.bottom);
     pDC->SelectClipRgn(&rgn);     
     
     //CRgn region;
     //region.CreateRectRgnIndirect( &rectClip );
     //pDC->SelectClipRgn( &region );
     //int       rtype = pDC->IntersectClipRect(&rectClip);

     m_curPage = pInfo->m_nCurPage;

     Draw( pDC );

     pDC->SelectObject( oldFont );
}

int Report::RowsPerPage( CDC *pDC )
{
     TEXTMETRIC met;

     pDC->GetOutputTextMetrics( &met );

     int       pageHeight = pDC->GetDeviceCaps( VERTRES );
     int       textHeight = met.tmHeight;

     int       usableHeight = pageHeight - GetHeaderHeight( pDC ) - GetFooterHeight( pDC );

     return( usableHeight / textHeight );
}

void Report::PrintRow( HTREEITEM item, int rowNum, CDC *pDC, int textHeight )
{
     CString   outText = m_treeView->GetItemText( item );

     int       rowPos = (rowNum * textHeight) + GetHeaderHeight( pDC );

     int       right = pDC->GetDeviceCaps( HORZRES );

     TreeData ourData;

     CnfAssert( m_treeView != NULL );
     if (m_treeView->GetTreeData( item, ourData ))
     {
          int       textPos = right;

          if (ourData.opPtr != NULL)
          {
               int            colon = outText.Find( ':' );

               if (colon >= 0)
               {
                    CString   preTab = outText.Left( colon + 1 );
                    CString   postTab = outText.Mid( colon + 1 );

                    lrTrim( preTab );
                    lrTrim( postTab );

                    outText = preTab + "\t" + postTab;
               }

               pDC->TabbedTextOut( 0, rowPos, outText, 1, &m_tabstop, 0 );

               CString   opText = "(text block)";

               if (ourData.opPtr->type != 'T')
               {
                    opText = ourData.opPtr->curValue;
               }

               CFont    *saveFont = pDC->SelectObject( &m_boldPrintFont );

               CSize     ext = pDC->GetOutputTextExtent( opText );
               
               textPos = right - ext.cx;

               pDC->TextOut( textPos, rowPos, opText );

               pDC->SelectObject( saveFont );

               ext = pDC->GetOutputTabbedTextExtent( outText, 1, &m_tabstop );

               LineFrom( pDC, rowPos, ext.cx, textPos - 1 );
          }
          else
          {
               BoxText( pDC, outText, 0, rowPos, 
                        right - 1, rowPos + textHeight - 1 
                      );
               m_curSection = outText;
          }
     }
}

void Report::SetTreeView( OptionTree *tv )
{
     CnfAssert( tv != NULL );
     CnfAssert( tv->IsKindOf( RUNTIME_CLASS( OptionTree ) ) );

     m_treeView = tv;
}

void Report::Footer( CDC *pDC )
{
     CRect      clipRect;
     pDC->GetClipBox( &clipRect );
     clipRect.left = clipRect.top = 0;
     clipRect.right = pDC->GetDeviceCaps(HORZRES) - 1;
     clipRect.bottom = pDC->GetDeviceCaps(VERTRES) - 1;

     TEXTMETRIC met;

     pDC->GetOutputTextMetrics( &met );

     int       textTop = clipRect.bottom - met.tmHeight;

     CString   footerText;
     footerText.Format( "Page %u", m_curPage );

     CSize     footerExt = pDC->GetOutputTextExtent( footerText );
     pDC->TextOut( clipRect.right - footerExt.cx,
                   textTop,
                   footerText 
                 );

     pDC->TextOut( 0, textTop, m_reportDate );

     pDC->Rectangle( 0, textTop - 5,
                     clipRect.right - 1, textTop - 4
                   );

     return;
}

int Report::GetFooterHeight( CDC *pDC )
{
     TEXTMETRIC met;

     pDC->GetOutputTextMetrics( &met );

     return( met.tmHeight * 2 );
}

int Report::GetHeaderHeight( CDC *pDC )
{
     TEXTMETRIC met;

     pDC->GetOutputTextMetrics( &met );

     return( met.tmHeight * 3 );
}

void Report::Header( CDC *pDC, const CString &title )
{
     TEXTMETRIC met;

     CFont     *saveFont = pDC->SelectObject( &m_boldPrintFont );

     pDC->GetOutputTextMetrics( &met );

     CString   name;

     if (bbsTitle.GetLength() > 0)
     {
          name = "Configuration Report for " + bbsTitle;
     }
     else
     {
          name = "" SVR_NAME " Configuration Report";
     }

     pDC->TextOut( 0, 0, name );

     int       pageRight = pDC->GetDeviceCaps(HORZRES) - 1;

     if (title.GetLength() > 0)
     {
          CSize     titleExt = pDC->GetOutputTextExtent( title );

          pDC->TextOut( (pageRight - titleExt.cx) + 1, 0,
                        title
                      );
     }

     pDC->SelectObject( saveFont );

     DWORD     backColor = pDC->GetBkColor();

     int       rectLeft = 0;
     int       rectTop = met.tmHeight + 5;
     int       rectBottom = rectTop + 5;
     int       rectRight = pageRight;

     CRect     rec( rectLeft, rectTop, rectRight, rectBottom );

     pDC->FillSolidRect( &rec, RGB( 0, 0, 0 ) );

     pDC->SetBkColor( backColor );

     return;
}

void Report::LineFrom( CDC *pDC, int ypos, int startXpos, int endXpos )
{
     TEXTMETRIC     met;
     pDC->GetOutputTextMetrics( &met );

     int       ave = met.tmAveCharWidth;

     int       first = (startXpos / ave) * ave;
     int       maxPos = endXpos - ave;

     for ( int pos = first; pos <= maxPos; pos += ave )
     {
          if (pos > startXpos)
          {
               pDC->TextOut( pos, ypos, "." );
          }
     }
}


void Report::SetupDate()
{
     CTime t = CTime::GetCurrentTime();

     int       hour = t.GetHour();

     char      ampm = (hour < 12) ? 'a' : 'p';

     if (hour > 12)
     {
          hour -= 12;
     }

     if (hour == 0)
     {
          hour = 12;
     }

     m_reportDate.Format( "%i/%i/%i, %i:%02i%c",
                          t.GetMonth(), t.GetDay(), t.GetYear(),
                          hour, t.GetMinute(), ampm
                        );
}


void Report::SetDoc( CnfDoc *doc )
{
     m_doc = doc;
}

void Report::BoxText( CDC *pDC, const CString &outText, int left, int top, int right, int bottom )
{
     DWORD     oldBg = pDC->GetBkColor();

     pDC->FillSolidRect( CRect( left, top, right, bottom ), 
                         RGB( 192, 192, 192 )
                       );
     pDC->TextOut( left, top, outText );

     pDC->SetBkColor( oldBg );
}

BOOL Report::IsLevel( HTREEITEM item )
{
     BOOL      isLev = FALSE;

     if ((item != NULL) && (level <= 0))
     {
          TreeData  ourData;
          if (m_treeView->GetTreeData( item, ourData ))
          {
               isLev = (ourData.parentKey.GetLength() <= 0); 
          }
     }

     return( isLev );
}

BOOL Report::IsSection( HTREEITEM item )
{
     TreeData  ourData;
     BOOL      isSec = FALSE;

     if (m_treeView->GetTreeData( item, ourData ))
     {
          isSec = (ourData.opPtr == NULL);

          if (isSec && (level <= 0))
          {
               isSec = (ourData.parentKey.GetLength() > 0);
          }
     }

     return( isSec );
}

char Report::GetItemType( HTREEITEM cur )
{
     TreeData  ourData;
     char      type = 0;

     if (m_treeView->GetTreeData( cur, ourData ))
     {
          if (ourData.opPtr != NULL)
          {
               type = ourData.opPtr->type;
          }
     }

     return( type );
}

void Report::ReportToFile( const char *filename )
{
     CString   reportFn;

     if (filename == NULL)
     {
          CFileDialog    dlg( FALSE, ".txt", "wgscfg.txt",
                              OFN_HIDEREADONLY | OFN_NOCHANGEDIR | 
                              OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | 
                              OFN_PATHMUSTEXIST
                            );

          if (dlg.DoModal() != IDOK)
          {
               return;
          }

          reportFn = dlg.GetPathName();
     }
     else
     {
          reportFn = filename;
     }

     FILE     *outf = fopen( reportFn, "w" );

     if (outf == NULL)
     {
          ::MessageBox( NULL, "Unable to open " + reportFn, "" SVR_NAME " Configuration Editor", 
                        MB_OK | MB_ICONSTOP | MB_APPLMODAL
                      );
     }
     else
     {
          CWaitCursor cursor;
          SavingDialog *sd = new SavingDialog;
          sd->m_fname = reportFn;

          sd->Create( IDD_SAVING );

          CString   name;

          if (bbsTitle.GetLength() > 0)
          {
               name = "Configuration Report for " + bbsTitle;
          }
          else
          {
               name = "" SVR_NAME " Configuration Report";
          }

          SetupDate();

          fprintf( outf, "%s     %s\n\n", 
                         (const char *)name,
                         (const char *)m_reportDate
                 );

          int       total = m_treeView->GetCount();
          HTREEITEM cur = m_treeView->GetFirst();
          BOOL      doLevels = (level <= 0);

          CString   itext;
          TreeData  ourData;
          CString   val;

          for ( int sofar = 0; sofar < total; ++sofar )
          {
               itext = m_treeView->GetItemText( cur );

               if (doLevels && IsLevel( cur ))
               {
                    fprintf( outf, "\n%s:\n\n", (const char *)itext );
               }
               else if (IsSection( cur ))
               {
                    if (m_treeView->GetTreeData( cur, ourData ))
                    {
                         itext = ourData.filename + " - " + itext;
                    }

                    fprintf( outf, "%s%s\n",
                                   doLevels ? "\t" : "\n",
                                   (const char *)itext
                           );
               }
               else if (GetItemType( cur ) != 'T')
               {
                    val.Empty();

                    if (m_treeView->GetTreeData( cur, ourData ))
                    {
                         if (ourData.opPtr != NULL)
                         {
                              val = ourData.opPtr->curValue;
                         }
                    }

                    int       sep = itext.Find( ':' );

                    if (sep > 0)
                    {
                         itext = itext.Left( sep );
                    }

                    sep = itext.Find( ' ' );

                    if (sep > 0)
                    {
                         itext = itext.Left( sep );
                    }

                    while (itext.GetLength() < 8)
                    {
                         itext += ' ';
                    }

                    fprintf( outf, "%s%s\t%s\n",  
                                   doLevels ? "\t\t" : "\t",
                                   (const char *)itext,
                                   (const char *)val
                           );
               }

               cur = m_treeView->NextItem( cur );
               CnfAssert( (cur != NULL) || ((sofar + 1) == total) );

               sd->m_progress.SetPos( (sofar * 100) / total );
               sd->UpdateWindow();
          }

          fclose( outf );

          delete sd;
     }
}
