// DlgObjectProperties.cpp : implementation file
//

#include "stdafx.h"
#include "mw4gameed.h"
#include <stdarg.h>
#include <stuff\notationfile.hpp>
#include <stuff\vector3d.hpp>
#include "DlgObjectProperties.h"


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

const int c_Max_Objects_Supported = 100;
extern CMW4GameEdApp theApp;

//
// User passes in an array of data to the dialog at startup.
// this array type is...CDOPPROPERTIES_TO_EDIT_ARRAY
//

                   

LPCSTR MyLoadStringTempBuffer(UINT uiID);
//
// This function needs to be moved somewhere more generic
//
//
LPCSTR MyLoadStringTempBuffer(UINT uiID)
{
  static TCHAR strings[10][1024];
  static nNext = 0;
  nNext = (nNext + 1) % 10;
  ::LoadString(NULL, uiID, strings[nNext], 1023);
  return strings[nNext];

}



// puts a backslash as the last char unless it's an empty string, ends in a slash already, or ends with a ':'
void EndDirectoryWithBackSlash(CString & strDir)
{
  int nLen = strDir.GetLength();
  if (!nLen)
    return;
  if (strDir.GetAt(nLen-1) == '\\' || strDir.GetAt(nLen -1) == ':')
    return;
  strDir += '\\';
}

/////////////////////////////////////////////////////////////////////////////
// CDlgObjectProperties dialog


//
// Rather than have the calling function have to fill in the structure manually they can call this 
// function with all the propeties they want to edit. Then they just pass this in to the appropriate
// function to specify what properties to get. They will have to clear up the memory with free though
//
CDOPPROPERTIES_TO_EDIT_ARRAY * CDlgObjectProperties::CreatePropertiesToEditArray(int nCount, ...)
{

   va_list marker;
   CDOPPROPERTIES_TO_EDIT_ARRAY * pRet = Allocate_CDOPPROPERTIES_TO_EDIT_ARRAY(nCount);


   va_start( marker, nCount );     /* Initialize variable arguments. */
   pRet->nCount = nCount;
   for (int i = 0 ; i < nCount; i++)
   {
      pRet->ppProperties_To_Edit_Entry[i]->ePropertyType = va_arg(marker, CDOPVARTYPE);
      strcpy(pRet->ppProperties_To_Edit_Entry[i]->szPropertyTag, va_arg(marker, char *));
      strcpy(pRet->ppProperties_To_Edit_Entry[i]->szPropertyTagForDisplay, va_arg(marker, char *));
      pRet->ppProperties_To_Edit_Entry[i]->uiReserved1_CTRLID = 0;
   }
   va_end( marker );              /* Reset variable arguments.      */
   return pRet; 
}
//
// Since I am using a pointer to represent an array the structure has to be allocated with enough size to
// store the array. In addition the internal pointer variable must be directed to within
// the allocation itself. This way I can use array notation To then on.
//
CDOPPROPERTIES_TO_EDIT_ARRAY * CDlgObjectProperties::Allocate_CDOPPROPERTIES_TO_EDIT_ARRAY(int nPropertyCount, CDOPPROPERTIES_TO_EDIT_ARRAY * * ppCDOPPROPERTIES_TO_EDIT_ARRAY)
{
  CDOPPROPERTIES_TO_EDIT_ARRAY * pNew = (CDOPPROPERTIES_TO_EDIT_ARRAY *)
    malloc(sizeof(CDOPPROPERTIES_TO_EDIT_ARRAY) + (nPropertyCount) * sizeof(CDOPPROPERTIES_TO_EDIT_ENTRY *) + 
    (nPropertyCount) * sizeof(CDOPPROPERTIES_TO_EDIT_ENTRY)); // we could - 1 from the count since one is allocated as part of the structure, but
                                                                                                      // I'm electing to have the 4 byte buffer instead of worrying about nPropertyCount == 0

  for (int i = 0; i < nPropertyCount; i++)
  {
    pNew->ppProperties_To_Edit_Entry[i] = (CDOPPROPERTIES_TO_EDIT_ENTRY *)(void *)(((DWORD)&(pNew->ppProperties_To_Edit_Entry)) + 
          sizeof(CDOPPROPERTIES_TO_EDIT_ENTRY *) * (nPropertyCount) + i * sizeof(CDOPPROPERTIES_TO_EDIT_ENTRY));
  }
  if (ppCDOPPROPERTIES_TO_EDIT_ARRAY)
    *ppCDOPPROPERTIES_TO_EDIT_ARRAY = pNew;
  return pNew;
}

//
// Since I am using a pointer to represent an array the structure has to be allocated with enough size to
// store the array. In addition the internal pointer variable must be directed to within
// the allocation itself. This way I can use array notation To then on.
//
CDOPPROPERTIES_OBJECT_PROPERTIES * CDlgObjectProperties::Allocate_CDOPPROPERTIES_OBJECT_PROPERTIES(int nPropertyCount, CDOPPROPERTIES_OBJECT_PROPERTIES * * ppCDOPPROPERTIES_OBJECT_PROPERTIES)
{
   CDOPPROPERTIES_OBJECT_PROPERTIES * pNew = (CDOPPROPERTIES_OBJECT_PROPERTIES *)
     malloc (sizeof(CDOPPROPERTIES_OBJECT_PROPERTIES) + nPropertyCount * sizeof(CDOPPROPERTYVALUE *) +  
          nPropertyCount * sizeof(CDOPPROPERTYVALUE));
   for (int i = 0 ; i < nPropertyCount; i++)
   {
     pNew->ppObjectProperties[i] = (CDOPPROPERTYVALUE * )(void *)(((DWORD)&(pNew->ppObjectProperties)) + 
       sizeof(CDOPPROPERTYVALUE *) * nPropertyCount + sizeof(CDOPPROPERTYVALUE) * i);
   }
   if (ppCDOPPROPERTIES_OBJECT_PROPERTIES)
     *ppCDOPPROPERTIES_OBJECT_PROPERTIES = pNew;
   return pNew;
}

//
// Since I am using a pointer to represent an array the structure has to be allocated with enough size to
// store the array. In addition the internal pointer variable must be directed to within
// the allocation itself. This way I can use array notation To then on.
//
CDOPPROPERTIES_OBJECT_LIST * CDlgObjectProperties::Allocate_CDOPPROPERTIES_OBJECT_LIST(int nObjectCount, int nPropertyCount, CDOPPROPERTIES_OBJECT_LIST ** ppCDOPPROPERTIES_OBJECT_LIST )
{

  // This one is the most complex since it has to allocate enough room for all the objects, to have all the properties and have
  // all the data for those properties

   CDOPPROPERTIES_OBJECT_LIST * pNew = (CDOPPROPERTIES_OBJECT_LIST *)
     malloc(sizeof(CDOPPROPERTIES_OBJECT_LIST) + nObjectCount * sizeof(CDOPPROPERTIES_OBJECT_PROPERTIES *)
        +(nObjectCount * (sizeof(CDOPPROPERTIES_OBJECT_PROPERTIES) + nPropertyCount * sizeof(CDOPPROPERTYVALUE *)))
        +(nObjectCount * (nPropertyCount * sizeof(CDOPPROPERTYVALUE)))
          );             
   for (int i = 0; i < nObjectCount; i++)
   {
     // For the object array set up the pointers to each properties array
     pNew->ppPropertiesArray[i] = (CDOPPROPERTIES_OBJECT_PROPERTIES * )(void *)(((DWORD)&(pNew->ppPropertiesArray))  
           +sizeof(CDOPPROPERTIES_OBJECT_PROPERTIES * ) * nObjectCount 
           + i * (sizeof(CDOPPROPERTIES_OBJECT_PROPERTIES) + nPropertyCount * (sizeof(CDOPPROPERTYVALUE  *))  
                 +nPropertyCount * sizeof(CDOPPROPERTYVALUE)));
     // For each object set up the pointer list for the properties array
     for (int j = 0; j < nPropertyCount; j++)
     {
       pNew->ppPropertiesArray[i]->ppObjectProperties[j] = (CDOPPROPERTYVALUE  *)(void *)(((DWORD)&(pNew->ppPropertiesArray[i]->ppObjectProperties))  
            + nPropertyCount * sizeof(CDOPPROPERTYVALUE  *) 
            + j * sizeof(CDOPPROPERTYVALUE));
     }
   }

   if (ppCDOPPROPERTIES_OBJECT_LIST)
     *ppCDOPPROPERTIES_OBJECT_LIST = pNew;
   return pNew;

}



//****************************************************************************************
//
//
//
//****************************************************************************************
BOOL CDlgObjectProperties::Copy_CDOPPROPERTIES_TO_EDIT_ARRAY(CDOPPROPERTIES_TO_EDIT_ARRAY * pCDOPPROPERTIES_TO_EDIT_ARRAY_DEST, CDOPPROPERTIES_TO_EDIT_ARRAY * pCDOPPROPERTIES_TO_EDIT_ARRAY_SRC)
{
  pCDOPPROPERTIES_TO_EDIT_ARRAY_DEST->nCount = pCDOPPROPERTIES_TO_EDIT_ARRAY_SRC->nCount;
  for (int i = 0; i < pCDOPPROPERTIES_TO_EDIT_ARRAY_SRC->nCount; i++)
  {
    memcpy(pCDOPPROPERTIES_TO_EDIT_ARRAY_DEST->ppProperties_To_Edit_Entry[i], 
           pCDOPPROPERTIES_TO_EDIT_ARRAY_SRC->ppProperties_To_Edit_Entry[i], 
           sizeof(CDOPPROPERTIES_TO_EDIT_ENTRY));

  }
  return TRUE;

}

//****************************************************************************************
//
//
//
//****************************************************************************************
BOOL CDlgObjectProperties::Copy_CDOPPROPERTIES_OBJECT_PROPERTIES(CDOPPROPERTIES_OBJECT_PROPERTIES * pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST, CDOPPROPERTIES_OBJECT_PROPERTIES * pCDOPPROPERTIES_OBJECT_PROPERTIES_SRC)
{
   pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->fDirty = pCDOPPROPERTIES_OBJECT_PROPERTIES_SRC->fDirty;
   pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->fReadOnly = pCDOPPROPERTIES_OBJECT_PROPERTIES_SRC->fReadOnly;
   pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->nProperties = pCDOPPROPERTIES_OBJECT_PROPERTIES_SRC->nProperties;
   memcpy(pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->szObjectName, 
          pCDOPPROPERTIES_OBJECT_PROPERTIES_SRC->szObjectName, 
          sizeof(pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->szObjectName));

   for (int i = 0 ; i < pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->nProperties; i++)
   {
     memcpy(pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->ppObjectProperties[i], 
            pCDOPPROPERTIES_OBJECT_PROPERTIES_SRC->ppObjectProperties[i], 
            sizeof(CDOPPROPERTYVALUE));
   }
   return TRUE;
}

//****************************************************************************************
//
//
//
//****************************************************************************************
BOOL CDlgObjectProperties::Copy_CDOPPROPERTIES_OBJECT_LIST(CDOPPROPERTIES_OBJECT_LIST * pCDOPPROPERTIES_OBJECT_LIST_DEST, CDOPPROPERTIES_OBJECT_LIST * pCDOPPROPERTIES_OBJECT_LIST_SRC)
{
   pCDOPPROPERTIES_OBJECT_LIST_DEST->nObjectCount = pCDOPPROPERTIES_OBJECT_LIST_SRC->nObjectCount;
   for (int i = 0; i < pCDOPPROPERTIES_OBJECT_LIST_DEST->nObjectCount; i++)
   {
     Copy_CDOPPROPERTIES_OBJECT_PROPERTIES(pCDOPPROPERTIES_OBJECT_LIST_DEST->ppPropertiesArray[i], 
                                           pCDOPPROPERTIES_OBJECT_LIST_SRC->ppPropertiesArray[i]);
   }
   return TRUE;
}

//****************************************************************************************
//
//
//
//****************************************************************************************
BOOL CDlgObjectProperties::IsEqual_CDOPPROPERTIES_TO_EDIT_ARRAY(CDOPPROPERTIES_TO_EDIT_ARRAY * pCDOPPROPERTIES_TO_EDIT_ARRAY1, CDOPPROPERTIES_TO_EDIT_ARRAY * pCDOPPROPERTIES_TO_EDIT_ARRAY2)
{
  if (pCDOPPROPERTIES_TO_EDIT_ARRAY1->nCount != pCDOPPROPERTIES_TO_EDIT_ARRAY2->nCount)
    return FALSE;

  for (int i = 0; i < pCDOPPROPERTIES_TO_EDIT_ARRAY1->nCount; i++)
  {
    if (memcmp(pCDOPPROPERTIES_TO_EDIT_ARRAY1->ppProperties_To_Edit_Entry[i], 
           pCDOPPROPERTIES_TO_EDIT_ARRAY2->ppProperties_To_Edit_Entry[i], 
           sizeof(CDOPPROPERTIES_TO_EDIT_ENTRY)))
           return FALSE;

  }
  return TRUE;

}

//****************************************************************************************
//
//
//
//****************************************************************************************
BOOL CDlgObjectProperties::IsEqual_CDOPPROPERTIES_OBJECT_PROPERTIES(CDOPPROPERTIES_OBJECT_PROPERTIES * pCDOPPROPERTIES_OBJECT_PROPERTIES1, CDOPPROPERTIES_OBJECT_PROPERTIES * pCDOPPROPERTIES_OBJECT_PROPERTIES2)
{
  if (pCDOPPROPERTIES_OBJECT_PROPERTIES1->nProperties != pCDOPPROPERTIES_OBJECT_PROPERTIES2->nProperties ||
      pCDOPPROPERTIES_OBJECT_PROPERTIES1->fDirty != pCDOPPROPERTIES_OBJECT_PROPERTIES2->fDirty  ||
      pCDOPPROPERTIES_OBJECT_PROPERTIES1->fReadOnly != pCDOPPROPERTIES_OBJECT_PROPERTIES2->fReadOnly ||
      memcmp(pCDOPPROPERTIES_OBJECT_PROPERTIES1->szObjectName, 
          pCDOPPROPERTIES_OBJECT_PROPERTIES2->szObjectName, 
          sizeof(pCDOPPROPERTIES_OBJECT_PROPERTIES2->szObjectName)))
    return FALSE;


  for (int i = 0 ; i < pCDOPPROPERTIES_OBJECT_PROPERTIES2->nProperties; i++)
  {
     if (memcmp(pCDOPPROPERTIES_OBJECT_PROPERTIES1->ppObjectProperties[i], 
            pCDOPPROPERTIES_OBJECT_PROPERTIES2->ppObjectProperties[i], 
            sizeof(CDOPPROPERTYVALUE)))
     {
       return FALSE;
     }

  }
  return TRUE;
}

//****************************************************************************************
//
//
//
//****************************************************************************************
BOOL CDlgObjectProperties::IsEqual_CDOPPROPERTIES_OBJECT_LIST(CDOPPROPERTIES_OBJECT_LIST * pCDOPPROPERTIES_OBJECT_LIST1, CDOPPROPERTIES_OBJECT_LIST * pCDOPPROPERTIES_OBJECT_LIST2)
{
   if (pCDOPPROPERTIES_OBJECT_LIST1->nObjectCount != pCDOPPROPERTIES_OBJECT_LIST2->nObjectCount)
    return FALSE;

  for (int i = 0 ; i < pCDOPPROPERTIES_OBJECT_LIST2->nObjectCount; i++)
  {
    if (!IsEqual_CDOPPROPERTIES_OBJECT_PROPERTIES(pCDOPPROPERTIES_OBJECT_LIST1->ppPropertiesArray[i], 
          pCDOPPROPERTIES_OBJECT_LIST2->ppPropertiesArray[i])) 
          return FALSE;
  }
  return TRUE;
}




//****************************************************************************************
//
//
//
//****************************************************************************************
CDlgObjectProperties::~CDlgObjectProperties()
{
  if (m_pObjectList)
  {
    free(m_pObjectList);
  }
}


//****************************************************************************************
//
//
//
//****************************************************************************************
CDlgObjectProperties::CDlgObjectProperties(CWnd* pParent /*=NULL*/)
	: CDialog(CDlgObjectProperties::IDD, pParent)
{
	//{{AFX_DATA_INIT(CDlgObjectProperties)
	//}}AFX_DATA_INIT

  m_pObjectList = NULL;
  ClearObjectDataSet();
  m_nCurSel = LB_ERR; // nothing selected, important to start here since we have no data loaded in the dialog yet

  // This will be passed in later with a list of properties to edit
  m_paPropertiesToEdit = NULL;

  m_fnFilterFunction = NULL;
  m_fShowFullObjectName = FALSE;
  m_fShowFullPropertyName = FALSE;
  m_eSearchType = eSingleSubdirectory;
  m_popCurObjectPropertiesArray = NULL;
  m_fDirty = FALSE;
  
  m_nLastControlID = IDC_DOP_PROPNAME;
}

//****************************************************************************************
//
//
//
//****************************************************************************************
int CDlgObjectProperties::DoModal()
{
  // 
  // Check to make sure we have all the data set that we need
  //
  if (
      m_strFileNameToSearchSubdirectoriesFor.IsEmpty() ||
      m_strObjectFilesRootDirectory.IsEmpty() ||
      m_paPropertiesToEdit == NULL ||
      !m_paPropertiesToEdit->nCount // have to have at least one property
      )
  {
    return IDABORT;
  }
  return CDialog::DoModal();

}

//****************************************************************************************
//
//
//
//****************************************************************************************
void CDlgObjectProperties::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CDlgObjectProperties)
	DDX_Control(pDX, IDC_OBJECTLIST, m_lbObjectList);
	//}}AFX_DATA_MAP
}

//****************************************************************************************
//
//
//
//****************************************************************************************

BEGIN_MESSAGE_MAP(CDlgObjectProperties, CDialog)
	//{{AFX_MSG_MAP(CDlgObjectProperties)
	ON_LBN_SELCHANGE(IDC_OBJECTLIST, OnSelchangeObjectlist)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDlgObjectProperties message handlers

BOOL CDlgObjectProperties::OnInitDialog() 
{
	CDialog::OnInitDialog();

  m_nCurSel = LB_ERR;
  m_fDirty = FALSE;
  ClearObjectDataSet();

  //
  // For now we just have a fixed size data set maximum. Otherwise we would have to 
  // iterate all the files once or create a link list of objects instead of an array.
  // Any of these can be done, the array was simply created to save time getting this 
  // code up to speed.
  //
  if (m_pObjectList)
    free(m_pObjectList);
  m_pObjectList = Allocate_CDOPPROPERTIES_OBJECT_LIST(c_Max_Objects_Supported, m_paPropertiesToEdit->nCount);
  m_pObjectList->nObjectCount = 0; // this many being used currently
  
  //
  // Search for the weapons subsytem files
  //
  LoadObjectData(m_strObjectFilesRootDirectory, m_eSearchType);


  //
  // Create all the sub-controls that represent the data that we are going to be editting in the
  // dialog
  // Note: We are not allowed to get here if we don't have a properties list since DoModal should kick
  //       us out, and this dialog is not meant to be modeless.
  CreateSubControlsForProperties();


  UpdateData(FALSE);
  m_lbObjectList.SetCurSel(0); // get the ball rolling by showing the first weapon in the list
  OnSelchangeObjectlist();

	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectProperties::CreateSubControlsForProperties()
{
  // 
  // Go through the list of properties we are editting and place all the controls.
  // Use the reserved value in the property array passed in to store the starting ID
  // of each properties control set. Offsets To there are based on the type of variable
  // that is being used.
  m_nLastControlID = IDC_DOP_PROPNAME;
  
  //
  // The following hidden objects have been added to the resource dialog in order to 
  // make formatting fully adjustable without having to modify constants in the code
  // We get the base size, and position for all controls To these
  // IDC_DOP_PROPNAME, IDC_DOP_PROPPRESENT, IDC_DOP_PROPEDIT1, IDC_DOP_PROPEDIT2, IDC_DOP_PROPEDIT3 
  //
  // Use the dummy hidden controls to place our new ones, unhide the dummy controls
  // for the first property, and then build the rest
  CWnd * pControlPropName     = GetDlgItem(IDC_DOP_PROPNAME);
  CWnd * pControlPropPresent  = GetDlgItem(IDC_DOP_PROPPRESENT);
  CWnd * pControlPropEdit1    = GetDlgItem(IDC_DOP_PROPEDIT1);
  CWnd * pControlPropEdit2    = GetDlgItem(IDC_DOP_PROPEDIT2);
  CWnd * pControlPropEdit3    = GetDlgItem(IDC_DOP_PROPEDIT3);
  if (!pControlPropName || !pControlPropPresent || !pControlPropEdit1 || !pControlPropEdit2 || !pControlPropEdit3)
    return; // badness has occurred, but no way out of it at this point

  CFont * pFontControlPropName     = pControlPropName->GetFont();
  CFont * pFontControlPropPresent  = pControlPropPresent->GetFont();
  CFont * pFontControlPropEdit1    = pControlPropEdit1->GetFont();
  CFont * pFontControlPropEdit2    = pControlPropEdit2->GetFont();
  CFont * pFontControlPropEdit3    = pControlPropEdit3->GetFont();



  CRect crRectDlg;
  CRect crRectPropName;
  CRect crRectPropPresent;
  CRect crRectPropEdit1;
  CRect crRectPropEdit2;
  CRect crRectPropEdit3;

  GetWindowRect(crRectDlg);
  pControlPropName->GetWindowRect(crRectPropName);
  pControlPropPresent->GetWindowRect(crRectPropPresent);
  pControlPropEdit1->GetWindowRect(crRectPropEdit1);
  pControlPropEdit2->GetWindowRect(crRectPropEdit2);
  pControlPropEdit3->GetWindowRect(crRectPropEdit3);

  //
  // Offset the rects relative to the dialog
  //
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crRectPropName, 2);
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crRectPropPresent, 2);
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crRectPropEdit1, 2);
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crRectPropEdit2, 2);
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crRectPropEdit3, 2);

//  crRectPropName.OffsetRect(-crRectDlg.left, -crRectDlg.top);
//  crRectPropPresent.OffsetRect(-crRectDlg.left, -crRectDlg.top);
//  crRectPropEdit1.OffsetRect(-crRectDlg.left, -crRectDlg.top);
//  crRectPropEdit2.OffsetRect(-crRectDlg.left, -crRectDlg.top);
//  crRectPropEdit3.OffsetRect(-crRectDlg.left, -crRectDlg.top);

  // for the first property we just unhide the appropriate boxes and set the property name
  int nCurrentProperty = 0;
  // if we didn't have at least one item in the property list DoModal() should have kicked us out
  // so it should be safe to reference this without checking here.
  pControlPropName->ShowWindow(SW_SHOW);

  const char * szStaticText = NULL;
  if (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[0]->szPropertyTagForDisplay[0])
  {
    szStaticText = m_paPropertiesToEdit->ppProperties_To_Edit_Entry[0]->szPropertyTagForDisplay;
  }
  else
  {
    szStaticText = 
      m_fShowFullPropertyName ? 
        m_paPropertiesToEdit->ppProperties_To_Edit_Entry[0]->szPropertyTag :
        GetEntryNameToPropertyName(m_paPropertiesToEdit->ppProperties_To_Edit_Entry[0]->szPropertyTag );
  }
  
  
  pControlPropName->SetWindowText(szStaticText);
  pControlPropPresent->ShowWindow(SW_SHOW);
  pControlPropEdit1->ShowWindow(SW_SHOW);

  // Next root ID is 1 after this set
  int nCurrID = IDC_DOP_PROPNAME + 3;

  // now see if we need to add the others
  if (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[0]->ePropertyType == eVector3D)
  {
    pControlPropEdit2->ShowWindow(SW_SHOW);
    pControlPropEdit3->ShowWindow(SW_SHOW);
  }
  else
  {
    pControlPropEdit2->ShowWindow(SW_HIDE);
    pControlPropEdit3->ShowWindow(SW_HIDE);
  }
  
  nCurrID += 2; // this has to be incremented no matter what since the controls are actually there.

  m_paPropertiesToEdit->ppProperties_To_Edit_Entry[0]->uiReserved1_CTRLID = IDC_DOP_PROPNAME;

  int nDiffY = crRectPropEdit1.Height() + 5; // constant used for vertical padding (use largest control type, edit box)
  HWND hWndTemp = NULL;
  while (++nCurrentProperty < m_paPropertiesToEdit->nCount)
  {
    CWnd wndControlPropName     ;
    CWnd wndControlPropPresent  ;
    CWnd wndControlPropEdit1    ;
    CWnd wndControlPropEdit2    ;
    CWnd wndControlPropEdit3    ;
//    BOOL fIsNumberOnly = m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->ePropertyType != eLPSTR;

    // we have to store off the ID for these properties
    m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->uiReserved1_CTRLID = nCurrID;

    crRectPropName.OffsetRect(   0, nDiffY);    
    crRectPropPresent.OffsetRect(0, nDiffY); 
    crRectPropEdit1.OffsetRect(  0, nDiffY);   
    crRectPropEdit2.OffsetRect(  0, nDiffY);   
    crRectPropEdit3.OffsetRect(  0, nDiffY);   
    
    if (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->szPropertyTagForDisplay[0])
    {
      szStaticText = m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->szPropertyTagForDisplay;
    }
    else
    {
      szStaticText = 
        m_fShowFullPropertyName ? 
          m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->szPropertyTag :
          GetEntryNameToPropertyName(m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->szPropertyTag );
    }
      hWndTemp = ::CreateWindow("STATIC", 
          szStaticText, 
          WS_VISIBLE | SS_LEFT | WS_CHILD, 
          crRectPropName.left, crRectPropName.top, crRectPropName.Width(), crRectPropName.Height(), 
          GetSafeHwnd(), (HMENU)nCurrID, theApp.m_hInstance, NULL); 
//      ::SetWindowLong(hWndTemp, nCurrID, GWL_ID);

      ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlPropName, (LPARAM)FALSE);


/*          wndControlPropName.Create("STATIC", 
      szStaticText, 
      WS_VISIBLE | SS_LEFT | WS_CHILD, 
      crRectPropName, 
      this, 
      nCurrID, 
      NULL);
    wndControlPropName.Detach();
      */
    nCurrID++;


    hWndTemp = ::CreateWindow("BUTTON", 
        "", 
        WS_VISIBLE | BS_CHECKBOX | WS_CHILD | BS_AUTOCHECKBOX, 
        crRectPropPresent.left, crRectPropPresent.top, crRectPropPresent.Width(), crRectPropPresent.Height(), 
        GetSafeHwnd(), (HMENU)nCurrID, theApp.m_hInstance, NULL); 
//    ::SetWindowLong(hWndTemp, nCurrID, GWL_ID);
    ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlPropPresent, (LPARAM)FALSE);
    
    /*    wndControlPropPresent.Create("BUTTON", 
      _T(""), 
      WS_VISIBLE | BS_CHECKBOX | WS_CHILD, 
      crRectPropPresent, 
      this, 
      nCurrID, 
      NULL);
    wndControlPropPresent.Detach();
      */
    nCurrID++;
    hWndTemp = ::CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_NOPARENTNOTIFY, "EDIT", 
        "", 
        WS_VISIBLE | WS_BORDER | ES_LEFT | WS_CHILD ,
        crRectPropEdit1.left, crRectPropEdit1.top, crRectPropEdit1.Width(), crRectPropEdit1.Height(), 
        GetSafeHwnd(), (HMENU)nCurrID, theApp.m_hInstance, NULL); 
//    ::SetWindowLong(hWndTemp, nCurrID, GWL_ID);
    ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlPropEdit1, (LPARAM)FALSE);
/*    wndControlPropEdit1.Create("EDIT", 
      _T(""), 
      WS_VISIBLE | ES_LEFT | WS_CHILD |
      (fIsNumberOnly ? ES_NUMBER : 0), // keep them To entering wrong info
      crRectPropEdit1, 
      this, 
      nCurrID, 
      NULL);
    wndControlPropEdit1.Detach();
      */
    nCurrID++;

    if (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->ePropertyType == eVector3D)
    {

      hWndTemp = ::CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_NOPARENTNOTIFY, "EDIT", 
          "", 
          WS_VISIBLE | WS_BORDER | ES_LEFT | WS_CHILD,
          crRectPropEdit2.left, crRectPropEdit2.top, crRectPropEdit2.Width(), crRectPropEdit2.Height(), 
          GetSafeHwnd(), (HMENU)nCurrID, theApp.m_hInstance, NULL); 
//      ::SetWindowLong(hWndTemp, nCurrID, GWL_ID);
      ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlPropEdit2, (LPARAM)FALSE);
/*      wndControlPropEdit2.Create("EDIT", 
        _T(""), 
        WS_VISIBLE | ES_LEFT | WS_CHILD |
        ES_NUMBER, // keep them To entering wrong info
        crRectPropEdit2, 
        this, 
        nCurrID, 
        NULL);
      wndControlPropEdit2.Detach();
  */
      nCurrID++;

      hWndTemp = ::CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_NOPARENTNOTIFY, "EDIT", 
          "", 
          WS_VISIBLE | WS_BORDER | ES_LEFT | WS_CHILD,
          crRectPropEdit3.left, crRectPropEdit3.top, crRectPropEdit3.Width(), crRectPropEdit3.Height(), 
          GetSafeHwnd(), (HMENU)nCurrID, theApp.m_hInstance, NULL); 
//      ::SetWindowLong(hWndTemp, nCurrID, GWL_ID);
      ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlPropEdit3, (LPARAM)FALSE);
/*      wndControlPropEdit3.Create("EDIT", 
        _T(""), 
        WS_VISIBLE | ES_LEFT | WS_CHILD |
        ES_NUMBER, // keep them To entering wrong info
        crRectPropEdit3, 
        this, 
        nCurrID, 
        NULL);
      wndControlPropEdit3.Detach();
  */
      nCurrID++;

    } // if we are entering a eVector3D property here
  } // while we are iterating through the properties
  m_nLastControlID = nCurrID;
} // end of CreateSubControlsForProperties()


//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectProperties::OnOK()
{
  // 
  // Validate the data and save any changes made since the last sel change
  //
  MoveCurSelDataToOrFromControls(FALSE); 
  //
  // Ask them if they want to commit the changes? Or should pressing OK be enough? Should there be a
  // safety net?
  //
  if (m_fDirty)
  {
    int ret = MessageBox( MyLoadStringTempBuffer(IDS_SAVE_WEAPONS_CHANGES), MyLoadStringTempBuffer(IDS_SAVE_WEAPONS_CHANGES_CAPTION), MB_YESNO);
    if (ret != IDYES)
    {
      return;
    }
    //
    // save the changes, exit if failed without ending the dialog
    //
    if (!SaveObjectData())
    {
      MessageBox(MyLoadStringTempBuffer(IDS_SAVE_WEAPONS_FAILED), MyLoadStringTempBuffer(IDS_ERROR_CAPTION), MB_OK | MB_ICONEXCLAMATION);
    }
  }

  // 
  // If we got here, everything should be saved
  //
  CDialog::OnOK();
}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectProperties::OnCancel()
{
  // 
  // It's safe to exit, as long as all the notation files are closed out, which should be the case 
  //
  CDialog::OnCancel();
}


//****************************************************************************************
// 
// Sets the root directory to search under. Only goes one level deep currently
//
//****************************************************************************************
BOOL CDlgObjectProperties::SetObjectFilesDirectory(LPCSTR lpszDirectoryPath)
{
  m_strObjectFilesRootDirectory = lpszDirectoryPath;
  EndDirectoryWithBackSlash(m_strObjectFilesRootDirectory );
  return TRUE;
}
//****************************************************************************************
//
// Sets the root file name type to search for like "*.data"
//
//****************************************************************************************
BOOL CDlgObjectProperties::SetFileNameToSearchSubdirectoriesFor(LPCSTR lpszFileSearchString)
{
  m_strFileNameToSearchSubdirectoriesFor = lpszFileSearchString;
  return TRUE;
}


//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectProperties::ClearObjectDataSet()
{
  //
  // If you didn't allocate this structure as a whole you have to walk the tree and kill the internal
  // pointer memory before you do this, but if you used the allocate_... function provided above it was
  // all allocated as one huge chunk instead of in parts.
  //
  if (m_pObjectList)
  {
    free(m_pObjectList);
    m_pObjectList = NULL;
  }

}

//****************************************************************************************
//
//
//****************************************************************************************
LPCSTR CDlgObjectProperties::GetPageNameToPropertyName(LPCSTR szPropertyName)
{
  static char s_szPageNameBuffer[128];
  // get everything up to the first \ in the string
  const char * pTempSrc = szPropertyName;
  char * pTempDest = s_szPageNameBuffer;

  while (*pTempSrc && *pTempSrc != '\\') *(pTempDest++) = *(pTempSrc++);
  *pTempDest = '\0';
  return s_szPageNameBuffer;
}

//****************************************************************************************
//
//
//****************************************************************************************
LPCSTR CDlgObjectProperties::GetEntryNameToPropertyName(LPCSTR szPropertyName)
{
  static char s_szEntryNameBuffer[128];
  // get everything after the last \ in the string
  const char * pTempSrc = szPropertyName;
  const char * pLastBackSlash = szPropertyName;
  while (*pTempSrc)
  {
    if (*pTempSrc == '\\')
      pLastBackSlash = pTempSrc;
    pTempSrc++;
  }
  if (*pLastBackSlash == '\\')
    pLastBackSlash++;

  strcpy(s_szEntryNameBuffer, pLastBackSlash);
  return s_szEntryNameBuffer;
}


//****************************************************************************************
//
// Save weapons data will save all the information back to the appropriate notation files. It only saves
// out the weapons that are changed.
// 
//****************************************************************************************
BOOL CDlgObjectProperties::SaveObjectData()
{
  int nFailures = 0;

  for (int i = 0; i< m_pObjectList->nObjectCount;i++)
  {
    if (!m_pObjectList->ppPropertiesArray[i]->fDirty) // not an error condition, just a shortcut
      continue; // don't worry about saving this one if it hasn't changed.
    if (!m_pObjectList->ppPropertiesArray[i]->szObjectName[0])
    {
      nFailures++;
      continue;
    }
    if (m_pObjectList->ppPropertiesArray[i]->fReadOnly) // shouldn't have been allowed to change this
    {
      nFailures++;
      continue;
    }
    //
    // Loop through all the properties and save them
    //
    Page * page = NULL;
    char szCurrentPageName[128] = ""; // used to prevent repetitively searching for the same page
    NotationFile outfile((LPCSTR)m_pObjectList->ppPropertiesArray[i]->szObjectName, NotationFile::Raw);
    for (int j=0; j < m_pObjectList->ppPropertiesArray[i]->nProperties; j++)
    {
      const char * szPageName = GetPageNameToPropertyName(m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->szPropertyTag);
      if (!page || strcmp(szCurrentPageName, szPageName))
      {
        strcpy(szCurrentPageName, szPageName);
        page = outfile.FindPage(szPageName);
        if (!page) // if for some reason we can't find the page, jump out
          continue;
      }
      const char * szEntryName = GetEntryNameToPropertyName(m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->szPropertyTag);
      //
      // For each item in the data list, if it shouldn't be there, delete it. If it supposed to be there set it
      // Assumption is made that there are not two of any particular value key
      //
	    if (!m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->fPresent)
      {
        page->DeleteNote(szEntryName);
      }
      else
      {
        page->DeleteNote(szEntryName); // temporary kludge to force the dirty bit. This can be removed
                                       // after we decide if it is safe to make "SetEntry" set the dirty bit itself
        switch (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->ePropertyType)
        {
        case efloat:
          page->SetEntry(szEntryName, m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.fltValue);
          break;
        case eint:
          page->SetEntry(szEntryName, m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.nValue);
          break;
        case eLPSTR:
          page->SetEntry(szEntryName, m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.szValue);
          break;
        case eBOOL:
          page->SetEntry(szEntryName, m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.fValue);
          break;
        case eVector3D:
          {
            Vector3D v3D(m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.v3dValues.x,
                         m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.v3dValues.y,
                         m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.v3dValues.z);
          page->SetEntry(szEntryName, (Vector3D)v3D);
          break;
          }
        } // switch for property type
      } // else, we are setting a property instead of removing one (ie.. fPresent true)
    } // for loop iterating properties
     
    outfile.Save();
  } // for loop iterating objects   

  return (!nFailures);
}

//****************************************************************************************
// LoadObjectData()
//
// This routine will search the content\weaponsubsystems directory for subdirectories containing .data files
// It will then open those .data files to read in weapon data. It is a simplistic algorithm that should be replaced
// by either searching through resources.build or some other weapons reference file if they are made to be fully up 
// to date with their references to the weapons.
//
// Recursive function so watch what kind of initializations you do, or how you effect
// global variables.
//
// Directory passed in must end in a backslash
//****************************************************************************************
BOOL CDlgObjectProperties::LoadObjectData(LPCSTR szDirectoryToSearchFor, DIRECTORYSEARCHTYPE eSearchType)
{
  CString strRootDirSearchString = szDirectoryToSearchFor;
  strRootDirSearchString += "*.*";
  CString strFullSubdirectoryName ;
  BOOL fRet = TRUE;
  //
  // If we are only supposed to seach in the root directory pass this to the next function that finds
  // the actual files
  //
  if (eSearchType == eRootOnly)
  {
    fRet = LoadDataFilesFromSubdirectory(szDirectoryToSearchFor);
    return fRet;  // return so we don't do any more searching
  }
  
  //
  // If we are doing all directories then we should search in the root as well, then
  // proceed down to the next level.
  //
  if (eSearchType == eAllSubdirectories)
  {
    fRet = LoadDataFilesFromSubdirectory(szDirectoryToSearchFor);
  }
  //
  // If we got an error, exit now, we don't continue searching the subdirectories
  //
  if (!fRet)
    return fRet;

  // 
  // If we got here we need to search for subdirectories of the current one
  // Then call this function recursively on each of those directories
  //

  //
  // Now we iterate through the current directory looking for subdirectories.
  //
  //
  WIN32_FIND_DATA wfdFindFileData;

  HANDLE hFileSearch = FindFirstFile((LPCSTR)strRootDirSearchString , &wfdFindFileData);
  if (hFileSearch == INVALID_HANDLE_VALUE)
  {
//    strOutput.Format("\r\nError : Didn't find any files in the source dir to copy.");
//    OutputString(strOutput);
    return TRUE;
  }
  if (wfdFindFileData.cFileName[0] == '\0')
  {
//    strOutput.Format("\r\nError : Didn't find any files in the source dir to copy.");
//    OutputString(strOutput);
    FindClose(hFileSearch); 
    return TRUE;
  }
  while (wfdFindFileData.cFileName[0])
  {
    if (!stricmp(wfdFindFileData.cFileName, ".") || !stricmp(wfdFindFileData.cFileName, ".."))
    {
      goto NextFile;
    }

    if (!(wfdFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
    {
      goto NextFile;
    }

    strFullSubdirectoryName = szDirectoryToSearchFor;
    strFullSubdirectoryName += wfdFindFileData.cFileName;
    EndDirectoryWithBackSlash(strFullSubdirectoryName);
    if (eSearchType == eAllSubdirectories)
    {
      LoadObjectData(strFullSubdirectoryName, eAllSubdirectories);
    }
    else // since eRootOnly was already weeded out above, this means "eSingleSubdirectory"
    {
      LoadObjectData(strFullSubdirectoryName, eRootOnly);
    }



NextFile:

    if (!FindNextFile(hFileSearch, &wfdFindFileData))
    {
      break;
    }

  } // while

  FindClose(hFileSearch);

  fRet = TRUE;
  //
  // Now the list box should be updated. We need a default selection, and then that will trigger the 
  // the fields to update correctly on the dialog box.
  //
  return fRet;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::LoadDataFilesFromSubdirectory(CString strSubdirectory)
{
  EndDirectoryWithBackSlash(strSubdirectory);
  CString strFullFilename ;
  CString strRootDirSearchString = strSubdirectory;

  strRootDirSearchString += m_strFileNameToSearchSubdirectoriesFor;
  //
  // Now we iterate through the subdirectory looking for files
  //
  //

  WIN32_FIND_DATA wfdFindFileData;

  HANDLE hFileSearch = FindFirstFile((LPCSTR)strRootDirSearchString , &wfdFindFileData);
  if (hFileSearch == INVALID_HANDLE_VALUE)
  {
//    strOutput.Format("\r\nError : Didn't find any files in the source dir to copy.");
//    OutputString(strOutput);
    return TRUE;
  }
  if (wfdFindFileData.cFileName[0] == '\0')
  {
//    strOutput.Format("\r\nError : Didn't find any files in the source dir to copy.");
//    OutputString(strOutput);
    FindClose(hFileSearch); 
    return TRUE;
  }
  while (wfdFindFileData.cFileName[0])
  {
    if (!stricmp(wfdFindFileData.cFileName, ".") || !stricmp(wfdFindFileData.cFileName, ".."))
    {
      goto NextFile;
    }

    if (wfdFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    {
      goto NextFile;
    }

    strFullFilename  = strSubdirectory;
    strFullFilename  += wfdFindFileData.cFileName;
    
    //
    // See if there is an additional filter function to call, and if so, call it
    //
    if (m_fnFilterFunction && !m_fnFilterFunction((LPCSTR)strFullFilename))
    {
      goto NextFile;
    }
    // 
    // We have a .data file, so see if it is appropriately setup and load into a data structure and 
    // add to the list box. We need to know if it is read only so we know whether or not we can 
    // alter the data
    // The data structure will also have to store a variable to know if the data has been altered.
    // 
    if (LoadObjectDataFromFile(strFullFilename , (wfdFindFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)))
    {
      // 
      // If we successfully loaded the weapon, add it to the list box
      //
      // Alias some of the data for readability of the code
      int nCurrObject = m_pObjectList->nObjectCount;
      CDOPPROPERTIES_OBJECT_PROPERTIES * pObjectPropertiesArray = (m_pObjectList->ppPropertiesArray[nCurrObject]);
      
      //
      // CString is used to remove the directory info To the weapons name. Find the last \
      //
      CString strObjectNameOnly(pObjectPropertiesArray->szObjectName);
      int nPos = 0;
      if (!m_fShowFullObjectName)
      {
        nPos = strObjectNameOnly.ReverseFind('\\');
        nPos ++; // goes to 0 if not found, otherwise the character after the \ or the null terminator if that's last
      }

      int nSel = m_lbObjectList.AddString((pObjectPropertiesArray->szObjectName + nPos));
      m_lbObjectList.SetItemData(nSel, (DWORD) pObjectPropertiesArray);
      m_pObjectList->nObjectCount++; // only increment on success since we don't want the index off in the list
    }



NextFile:

    if (!FindNextFile(hFileSearch, &wfdFindFileData))
    {
      break;
    }
  
  } // while

  FindClose(hFileSearch);
  return TRUE;
}

//****************************************************************************************
//
//
//
//
//
//****************************************************************************************
BOOL CDlgObjectProperties::LoadObjectDataFromFile(CString strFullObjectFilename, BOOL fReadOnly )
{
//  CString strOutput;

  NotationFile infile(strFullObjectFilename, NotationFile::Raw);

  //
  // Alias some of the data for readability of the code
  int nCurrObject = m_pObjectList->nObjectCount;
  CDOPPROPERTIES_OBJECT_PROPERTIES * pObjectPropertiesArray = (m_pObjectList->ppPropertiesArray[nCurrObject]);
  int nTotalProperties = m_paPropertiesToEdit->nCount;
  //
  // allocate the memory for the properties for this object
  //
  pObjectPropertiesArray->fDirty = FALSE;
  pObjectPropertiesArray->fReadOnly = fReadOnly;
  pObjectPropertiesArray->nProperties = nTotalProperties;
  strcpy(pObjectPropertiesArray->szObjectName, (LPCSTR)strFullObjectFilename);
  
// already allocated
//  pObjectPropertiesArray->ppObjectProperties = Allocate_CDOPPROPERTIES_OBJECT_PROPERTIES(nTotalProperties);

  //
  // Now go through and for each item in the properties list we need to get the page and entry
  // we will cache the page name so we don't keep getting the same one if it repeats
  //


  Page * page = NULL;
  char szCurrentPageName[128] = ""; // used to prevent repetitively searching for the same page
  for (int j=0; j < nTotalProperties; j++)
  {
    pObjectPropertiesArray->ppObjectProperties[j]->fPresent = FALSE; // by default...

    switch (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->ePropertyType)
    {
      case efloat:
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.fltValue = 0.0f;
        break;
      case eint:
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.nValue = 0;
        break;
      case eLPSTR:
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.szValue[0] = 0;
        break;
      case eBOOL:
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.fValue = FALSE;
        break;
      case eVector3D:
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.v3dValues.x = 0.0f;
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.v3dValues.y = 0.0f;
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.v3dValues.z = 0.0f;
        break;
    } // switch for property type
    
    const char * szPageName = GetPageNameToPropertyName(m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->szPropertyTag);
    if (!page || strcmp(szCurrentPageName, szPageName))
    {
      strcpy(szCurrentPageName, szPageName);
      page = infile.FindPage(szPageName);
      if (!page) // if for some reason we can't find the page, jump out
        continue;
    }
    const char * szEntryName = GetEntryNameToPropertyName(m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->szPropertyTag);
    //
    // For each item in the data list, if it shouldn't be there, delete it. If it supposed to be there set it
    // Assumption is made that there are not two of any particular value key
    //
    switch (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->ePropertyType)
    {
      case efloat:
        if (page->GetEntry(szEntryName, &(pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.fltValue)))
        {
          pObjectPropertiesArray->ppObjectProperties[j]->fPresent = TRUE;
        }
        break;
      case eint:
        if (page->GetEntry(szEntryName, &(pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.nValue)))
        {
          pObjectPropertiesArray->ppObjectProperties[j]->fPresent = TRUE;
        }
        break;
      case eLPSTR:
        {
          const char * szTemp; 
          if (page->GetEntry(szEntryName, &szTemp))
          {
            strcpy(pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.szValue, szTemp) ;
            pObjectPropertiesArray->ppObjectProperties[j]->fPresent = TRUE;
          }
        }
        break;
      case eBOOL:
        if (page->GetEntry(szEntryName, &(pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.fValue)))
        {
          pObjectPropertiesArray->ppObjectProperties[j]->fPresent = TRUE;
        }
        break;
      case eVector3D:
        {
          Vector3D v3D(0.0f, 0.0f, 0.0f);
          if (page->GetEntry(szEntryName, (Vector3D *)&v3D))
          {
            pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.v3dValues.x = v3D.x;
            pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.v3dValues.y = v3D.y;
            pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.v3dValues.z = v3D.z;
            pObjectPropertiesArray->ppObjectProperties[j]->fPresent = TRUE;
          }
        break;
        }
    } // switch for property type

  } // for loop iterating properties


  //
  // This is a good place to make any known automated updates to the data. If we have just made them all 
  // add a new variable, or we removed a line, or we changed the type of data stored, we can do it at load
  // time, then mark the dirty flag. Beware to honor the readonly flag here though. We don't want the read only
  // and the dirty bit set at the same time.
  // 

  //
  // Note that we don't increment the weapon count here as we have to make sure we added to the list box
  // since they are kept in sync by the index in the list box.
  //
  return TRUE;
}




//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectProperties::OnSelchangeObjectlist() 
{
  //
  // First check what the old focus was, see if the data has changed, and if it has then we can
  // update the weapons data structure and move to the next selection
  //

  // Load the current data into the variables, then copy that data to a weaponinfo structure

  MoveCurSelDataToOrFromControls(FALSE); 

  //
  // If we have selected something then we need to update info to the controls
  // 
  m_nCurSel = m_lbObjectList.GetCurSel();
  m_popCurObjectPropertiesArray = (CDOPPROPERTIES_OBJECT_PROPERTIES * )(void *)m_lbObjectList.GetItemData(m_nCurSel);
  MoveCurSelDataToOrFromControls(TRUE);
  EnableControlsForCurrentSelection();

}



//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::DisableAllControls()
{

  for (UINT i = IDC_DOP_PROPNAME; i < m_nLastControlID ; i++)
  {
    CWnd * pWnd = GetDlgItem(i);
    if (pWnd)
      pWnd->EnableWindow(FALSE);
  }
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::EnableControlsForCurrentSelection()
{
  // In addition to enabling disabling the various controls...
  // We also have to set the read only text properly when we are done
  //

  
  // if there is not a current selection then disable all and move on
  if (m_popCurObjectPropertiesArray == NULL)
  {
    DisableAllControls();
    SetDlgItemText(IDC_READONLY, MyLoadStringTempBuffer(IDS_READONLY));
    return FALSE;
  }

  BOOL fReadOnly = m_popCurObjectPropertiesArray->fReadOnly;
  // 
  // If we got here then we have a selected item and need to enable/disable the controls properly
  // based on the data object info structures
  // 
  for (int i = 0; i < m_paPropertiesToEdit->nCount; i++)
  {
    // First is the first ID of the controls for this property. End is the first control of the next
    // property.
    UINT uiFirst = m_paPropertiesToEdit->ppProperties_To_Edit_Entry[i]->uiReserved1_CTRLID;
    CWnd * pWnd = GetDlgItem(uiFirst);
    BOOL fPresent = m_popCurObjectPropertiesArray->ppObjectProperties[i]->fPresent;

    if (pWnd)
    {
      pWnd->EnableWindow(!fReadOnly);
    }
    pWnd = GetDlgItem(uiFirst+1);
    if (pWnd)
    {
      pWnd->EnableWindow(!fReadOnly);
    }
    pWnd = GetDlgItem(uiFirst+2);
    if (pWnd)
    {
      pWnd->EnableWindow(!fReadOnly && fPresent);
    }
    if (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[i]->ePropertyType == eVector3D)
    {
      pWnd = GetDlgItem(uiFirst+3);
      if (pWnd)
      {
        pWnd->EnableWindow(!fReadOnly && fPresent);
      }

      pWnd = GetDlgItem(uiFirst+4);
      if (pWnd)
      {
        pWnd->EnableWindow(!fReadOnly && fPresent);
      }
    }  // if for vector3d property type

  } // for loop for property list



  SetDlgItemText(IDC_READONLY, (fReadOnly ? MyLoadStringTempBuffer(IDS_READONLY) : _T("")));


  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::MoveCurSelDataToOrFromControls(BOOL fToControls)
{


  // if there is not a current selection then disable all and move on
  if (m_popCurObjectPropertiesArray == NULL)
  {
    DisableAllControls();
    SetDlgItemText(IDC_READONLY, MyLoadStringTempBuffer(IDS_READONLY));
    return FALSE;
  }

  //
  // If we are retrieving To the controls we need to save off what we have and
  // see if it changed.
  //

  CDOPPROPERTIES_OBJECT_PROPERTIES * pPropertyArray = NULL;
  if (!fToControls)
  {
    pPropertyArray = Allocate_CDOPPROPERTIES_OBJECT_PROPERTIES(
                                     m_paPropertiesToEdit->nCount);
    Copy_CDOPPROPERTIES_OBJECT_PROPERTIES(pPropertyArray, m_popCurObjectPropertiesArray);
  }
  UpdateControlData(fToControls); // put the data in the controls based on the current data pointer


  //
  // See if the data changed if we are reading To the controls
  //
  if (!fToControls)
  {
    if (!IsEqual_CDOPPROPERTIES_OBJECT_PROPERTIES(pPropertyArray, m_popCurObjectPropertiesArray)) 
    {
      m_popCurObjectPropertiesArray->fDirty = TRUE; // it changed
      m_fDirty = TRUE; // at least one object has changed.
    }
    free(pPropertyArray);
  }

  return TRUE;

}

//****************************************************************************************
//
//****************************************************************************************

BOOL CDlgObjectProperties::GetBOOLFromButton(UINT uiID, BOOL * pfDest)
{
  CButton * pWnd = (CButton *)GetDlgItem(uiID);
  if (!pWnd)
    return FALSE;
  *pfDest = pWnd->GetCheck();
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::GetBOOLFromControl(UINT uiID, BOOL * pfDest)
{
  CWnd * pWnd = GetDlgItem(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  GetDlgItemText(uiID, szTemp, 255);
  
  *pfDest = FALSE;
  if (!stricmp(szTemp, "true") || 
      !stricmp(szTemp, "yes") || 
      !stricmp(szTemp, "1")) 
  {
    *pfDest = TRUE;
  }
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::GetfloatFromControl(UINT uiID, float * pfltDest)
{
  CWnd * pWnd = GetDlgItem(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  GetDlgItemText(uiID, szTemp, 255);
  *pfltDest = (float)atof(szTemp);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::GetLPSTRFromControl(UINT uiID, LPSTR pszDest)
{
  CWnd * pWnd = GetDlgItem(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  GetDlgItemText(uiID, szTemp, 255);
  szTemp[255] = 0;
  strcpy(pszDest, szTemp);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::GetintFromControl(UINT uiID, int * pnDest)
{
  CWnd * pWnd = GetDlgItem(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  GetDlgItemText(uiID, szTemp, 255);
  *pnDest = atoi(szTemp);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::GetVector3DFromControl(UINT uiID, float * pfltx, float * pflty, float * pfltz)
{
  CWnd * pWnd = GetDlgItem(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  GetDlgItemText(uiID, szTemp, 255);
  *pfltx = (float)atof(szTemp);
  pWnd = GetDlgItem(uiID +1);
  if (!pWnd)
    return FALSE;
  GetDlgItemText(uiID+1, szTemp, 255);
  *pflty = (float)atof(szTemp);
  pWnd = GetDlgItem(uiID +2);
  if (!pWnd)
    return FALSE;
  GetDlgItemText(uiID+2, szTemp, 255);
  *pfltz = (float)atof(szTemp);

  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::SetBOOLToButton(UINT uiID, BOOL fDest)
{
  CButton * pWnd = (CButton *)GetDlgItem(uiID);
  if (!pWnd)
    return FALSE;
  pWnd->SetCheck(fDest);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::SetBOOLToControl(UINT uiID, BOOL fSrc)
{
  CWnd * pWnd = GetDlgItem(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  sprintf(szTemp, fSrc ? "true" : "false");
  SetDlgItemText(uiID, szTemp);
  return TRUE;

}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::SetfloatToControl(UINT uiID, float fltSrc)
{
  CWnd * pWnd = GetDlgItem(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  sprintf(szTemp, "%f", (double) fltSrc);
  SetDlgItemText(uiID, szTemp);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::SetLPSTRToControl(UINT uiID, LPSTR pszSrc)
{
  CWnd * pWnd = GetDlgItem(uiID);
  if (!pWnd)
    return FALSE;
  SetDlgItemText(uiID, pszSrc);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::SetintToControl(UINT uiID, int nSrc)
{
  CWnd * pWnd = GetDlgItem(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  sprintf(szTemp, "%d", nSrc);
  SetDlgItemText(uiID, szTemp);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::SetVector3DToControl(UINT uiID, float fltx, float flty, float fltz)
{
  CWnd * pWnd = GetDlgItem(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  sprintf(szTemp, "%f", (double) fltx);
  SetDlgItemText(uiID, szTemp);

  sprintf(szTemp, "%f", (double) flty);
  SetDlgItemText(uiID + 1, szTemp);

  sprintf(szTemp, "%f", (double) fltz);
  SetDlgItemText(uiID + 2, szTemp);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectProperties::UpdateControlData(BOOL fToControls)
{
  if (!m_popCurObjectPropertiesArray)
    return FALSE;
  BOOL fReadOnly = m_popCurObjectPropertiesArray->fReadOnly;
  if (fReadOnly && !fToControls)
    return FALSE; // don't bother reading if we don't have to
  // 
  // 
  for (int i = 0; i < m_paPropertiesToEdit->nCount; i++)
  {
    // First is the first ID of the controls for this property. End is the first control of the next
    // property.
    UINT uiFirst = m_paPropertiesToEdit->ppProperties_To_Edit_Entry[i]->uiReserved1_CTRLID;
    if (!fToControls)
    {
      GetBOOLFromButton(uiFirst + 1,&(m_popCurObjectPropertiesArray->ppObjectProperties[i]->fPresent) );
      switch (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[i]->ePropertyType)
      {
        case efloat:
          GetfloatFromControl(uiFirst + 2,&(m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.fltValue));
          break;
        case eint:    
          GetintFromControl(uiFirst + 2,&(m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.nValue));
          break;
        case eLPSTR:  
          GetLPSTRFromControl(uiFirst + 2,(m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.szValue));
          break;
        case eBOOL:   
          GetBOOLFromControl(uiFirst + 2,&(m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.fValue));
          break;
        case eVector3D:
          GetVector3DFromControl(uiFirst + 2,&(m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.v3dValues.x),
                                           &(m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.v3dValues.y),
                                           &(m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.v3dValues.z));
          break;
      } // switch on property type
    } // if !fFromControls
    else
    {
      SetBOOLToButton(uiFirst + 1,(m_popCurObjectPropertiesArray->ppObjectProperties[i]->fPresent) );
      switch (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[i]->ePropertyType)
      {
        case efloat:
          SetfloatToControl(uiFirst + 2,(m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.fltValue));
          break;
        case eint:    
          SetintToControl(uiFirst + 2,(m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.nValue));
          break;
        case eLPSTR:  
          SetLPSTRToControl(uiFirst + 2,(m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.szValue));
          break;
        case eBOOL:   
          SetBOOLToControl(uiFirst + 2,(m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.fValue));
          break;
        case eVector3D:
          SetVector3DToControl(uiFirst + 2,(m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.v3dValues.x),
                                           (m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.v3dValues.y),
                                           (m_popCurObjectPropertiesArray->ppObjectProperties[i]->uPropValue.v3dValues.z));
          break;
      } // switch on property type
    }
  } // for loop for property list

  return TRUE;
}



