/* Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. */

// DisplayAttDlg.cpp : implementation file

#include "stdafx.h"
#include "clientUI.h"
#include "DisplayAttDlg.h"
#include "DisplayDicomDlg.h"

// DisplayAttDlg dialog

IMPLEMENT_DYNAMIC(DisplayAttDlg, CDialog)

void DisplayAttDlg::init(DICOM_IMAGE *dicom, BOOL edit, DICOM_DBS *dm_dbs) {
        // Initialize the local DICOM_IMAGE to be displayed in the dialog.
	copy_dicom_image(dicom, &m_dicom);
        
        // Initialize properties and status for the dialog.
	m_edit = edit;
	m_tab = 0;
	m_selAttIndex= 0;
	m_selIndex = 0;
	this->dbs = dm_dbs;

        // Initialize the name of all tabs. First three tabs are the groups
        // of all attributes. The fourth tab is the image. 
	m_tabItemName[0] = "Patient";
	m_tabItemName[1] = "Study";
	m_tabItemName[2] = "Acquisition";
	m_tabItemName[3] = "Image";

        // Initialize the number of attributes displayed under each tabs.
	m_groupNum[0] = PATIENT_ITEMNUM;
	m_groupNum[1] = STUDY_ITEMNUM;
	m_groupNum[2] = ACQUISITION_ITEMNUM;

        // Initialize the name of attributes displayed under each tabs.
	for (int i = 0; i < ITEMNUM - 1; i++)
		m_groupIndex[i] = new int[m_groupNum[i]];

        // Initialize the name of all patient-related attributes.
	m_groupIndex[0][0] = PATIENTAGE;
	m_groupIndex[0][1] = PATIENTBIRTHDATE;
	m_groupIndex[0][2] = PATIENTID;
	m_groupIndex[0][3] = PATIENTNAME;
	m_groupIndex[0][4] = PATIENTSEX;

        // Initialize the name of all study-related attributes.
	m_groupIndex[1][0] = INSTITUTIONNAME;
	m_groupIndex[1][1] = INSTITUTIONALDEPARTMENTNAME;
	m_groupIndex[1][2] = STUDYDESCRIPTION;
	m_groupIndex[1][3] = STUDYDATE;
	m_groupIndex[1][4] = STATIONNAME;

        // Initialize the name of all acquisition-related attributes.
	m_groupIndex[2][0] = ACQUISITIONDATE;
	m_groupIndex[2][1] = ACQUISITIONTIME;
	m_groupIndex[2][2] = MANUFACTURER;
	m_groupIndex[2][3] = PERFORMINGPHYSICIANNAME;
	m_groupIndex[2][4] = OPERATORNAME;

}

// Retrieve attributes and image from the given DICOM_IMAGE and display the 
// in the dialog. The dialog is editable under admin role. Any attributes 
// could be updated in bdb under this mode. 
DisplayAttDlg::DisplayAttDlg(DICOM_IMAGE *dicom, BOOL edit, DICOM_DBS *dm_dbs, CWnd* pParent /*=NULL*/)
	: CDialog(DisplayAttDlg::IDD, pParent)
{
	init(dicom, edit, dm_dbs);
}

// Given DICOM's id, get DICOM content from bdb. Retrieve its attributes and
// image and display them in the dialog. The dialog could be editable under 
// admin role. Any attributes could be updated in bdb under this mode. 
DisplayAttDlg::DisplayAttDlg(int id, BOOL edit, DICOM_DBS *dm_dbs, CWnd* pParent)
	: CDialog(DisplayAttDlg::IDD, pParent)
{
        DICOM_IMAGE dicom;
        int ret;

        // Retrieve DICOM from bdb with the id. Since the id is unique here,
        // only 0 or 1 DICOM should be retrieved. If more than 1 DICOM with 
        // the same id, it should be considered as failure like other bdb 
        // error and any retrieved DICOM should be freed.
	memset(&dicom, 0, sizeof(DICOM_IMAGE));
	ret = retrieve_dicom_with_id(dm_dbs, id, &dicom);
	if ((ret == -1) || (ret > 1)) {
	       free_dicom_image(&dicom);
               MessageBox(TEXT("Error initialize this dialog. Please retry.\
 If you still see any error, please clean the home directory \'dicom\' \
directory and restart the program."));
	       return;
	}
        // Call another constructor of the same object.
        init(&dicom, edit, dm_dbs);
            
        free_dicom_image(&dicom);
		ret = 1;
		ret = 2;
}

DisplayAttDlg::~DisplayAttDlg()
{
        // Free the memory in m_dicom.
        free_dicom_image(&m_dicom);
}

void DisplayAttDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_ATTRIBUTE_LIST, pCtrlAtt);
	DDX_Control(pDX, IDC_ATTRIBUTE_GROUP_TAB, pTabCtrl);
	DDX_Control(pDX, IDC_EDIT_VALUE, pValueEdit);
	DDX_Control(pDX, IDC_UPDATE_BUTTON, pUpdateButton);
}

BOOL DisplayAttDlg::OnInitDialog()
{
        // Column in the list control.
        LVCOLUMN lvColumn;
        // Item in the list control.
	LVITEM lvItem;
	// The value of the item in the list control.
	LPCTSTR lpItemText;
	
	int i;
	
        // The width of the full screen.
        int fullScreenCx = GetSystemMetrics(SM_CXFULLSCREEN);
        // The height of the full screen.  
	int fullScreenCy = GetSystemMetrics(SM_CYFULLSCREEN);

        // The height of those tabs.
	int tabCy = 20;
	// The height of the button. If the current dialog is not editable, 
	// the botton is not visible so its height is 0.
	int buttonCy = (m_edit) ? 20: 0;
	// The height of the list.
	int listCy = GetSystemMetrics(SM_CYFULLSCREEN) - tabCy - buttonCy;
	
	// Initialize the dialog.
	CDialog::OnInitDialog();
        
        // Set the dialog position.
	this->SetWindowPos(this, 0, 0, GetSystemMetrics(SM_CXFULLSCREEN), 
            GetSystemMetrics(SM_CYFULLSCREEN), SWP_NOZORDER|SWP_NOMOVE);

        // Set the position of tab control and set the size of every tab.
	pTabCtrl.SetWindowPos(this, 0, 0, fullScreenCx, tabCy, 
            SWP_NOZORDER|SWP_NOMOVE);
	CSize itemSize(fullScreenCx/ITEMNUM, 0);
	pTabCtrl.SetItemSize(itemSize);
	
	// Insert all tabs into the tab control.
	for (int i = 0; i < ITEMNUM; i++)
		pTabCtrl.InsertItem(i, m_tabItemName[i]);

        // Set the position and properties of list control for displayed 
        // attributes. If the dialog is editable, the list control is also 
        // editable. Otherwise, the edit box and update botton should be 
        // invisible.
	pCtrlAtt.SetWindowPos(this, 0, tabCy, fullScreenCx, listCy, 
            SWP_NOZORDER|SWP_NOMOVE);
	if (m_edit) 
                pCtrlAtt.SetExtendedStyle(
		    pCtrlAtt.GetStyle() | LVS_EDITLABELS );
	else {
		pValueEdit.ShowWindow(SW_HIDE);
		pUpdateButton.ShowWindow(SW_HIDE);
	}

        // Set the style and content of columns in the list control. 
	lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
	lvColumn.fmt = LVCFMT_LEFT;
	lvColumn.cx = GetSystemMetrics(SM_CXFULLSCREEN) / 2;

        // The first column is "Attribute", the name of the attribute.
	lvColumn.pszText = TEXT("Attribute");
	pCtrlAtt.InsertColumn(0, &lvColumn);

        // The second column is "Value", the value of the attribute.
	lvColumn.pszText = TEXT("Value");
	pCtrlAtt.InsertColumn(1, &lvColumn);

	// Set the style in the list control. Insert those attributes under 
        // the group of m_tab. All strings from bdb should be converted to 
        // wide strings to display correctly on the screen.
	lvItem.mask = LVIF_TEXT;
	lvItem.iItem = 0;
	for (i = m_groupNum[m_tab] - 1; i >= 0; i--) {
                // Insert a new attribute name. The attribute name could be 
                // achieved in atts_name according to the index in attribute
                // groups.
                lvItem.iSubItem = 0;
                lvItem.pszText =  (convertCharToWchar(
                    atts_name[m_groupIndex[m_tab][i]]));
                pCtrlAtt.InsertItem(&lvItem);
                
                // Set its attribute value. The attribute value could be 
                // achieved in m_dicom according to the index in attribute
                // groups.
                lpItemText = (convertCharToWchar(position_dicom_field(
                    m_groupIndex[m_tab][i], &m_dicom, NULL, NULL)));
                pCtrlAtt.SetItemText(0, 1, lpItemText);
                
                // Free the memory of converted wide string.
                free((WCHAR *)lvItem.pszText);
		free((WCHAR *)lpItemText);		
	}

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

BEGIN_MESSAGE_MAP(DisplayAttDlg, CDialog)
	ON_NOTIFY(TCN_SELCHANGE, IDC_ATTRIBUTE_GROUP_TAB, 
            &DisplayAttDlg::OnTcnSelchangeAttributeGroupTab)
	ON_NOTIFY(NM_DBLCLK, IDC_ATTRIBUTE_LIST, 
            &DisplayAttDlg::OnNMDblclkAttributeList)
	ON_BN_CLICKED(IDC_UPDATE_BUTTON, 
            &DisplayAttDlg::OnBnClickedUpdateButton)
END_MESSAGE_MAP()

// When click any tab in tab control, display the content accordingly.
void DisplayAttDlg::OnTcnSelchangeAttributeGroupTab(NMHDR *pNMHDR, 
    LRESULT *pResult)
{
        // Item in list control.
        LVITEM lvItem;
        // The value of the item in the list control.
	LPCTSTR lpItemText;
        
        // Pointer to image data.
        char *image;
        // The size of image data.
        int size;
        
        int i;
        
	*pResult = 0;
        
        // Get the current selected tab.
	int groupIndex = pTabCtrl.GetCurSel();
	// If the selected tab is not the current tab being displayed, then
	// refresh the list control and display those attributes under the 
	// selected tab.
	if ( groupIndex != m_tab) {
                if (groupIndex == ITEMNUM - 1) {
                        // When selecting the image tab, get image and its 
			// size of the current DICOM, and display it in
			// another dialog. 
                        image = m_dicom.thumbnail_content;
                        size = m_dicom.thumbnail_content_length;
                        DisplayDicomDlg displayDicomDlg(image, size);
                        displayDicomDlg.DoModal();
                } else {
                        // When selecting group tab other than the current 
                        // one, deleted those attributes being displayed and
                        // refresh with attributes under the selected group.
			pCtrlAtt.DeleteAllItems();
			lvItem.mask = LVIF_TEXT;
			lvItem.iItem = 0;
			for (i = m_groupNum[m_tab] - 1; i >= 0; i--) {
				lvItem.iSubItem = 0;

				// Convert attribute name to wide string.
				lvItem.pszText =  (convertCharToWchar(
                                    atts_name[m_groupIndex[
				    groupIndex][i]]));
        
                                // Convert attribute value to wide string.
				lpItemText = (convertCharToWchar(
                                    position_dicom_field(
                                    m_groupIndex[groupIndex][i], 
                                    &m_dicom, NULL, NULL)));
                                
                                // Insert the attribute name and its value.
				pCtrlAtt.InsertItem(&lvItem);
				pCtrlAtt.SetItemText(0, 1, lpItemText);
				
				// Free the memory of converted wide string.
				free((WCHAR *)lvItem.pszText);
				free((WCHAR *)lpItemText);
			}

                        // Update m_tab to the new group index.
			m_tab = groupIndex;
		}
	}
}

// When click on any item in the list control, if it is editable, display
// its attribute value (in the second column). If it is not editable,  
// nothing happens.
void DisplayAttDlg::OnNMDblclkAttributeList(NMHDR *pNMHDR, LRESULT *pResult)
{
	// Selected item in list control. 
	POSITION ps;
	// Attribute value. 
	char *value;
	// Wide string of the attribute value.
	LPWSTR text;
	// Position in the window.
	DWORD dwPos;
	UINT nFlag;
	
	*pResult = 0;
	
	// If the dialog is editable, display attribute value in edit box.
	if (m_edit) {
                dwPos = GetMessagePos();     
                CPoint point(LOWORD(dwPos), HIWORD(dwPos));   
		ScreenToClient(&point);   
		pCtrlAtt.HitTest(point, &nFlag);

                // Get the index of selected item in list control. 
                ps = pCtrlAtt.GetFirstSelectedItemPosition();
                m_selIndex=pCtrlAtt.GetNextSelectedItem(ps);
                
                // Get the attribute value from the index, convert it to 
                // wide string, and display it in edit box.
                if (m_selIndex >= 0) {
                        // Get the index of selected attribute in all 
			// attributes.
                        m_selAttIndex = m_groupIndex[m_tab][m_selIndex];
                        
                        // Get the value of current attribute.
                        value = position_dicom_field(m_selAttIndex, 
                            &m_dicom, NULL, NULL);
                        
                        // Convert the value to wide string.
                        text = (convertCharToWchar(value));
                        
                        // Set the text in edit box to be the wide string.
                        pValueEdit.SetWindowTextW(text);
                        
                        // Free the memory of wide string.
                        //free((WCHAR *)text);
			free(text);
                }
	}
}

// When click on the update botton, update the attribute with the text in  
// the edit box.
void DisplayAttDlg::OnBnClickedUpdateButton()
{
        
        // Item in the list control.
        LVITEM lvItem;
        // Wide string of the attribute value.
	LPWSTR lpItemText;
        // Wide string from edit box.
        wchar_t attValue[50];
	// Buffer to cache the new bytes.
	char buffer[54] = {0};
	// Pointer to attribute value in DICOM and new attribute value.
	char *p;
	char *field;
	
        // The length of the original bytes in DICOM.
	int original_length = 0;
	// The length of new bytes in DICOM.
	int new_length = 0;
	// The offset.
	int offset  = 0;
	
	int ret;

        // If the dialog is not editable, nothing is done.
	if (m_edit == false)
		return;
	
	// Find the value of selected attribute in m_dicom and return its
	// position in offset and its length in original_length.
 
	p = position_dicom_field(m_selAttIndex, &m_dicom, &field, 
            &original_length);

	//original_length = strlen(p);
	
	// Free the memory of content in m_dicom for the selected attribute.
	free(p);
	
	// Get the text in edit box and convert text to char. 
	pValueEdit.GetWindowTextW((LPTSTR)attValue, 50);
	p = convertWcharToChar(attValue);
	new_length = strlen(p);

	// Delete the original item from list control.
	pCtrlAtt.DeleteItem(m_selIndex);
	
	// Insert a new item to list control on the same position as the 
        // original item. The first column in the item is the name of the 
        // selected attribute. The second column in the item is the new 
        // content of selected attribute.
        pCtrlAtt.InsertItem(m_selIndex, 
	    (convertCharToWchar(atts_name[m_selAttIndex]))); 
        pCtrlAtt.SetItemText(m_selIndex, 1, (LPTSTR)attValue);

	
	// To replace the old attribute in bdb, both its length and content
        // should be updated. So convert the new length to bytes and store 
        // them in a buffer together with bytes of new attribute value. 
        // Finally partially write them into bdb.


    update_field(this->dbs, &m_dicom, field, p, new_length);


}
