// StrEdit.cpp : implementation file
//

#include "stdafx.h"

#include "cnf.h"
#include "TreeData.h"
#include "WgsCnf.h"
#include "CnfDoc.h"
#include "modifier.h"
#include "OptEdit.h"
#include "CnfDoc.h"
#include "StrEdit.h"
#include "gcommlib.h"
#include "lingo.h"
#include "cstrutil.h"
#include "CnfAsrt.h"
#include "mainfrm.h"

#define FILREV "$Revision: 2 $"

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

/////////////////////////////////////////////////////////////////////////////
// StringEdit

BEGIN_MESSAGE_MAP(StringEdit, CEdit)
     //{{AFX_MSG_MAP(StringEdit)
     ON_CONTROL_REFLECT(EN_CHANGE, OnChange)
     ON_WM_CTLCOLOR_REFLECT()
     //}}AFX_MSG_MAP
END_MESSAGE_MAP()

IMPLEMENT_DYNCREATE(StringEdit, CEdit)

StringEdit::StringEdit()
{
     m_multiLine = FALSE;
     m_opt = NULL;
     m_curLang = -1;
     m_editType = '\0';
     m_modified = FALSE;
     m_languageChecked=FALSE;
}

StringEdit::~StringEdit()
{
     if (m_modified && ::IsWindow( m_hWnd ))
     {
          CnfDoc *theDoc = GetDocument();

          if (theDoc == NULL)
               return;

          if (m_multiLine)
          {
               TreeData ourData;

               BOOL lookRes = theDoc->Lookup( m_count.itemKey, ourData );

               CnfAssert( lookRes );

               for ( int count = 0; count < nlingo; ++count )
               {
                    SetOpText( blockStrings[ count ], count );
               }

               ourData.langBlocks = blockStrings;

               theDoc->Set( m_count.itemKey, ourData );
          }
          else
          {
               CString   newText;

               GetWindowText( newText );

               SetOpText( newText );
          }
     }
}



/////////////////////////////////////////////////////////////////////////////
// StringEdit message handlers

void
StringEdit::SetKey(                // set this option's lookup key
const CString &key)                //   the key
{
     m_count.itemKey = key;
}

void
StringEdit::OnChange()             // handle edit change notification
{
     CString   newText;

     GetWindowText( newText );

     if (! (newText == m_oldVal))  // != gives compiler error!?!
     {
          m_oldVal = newText;
          m_modified = TRUE;

          if (m_multiLine)
          {
               CnfAssert( m_curLang >= 0 );
               CnfAssert( m_curLang < nlingo );

               blockStrings[ m_curLang ] = newText;

               if (!m_languageChecked) {                    
               
                    CnfDoc *theDoc = GetDocument();

                    if (theDoc != NULL) {
                         TreeData ourData;

                         BOOL lookRes = theDoc->Lookup( m_count.itemKey, ourData );
                         for (msgfil* mfp=&mfhead ; mfp != NULL ; mfp=mfp->next) {
                              if (sameas(ourData.filename,mfp->name)) {
                                   if ((m_curLang != 0) && mfp->mslidx[m_curLang] == 0) {
                                        addlang(mfp,m_curLang);
                                   }
                                   break;
                              }
                         }
                         m_languageChecked=TRUE;
                    }
               }
          }

          CnfDoc *theDoc = GetDocument();

          if (AnyChangesFromOriginal())
          {
               theDoc->UpdateAllViews( NULL, ItemModified, &m_count );
          }
          else
          {
               theDoc->UpdateAllViews( NULL, ItemUnModified, &m_count );
          }
     }
}

void
StringEdit::SetWindowText(         // update edit text
LPCTSTR newText)                   //   new edit text
{
     m_undoVal = m_oldVal = ExpandNewLines( newText );

     CEdit::SetWindowText( m_oldVal );
}


BOOL                               //   TRUE if created OK
StringEdit::Create(                // create and setup single or multi edit
DWORD dwStyle,                     //   edit style
const RECT& rect,                  //   control size
CWnd* pParentWnd,                  //   our parent window
UINT nID,                          //   control ID
TreeData *ourData)                 //   info on our edited option
{
     m_multiLine = ((dwStyle & ES_MULTILINE) != 0);

     if (m_multiLine)
     {
          CnfAssert( ourData != NULL );
          CnfAssert( ourData->langBlocks.size() == nlingo );

          blockStrings = ourData->langBlocks;
     }

     CnfAssert( ourData->opPtr != NULL );
     m_opt = ourData->opPtr;

     return CEdit::Create(dwStyle, rect, pParentWnd, nID);
}

void
StringEdit::SelectLanguage(        // select a given language to edit
int nLang)                         //   the language index (0..nlingo - 1)
{
     CnfAssert( m_multiLine );
     CnfAssert( nLang >= 0 );
     CnfAssert( nLang <  nlingo );

     m_curLang = nLang;
     m_oldVal = blockStrings[ m_curLang ];
     SetWindowText( m_oldVal );
     m_languageChecked=FALSE;
}


void
StringEdit::SetOpText(             // update the "real" option text
CString &newText,                  //   new value for option text
int nLang)                         //   if multi edit, the language number
{
     CnfAssert( m_opt != NULL );

     if (m_multiLine)
     {
          CnfAssert( m_opt->values != NULL );
          CnfAssert( nLang >= 0 );
          CnfAssert( nLang < nlingo );

          altlng *langs = m_opt->values;
          altlng &block = langs[ nLang ];

          if (! block.altered)
          {
               block.value.ptr = (char *)alczer( newText.GetLength() + 1 );
          }
          else if (strlen( block.value.ptr ) < newText.GetLength() )
          {
               block.value.ptr = (char *)realloc( block.value.ptr, newText.GetLength() + 1 );
          }

          CnfAssert( block.value.ptr != NULL );

          strcpy( block.value.ptr, newText );

          block.altered = TRUE;
     }
     else
     {
          m_opt->curValue = newText;
     }

     if (m_modified)
     {
          CnfDoc *doc = GetDocument();

          if (AnyChangesFromOriginal())
          {
               doc->UpdateAllViews( NULL, ItemModified, &m_count );
          }
          else
          {
               doc->UpdateAllViews( NULL, ItemUnModified, &m_count );
          }
     }
}

void
StringEdit::ChangeText(            // update window text, note change
LPCTSTR newText)                   //   new edit text
{
     CEdit::SetWindowText( ExpandNewLines( newText ) );
     OnChange();
}


void
StringEdit::GetWindowText(         // get window text, adjusting newlines
CString &text)                     //   current text (returned)
{
     CString   orig;
     
     CEdit::GetWindowText( orig );
     
     text =  CompressNewLines( orig );
}


CString                            //   expanded text
StringEdit::ExpandNewLines(        // convert LF to CRLF in a string
const CString &st)                 //   string to convert
{
     CString   result;

     for ( int count = 0; count < st.GetLength(); ++count )
     {
          switch (st[ count ])
          {
               case '\n':
                    result += "\r\n";
                    break;

               default:
                    result += st[ count ];
                    break;
          }
     }

     return( result );
}

CString                            //   compressed string
StringEdit::CompressNewLines(      // convert CRLF to LF in a string
const CString &st)                 //   string to convert
{
     CString   result;
     int       count = 0;

     while (count < st.GetLength())
     {
          if ((st[ count ] == '\r') && (st[ count + 1 ] == '\n'))
          {
               result += '\n';
               count += 2;
          }
          else
          {
               result += st[ count ];
               ++count;
          }
     }

     return( result );
}

BOOL                               //   TRUE if item is valid
StringEdit::IsValid()              // check item content and bounds
{
     BOOL      valid = TRUE;       // is item valid?
     CString   reason;             // if not, why not?

     CString   theVal;
     GetWindowText( theVal );

     switch (m_editType)
     {
          case 'S':                // length, that's it
               valid = (theVal.GetLength() <= m_floor);

               if (! valid)
               {
                    reason = "Text is too long.";
               }
               break;

          case 'N':                // number, non-empty, in bounds
          case 'L':
               lrTrim( theVal );
               valid = IsNumber( theVal );

               if (valid)
               {
                    LONG      nval = atol( theVal );

                    valid = (nval >= m_floor) && (nval <= m_ceiling);

                    if (! valid)
                    {
                         reason = (nval < m_floor) ? "Value is too low" : "Value is too high";
                    }
               }
               else
               {
                    reason = "Only numbers are allowed here";
               }

               if (valid)
                    SetWindowText( theVal );
               break;

          case 'H':                // hex, non-empty, in bounds
               lrTrim( theVal );
               valid = IsHexNumber( theVal );

               if (valid)
               {
                    LONG      nval = htol( theVal );

                    valid = (nval >= m_floor) && (nval <= m_ceiling);

                    if (! valid)
                    {
                         reason = (nval < m_floor) ? "Value is too low" : "Value is too high";
                    }
               }
               else
               {
                    reason = "Only hex numbers are allowed here";
               }


               if (valid)
               {
                    theVal.MakeUpper();
                    SetWindowText( theVal );
               }
               break;
     }

     if (valid)                    // update op text
     {
          if (m_editType == 'T')
          {
               for ( int count = 0; count < nlingo; ++count )
               {
                    SetOpText( blockStrings[ count ], count );
               }
          }
          else
          {
               SetOpText( theVal );
          }
     }

     if (! valid)                  // say why
     {
          theMainFrame->SetHeaderStatus( reason );
     }

     return( valid );
}

void 
StringEdit::SetEditDetails(        // set editing info
char editType,                     //   type (C,S,T,L,N,H)
LONG floor,                        //   minimum value
LONG ceiling)                      //   maximum value (or length)
{
     m_editType = editType;
     m_floor = floor;
     m_ceiling = ceiling;
}

HBRUSH StringEdit::CtlColor(CDC* pDC, UINT nCtlColor) 
{
     HBRUSH    brush = NULL;

     if (pDC != NULL)
     {
          pDC->SetBkColor( GetSysColor( COLOR_WINDOWTEXT ) );

          brush = GetSysColorBrush( COLOR_WINDOWTEXT );
     }

     return( brush );
}

BOOL                               //   TRUE if message handled
StringEdit::Undo()                 // reset control text to saved value
{
     CEdit::SetWindowText( m_undoVal );
     OnChange();

     return( TRUE );
}


BOOL                               //   TRUE if any values differ from saved
StringEdit::AnyChangesFromOriginal() // check for changes
{
     BOOL      changes = FALSE;

     if (m_multiLine)
     {
          CnfAssert( m_origValues != NULL );

          for ( int count = 0; count < nlingo; ++count )
          {
               if (blockStrings[ count ].Compare( m_origValues[ count ] ) != 0)
               {
                    changes = TRUE;
                    break;
               }
               else
               {
                    blockStrings[ count ] = m_origValues[ count ];
               }
          }
     }
     else
     {
          changes = (m_oldVal.Compare( m_origValue ) != 0);

          if (! changes)
          {
               m_oldVal = m_origValue;
          }
     }

     return( changes );
}

void 
StringEdit::SetOriginalValue(      // set saved edit value
const CString &st)                 //   disk value
{
     CnfAssert( ! m_multiLine );
     m_origValue = st;
}

void 
StringEdit::SetOriginalValues(     // set saved language values
const CString *vals)               //   array of values
{
     CnfAssert( m_multiLine );
     m_origValues = vals;
}

BOOL                               //   TRUE if message handled
StringEdit::PreTranslateMessage(   // trap returns
MSG* pMsg)                         //   windows message info
{
     if ((! m_multiLine) && (pMsg->message == WM_KEYDOWN))
     {
          if (pMsg->wParam == VK_RETURN)
          {    
               CnfDoc *doc = GetDocument();

               if (doc != NULL)
               {
                    doc->UpdateAllViews( NULL, NextVisible );
                    return( TRUE );
               }
          }
     }
     
     return CEdit::PreTranslateMessage(pMsg);
}

CnfDoc *                           //   pointer to current document
StringEdit::GetDocument()          // get document pointer
{
     CnfAssert( ::IsWindow( GetSafeHwnd() ) );

     OptionEditView *theView = static_cast< OptionEditView * >( CWnd::GetParent() );
     CnfAssert( theView != NULL );

     CnfDoc *theDoc = static_cast< CnfDoc * >( theView->GetDocument() );
     CnfAssert( theDoc != NULL );

     return( theDoc );
}

BOOL StringEdit::CanUndo() const
{
     BOOL      changes = FALSE;

     changes = (m_oldVal.Compare( m_undoVal ) != 0);

     return( changes );
}
