/*************************************************************************\
* "$RCSfile: CPSAppHelpers.cpp,v $"                                       *
***************************************************************************
* Information by CVS or VSS:                                              *
*     Last check in on: $Date: 2011-10-20 07:25:48 $                      *
*     by                $Author: schluckebier $
*     Revision        : $Revision: 1.48 $                                 *
\*************************************************************************/
/*! \file CPSAppHelpers.cpp
*	\brief Implementation CPSApp Class
**************************************************************************/

#include "VIC_PS.h"
#include "CRegion.h"
#include "CZoomFactor.hpp"
#include "CLog.hpp"
#include "CPSApp.hpp"
#include "CDlg.hpp"
#include "CDlgMain.hpp"
#include "CFeature.hpp"
#include "CCamFeatures.hpp"
#include "CDlgTools.hpp"
#include "CDlgFileMgr.hpp"
#include "CDlgCam.hpp"
#include "CDlgSaveImage.hpp"
#include "CCpuLoad.hpp"

#pragma DATA_SECTION("d_intern")
static int g_pLut[256];	//!< LUT value for histogram stretching

/*************************************************************************/
void CPSApp::UpdateScaleFactors()
/*************************************************************************/
/*! @brief Calculates the scale coefficients for zoom function.
**************************************************************************/
{
	m_VCamPosX = m_PVPosX * m_PVScale;
	m_VCamPosY = m_PVPosY * m_PVScale;

	m_AppIni->setValue("PVPosX", m_PVPosX);
	m_AppIni->setValue("PVPosY", m_PVPosY);
	m_AppIni->setValue("PVScale", m_PVScale);

	m_PVWidth = (m_YPVImage.Width / m_PVScale) + 0.5;
	m_PVWidth = _min2(m_PVWidth, GetCamWidth());
	m_PVHeight = (m_YPVImage.Height / m_PVScale) + 0.5;
	m_PVHeight = _min2(m_PVHeight, GetCamHeight());

	m_VCamWidth  = GetCamWidth()  * m_PVScale;
	m_VCamHeight = GetCamHeight() * m_PVScale;

	if (m_VCamPosX + m_YPVImage.Width > m_VCamWidth)
		m_VCamPosX = m_VCamWidth - m_YPVImage.Width;
	if (m_VCamPosY + m_YPVImage.Height > m_VCamHeight)
		m_VCamPosY = m_VCamHeight - m_YPVImage.Height;

	m_RegionPV.NewWH(m_PVPosX, m_PVPosY, m_PVWidth, m_PVHeight);

	//=== Clear camera images ===
	GUI_FillY(&m_YPVImage, 128);
	GUI_Fill(&m_CPVImage, &rgbGray50);
}

/*************************************************************************/
void CPSApp::DisplayInfoArea(bool Refresh)
/*************************************************************************/
/*! @brief Shows log or histogram at info area of display.
	@param Refresh If 1 the log is redrawn even if there are no changes.
**************************************************************************/
{
	static int infoOld = InfoType::LOGFILE;
	CDlgMain *pDlgMain = CDlgMain::GetInstance();
	CLog *pLog = CLog::GetInstance();
	int Low, High;
	int VCamWidth, VCamHeight;
	int Hist[256];

	switch (m_Info)
	{
	case InfoType::LOGFILE:
		SetTP(TextStyle::MONOSPACE);
		if (Refresh || infoOld != InfoType::LOGFILE)
		{
			if (pLog->Redraw(&m_LogImage))
				DisplayImage(&m_LogImage, m_LogPosX, m_LogPosY);
		}
		else
		{
			if (pLog->Update(&m_LogImage))
				DisplayImage(&m_LogImage, m_LogPosX, m_LogPosY);
		}
		SetTP(TextStyle::NORMAL);
		break;
	case InfoType::HISTOGRAM:
	case InfoType::HISTOGRAM_LOG:
		if (m_pCamImage)
		{
			if (infoOld != m_Info)
				PrepareHistogram();

			VCamWidth  = GetCamWidth();
			VCamHeight = GetCamHeight();

			// Use Look-Up-Table
			if (m_HistStretch)
			{
				unsigned char *restrict pY = m_pCamImage->pY;

				for (int y = 0; y < VCamHeight; y++)
				{
					for (int x = 0; x < VCamWidth; x++)
						pY[x + (unsigned short)y * (unsigned short)m_pCamImage->Width] =
							g_pLut[pY[x + (unsigned short)y * (unsigned short)m_pCamImage->Width]];
				}
				CalcHistogram(m_pCamImage, Hist);
			}
			else
			{
				CalcHistogram(m_pCamImage, Hist);
				CalcLowHigh(Low, High, Hist);
				CalcLut(Low, High, g_pLut);
			}
			DrawHistogram(&m_HistImage, Hist, m_Info == InfoType::HISTOGRAM ? HistogramMode::LINEAR : HistogramMode::LOGARITHM);
		}
		break;
	case InfoType::APP:
		if (FxnDisplayDlgAppInfo)
		{
			(*FxnDisplayDlgAppInfo)(0);
		}
		else
		{
			m_Info--;
			pDlgMain->Refresh();
			pDlgMain->Show();
		}
		break;
	}
	infoOld = m_Info;
}

/*************************************************************************/
void CPSApp::DisplayImage(CIMAGE *pImage, int PosX, int PosY)
/*************************************************************************/
/*! @brief Display image.
	@param pImage Pointer to image
	@param PosX Display position
	@param PosY Display position
**************************************************************************/
{
	if (m_pDisplayImage)
		GUI_CopyArea(pImage, &m_Image, 0, 0, pImage->Width, pImage->Height, PosX, PosY);
	else
		GUI_Display(pImage, PosX, PosY);
}

/*************************************************************************/
void CPSApp::CopyInfoArea()
/*************************************************************************/
/*! @brief Copies log into info area of display image.
**************************************************************************/
{
	switch (m_Info)
	{
	case InfoType::LOGFILE:
		SetTP(TextStyle::MONOSPACE);
		CLog::GetInstance()->Redraw(&m_LogImage);
		SetTP(TextStyle::NORMAL);
		GUI_CopyArea(&m_LogImage, &m_Image, 0, 0,
			m_LogImage.Width, m_LogImage.Height,
			m_LogPosX, m_LogPosY);
		break;
	case InfoType::HISTOGRAM:
	case InfoType::HISTOGRAM_LOG:
		GUI_CopyArea(&m_HistImage, &m_Image, 0, 0,
			m_HistImage.Width, m_HistImage.Height,
			m_LogPosX, m_LogPosY);
		break;
	}
}

/*************************************************************************/
void CPSApp::DrawCPULoadPic()
/*************************************************************************/
/*! @brief Draws a CPU load diagram into #m_CPULoadPic.
**************************************************************************/
{
	GUI_WndFrameG(&m_CPULoadPic, m_Str[StringTable::CPU_LOAD], &m_ColBack, m_GradCol);
	CCpuLoad::GetInstance()->DrawCpuLoad(&m_CPULoadPic, 12, 20);
}

/*************************************************************************/
void CPSApp::DrawTemperaturePic()
/*************************************************************************/
/*! @brief Draws a temperature diagram into #m_CPULoadPic.
**************************************************************************/
{
	GUI_WndFrameG(&m_CPULoadPic, m_Str[StringTable::TEMPERATURE], &m_ColBack, m_GradCol);
	CCpuLoad::GetInstance()->DrawTemperature(&m_CPULoadPic, 12, 20);
}

/*************************************************************************/
void CPSApp::DrawFrameRatePic()
/*************************************************************************/
/*! @brief Draws a frame rate graph into #m_CPULoadPic.
**************************************************************************/
{
	GUI_WndFrameG(&m_CPULoadPic, m_Str[StringTable::FRAMERATE], &m_ColBack, m_GradCol);
	CCpuLoad::GetInstance()->DrawFrame(&m_CPULoadPic, 12, 20);
}

/*************************************************************************/
void CPSApp::DrawCameraInfoPic()
/*************************************************************************/
/*! @brief Draws camera status line information into #m_CPULoadPic.
**************************************************************************/
{
	GUI_WndFrameG(&m_CPULoadPic, m_Str[StringTable::CAM_INFO], &m_ColBack, m_GradCol);
	GUI_TextOutFormat(&m_CPULoadPic, 8, 30, "Average: %4d", m_GMsg.AverageValue);
	GUI_TextOut(&m_CPULoadPic, 8, 50, "Missed Trigger - ");
	GUI_TextOutFormat(&m_CPULoadPic, 8, 66, "Counter: %4d", m_GMsg.MissedTrigger);
}

/*************************************************************************/
void CPSApp::DisplayGraph(CIMAGE *pImage)
/*************************************************************************/
/*! @brief Displays #m_CPULoadPic in the bottom right corner of image/display.

	The type of information which is displayed depends on #m_ShowGraph.
	@param pImage Pointer to the image the graph is drawn. If \p NULL it is
	drawn on TCP-Display
**************************************************************************/
{
	CDlgMain *pDlgMain = CDlgMain::GetInstance();

	switch (m_ShowGraph)
	{
	case GraphType::TEMPERATURE:
		DrawTemperaturePic();
		break;
	case GraphType::CPU_LOAD:
		DrawCPULoadPic();
		break;
	case GraphType::FPS:
		DrawFrameRatePic();
		break;
	case GraphType::INFO:
		if (CCamFeatures::GetInstance()->GetStatusLineValue() == 0)
		{
			m_ShowGraph--;
			pDlgMain->Refresh();
			pDlgMain->Show();
		}
		DrawCameraInfoPic();
		break;
	}
	if (pImage == NULL)
		pImage = m_pDisplayImage;

	if (pImage)
		GUI_CopyArea(&m_CPULoadPic, pImage, 0, 0,
					 m_CPULoadPic.Width, m_CPULoadPic.Height,
					 pImage->Width - m_CPULoadPic.Width,
					 pImage->Height -m_CPULoadPic.Height);
	else
		GUI_Display(&m_CPULoadPic, m_ScreenWidth-m_CPULoadPic.Width,
					 m_ScreenHeight-m_CPULoadPic.Height);
}

/*************************************************************************/
int CPSApp::SaveImage()
/*************************************************************************/
/*! @brief Saves camera image to SD card.

	The filename and format information are taken from #m_SI.
**************************************************************************/
{
	int result, FSize;
	const char *Filename = m_SI.Filename;
	
	if (m_pCamImage == NULL)
		return -1;

	if (VIB_ChDir(VIB_Drive[1], m_SI.Path))
	{
		CLog::Print(m_VicPsStrings->getValue("Error003", "Can not find path!\n"), Filename);
		return -1;
	}

	FSize = VIB_FindFile2(VIB_Drive[1], Filename);
	if (FSize == FAT_DIRECTORY)
	{
		VIB_ChDir(VIB_Drive[1], "..");
		CLog::Print(m_VicPsStrings->getValue("Error001", "SaveImage error: %s is a directory!\n"), Filename);
		return -1;
	}
	if (FSize >= 0)
	{
		char Text[64];

		std::snprintf(Text, 64, "\n%s\n%s\n\n", m_Str[StringTable::FILE_EXISTS], m_Str[StringTable::OVERWRITE]);
		result = MessageBox(Text, 1, "???");
		if (result == 1)
		{
			VIB_DeleteFile(VIB_Drive[1], Filename);
		}
		else
		{
			VIB_ChDir(VIB_Drive[1], "..");
			CLog::Print(m_VicPsStrings->getValue("Error002", "File exists. Not deleted!\n"), Filename);
			return -2;
		}
	}

	switch (m_SI.Format)
	{
	case ImageFormat::BMP:
		result = GUI_WriteYImage(VIB_Drive[1], Filename, m_pCamImage);
		break;
	case ImageFormat::PNG:
		result = PNG_WriteYImage(VIB_Drive[1], Filename, m_pCamImage);
		break;
	case ImageFormat::JPG:
		result = JPEG_WriteYImage(VIB_Drive[1], Filename, m_pCamImage);
		break;
	}

	if (result <= 0)
		CLog::Print("Error writing file!\n");
	else
		CLog::Print("File %s saved.\n", Filename);

	if (m_SI.Autonumber)
	{
		m_SI.Counter++;
		GenerateFilename();
		CDlgSaveImage::GetInstance()->SetDirty(true);
	}
	VIB_ChDir(VIB_Drive[1], "..");
	return 0;
}

/*************************************************************************/
int CPSApp::LoadImage(const char *pFilename)
/*************************************************************************/
/*! @brief Loads and displays image.
	@param pFilename Name of file to load and show
	@return 0: OK\n
	-1: File does not exist
**************************************************************************/
{
	CDlgFileMgr *pDlgFileMgr = CDlgFileMgr::GetInstance();
	int Ret;

	switch (m_SI.Format)
	{
	case ImageFormat::BMP:
		Ret = pDlgFileMgr->DisplayBMPFile(pFilename);
		break;
	case ImageFormat::PNG:
		Ret = pDlgFileMgr->DisplayPNGFile(pFilename);
		break;
	case ImageFormat::JPG:
		Ret = pDlgFileMgr->DisplayJPGFile(pFilename);
		break;
	}
	return Ret;
}

/*************************************************************************/
void CPSApp::RecBackground(bool Redraw)
/*************************************************************************/
/*! @brief Recovers background after a full screen dialog is closed.
	@param Redraw Redraw main dialog
**************************************************************************/
{
	CDlgMain *pDlgMain = CDlgMain::GetInstance();

	if (Redraw)
		m_bForceRedraw = Redraw;

	if (m_bForceRedraw)
	{
		InitBackground();
		CopyInfoArea();
		DisplayGraph(&m_Image);
		GUI_CopyArea(&m_CVImage, &m_Image, 0, 0,
					 m_CVImage.Width, m_CVImage.Height, m_DlgAreaWidth, 0);
		Redraw = pDlgMain->GetVisible();
		pDlgMain->SetVisible(1);
		pDlgMain->ForceRedraw();
		pDlgMain->Show(&m_Image);
		pDlgMain->SetVisible(Redraw);
		m_bForceRedraw = false;
		GUI_Display(&m_Image, 0, 0);
		if (m_pDisplayImage)
			GUI_Display(&m_Image, 0, 0);
	}
}

/******************************************************************************/
void CPSApp::GenerateFilename()
/******************************************************************************/
/*! @brief Generates a valid filename from \p m_SI.Path, \p m_SI.FilenamePrefix,
	\p m_SI.Counter and \p m_SI.Format
*******************************************************************************/
{
	const char *pExt;

	switch (m_SI.Format)
	{
		case ImageFormat::BMP: pExt = "BMP"; break;
		case ImageFormat::PNG: pExt = "PNG"; break;
		case ImageFormat::JPG: pExt = "JPG"; break;
		default: pExt = ""; break;
	}
	std::snprintf(m_SI.Filename, 12, "%s%03d.%s", m_SI.FilenamePrefix, m_SI.Counter, pExt);
	std::snprintf(m_SI.FullFilename, 256, "%s/%s", m_SI.Path, m_SI.Filename);
}

/******************************************************************************/
void CPSApp::PrepareHistogram()
/******************************************************************************/
/*! @brief Initializes histogram window.
*******************************************************************************/
{
	const int Index = (m_Info == InfoType::HISTOGRAM) ? StringTable::HISTOGRAM : StringTable::HISTOGRAM_LOG;

	GUI_WndFrameG(&m_HistImage, m_Str[Index], &m_ColBack, m_GradCol);
	GUI_GradientH(&m_HistImage, 30, 142, 511, 14, &rgbBlack, &rgbWhite);
	GUI_TextOut(&m_HistImage, 20, 141, "0");
	GUI_TextOut(&m_HistImage, 542, 141, "100%");
}

/******************************************************************************/
void CPSApp::ChangeInfoArea(GUI_DLG *pDlg)
/******************************************************************************/
/*! @brief Checks for mouse position and changes Camera, Graph and Info window.
	@param pDlg Pointer to active dialog window\n
	Used to get mouse coordinates.
*******************************************************************************/
{
	const int PosX = pDlg->PosX;
	const int PosY = pDlg->PosY;

	if (PosX > m_DlgAreaWidth &&
		PosY < (m_ScreenHeight - m_CPULoadPic.Height))
	{
		m_View++;
		if (m_View > ViewType::APP)
			m_View = ViewType::CAMERA;
	}
	else if (PosX > (m_ScreenWidth  - m_CPULoadPic.Width) &&
			 PosY > (m_ScreenHeight - m_CPULoadPic.Height))
	{
		m_ShowGraph++;
		if (m_ShowGraph > GraphType::INFO)
			m_ShowGraph = GraphType::CPU_LOAD;
		if (m_ShowGraph == GraphType::INFO && CCamFeatures::GetInstance()->GetStatusLineValue() == 0)
			m_ShowGraph = GraphType::CPU_LOAD;
		m_GraphUpdateCounter = GRAPH_UPDATE_CYCLE;
	}
	else if (PosX > m_DlgAreaWidth &&
			 PosY > (m_ScreenHeight - m_CPULoadPic.Height))
	{
		m_Info++;
		if (m_Info > InfoType::APP)
			m_Info = InfoType::LOGFILE;
		if (m_Info == InfoType::APP && FxnDisplayDlgAppInfo == NULL)
			m_Info = InfoType::LOGFILE;
	}
}

/******************************************************************************/
void CPSApp::ChangeGraphArea(GUI_DLG *pDlg)
/******************************************************************************/
/*! @brief Checks for mouse position and changes Graph window.
	@param pDlg Pointer to active dialog window\n
	Used to get mouse coordinates.
*******************************************************************************/
{
	if (pDlg->PosX > (m_ScreenWidth  - m_CPULoadPic.Width) &&
		pDlg->PosY > (m_ScreenHeight - m_CPULoadPic.Height))
	{
		m_ShowGraph++;
		if (m_ShowGraph > GraphType::INFO)
			m_ShowGraph = GraphType::CPU_LOAD;
		if (m_ShowGraph == GraphType::INFO && CCamFeatures::GetInstance()->GetStatusLineValue() == 0)
			m_ShowGraph = GraphType::CPU_LOAD;
		m_GraphUpdateCounter = GRAPH_UPDATE_CYCLE;
	}
}

/******************************************************************************/
void CPSApp::SetApplicationName(const char *pName)
/******************************************************************************/
/*! @brief Sets title of application window.
	@param pName Window title
*******************************************************************************/
{
	if (pName)
		VIB_SStrCpy(m_ApplicationName, pName, 32);
	else
		m_ApplicationName[0] = '\0';
}

/******************************************************************************/
void CPSApp::SetApplicationIcon(CIMAGE *pIcon, const char *pLabel)
/******************************************************************************/
/*! @brief Sets application icon.
	@param pIcon New application icon image
	@param pLabel New application icon label
*******************************************************************************/
{
	GUI_CopyArea(pIcon, &m_Icon[IconId::APP], 0, 0, m_Icon[IconId::APP].Width, m_Icon[IconId::APP].Height, 0, 0);
	CDlgMain::GetInstance()->SetAppIconLabel(pLabel);
}

/******************************************************************************/
void CPSApp::PrepareFullScreen()
/******************************************************************************/
/*! @brief Prepares full screen mode.
*******************************************************************************/
{
	m_FullScreen = 1;
	if (m_ScreenWidth > m_CamReg.m_Width || m_ScreenHeight > m_CamReg.m_Height)
	{
		InitBackground();
		GUI_Display(&m_Image, 0, 0);
		if (m_pDisplayImage)
			GUI_Display(&m_Image, 0, 0);
		CDlgTools::GetInstance()->SetVisible(0);
	}
}

/******************************************************************************/
int CPSApp::FullScreenMode(int nKey)
/******************************************************************************/
/*! @brief Camera image is shown in full screen mode.
	@param nKey Last pressed key.
*******************************************************************************/
{
	static int s_x = 0;
	static int s_y = 0;
	int Ret = 0;

	while (MBX_pend(m_MbxVis, &m_CMsg, 0)) ;
	while (MBX_pend(m_MbxCam, &m_GMsg, 0)) ;

	if (m_GMsg.Image.pyI == NULL)
		return -1;
	m_pCamImage = m_GMsg.Image.pyI; // Posted Y image from Task_PSGrab()

	s_x = _min2(s_x, m_pCamImage->Width-m_ScreenWidth);
	s_x = _max2(0, s_x);
	s_y = _min2(s_y, m_pCamImage->Height-m_ScreenHeight);
	s_y = _max2(0, s_y);
	GUI_DisplayAreaY(m_pCamImage, 0, 0, s_x, s_y, m_pCamImage->Width, m_pCamImage->Height);
	CCpuLoad::IncrementDisplayCounter();

	if (GUI_DlgGetCoord)
	{
		int txp, typ;

		Ret = GUI_DlgGetCoord(&txp, &typ);
	}

	switch (nKey)
	{
		case KB_LEFT:  s_x -= 20; break;
		case KB_RIGHT: s_x += 20; break;
		case KB_UP:    s_y -= 20; break;
		case KB_DOWN:  s_y += 20; break;
		case KB_HOME:  s_x = s_y = 0; break;
		case KB_PGUP:  s_y = 0; s_x = m_pCamImage->Width  - m_ScreenWidth; break;
		case KB_END:   s_x = 0; s_y = m_pCamImage->Height - m_ScreenHeight; break;
		case KB_PGDOWN:
			s_x = m_pCamImage->Width  - m_ScreenWidth;
			s_y = m_pCamImage->Height - m_ScreenHeight;
			break;
	}

	if (nKey == KB_ESC || Ret)
	{
		RecBackground(1);
		m_FullScreen = 0;
		m_DlgFocus = DialogId::NONE;
		m_DlgNewFocus = DialogId::TOOLS;
	}
	return 0;
}

/******************************************************************************/
void CPSApp::SetPVPos(int x, int y, double dScale)
/******************************************************************************/
/*! @brief Sets preview position.
	@param x x-position
	@param y y-position
	@param dScale Scale factor
*******************************************************************************/
{
	m_PVPosX = x;
	m_PVPosY = y;
	m_PVScale = dScale;
	m_pZoom->Set(dScale);
	UpdateScaleFactors();
}

/******************************************************************************/
int CPSApp::MessageBox(const char *pText, int YesNo, const char *pTitle) const
/******************************************************************************/
/*! @brief Shows Message-box.
	@param pText Text
	@param YesNo 0: OK box\n
	1: Yes/No box
	@param pTitle Title string
	@return Button number
*******************************************************************************/
{
	if (YesNo)
		return GUI_MessageBoxEx(m_pDisplayImage, m_fKB, pText, pTitle, m_Str[StringTable::YES], m_Str[StringTable::NO], NULL);
	else
		return GUI_MessageBoxEx(m_pDisplayImage, m_fKB, pText, pTitle, "OK", NULL, NULL);
}

/******************************************************************************/
int CPSApp::LoadPreset(const char *PresetName)
/******************************************************************************/
/*! @brief Loads Preset from INI file.
	@param PresetName Name of INI file
	@return Error code
*******************************************************************************/
{
	Config *pConf;
	char *pText;
	char FName[20];

	if (PresetName[0])
	{
		VIB_ChDir(VIB_Drive[1], "PRESET");
		std::snprintf(FName, 20, "%s.INI", PresetName);
	
		pConf = Config::loadFromIni(VIB_Drive[1], FName, "PRESET_INI");
		if (pConf)
		{
			pText = pConf->getValue("PresetName", FName);
			if (strcmp(pText, PresetName) == 0)
			{
				*(m_CurrentIni) += *(pConf);
				CLog::Print("Loading preset from %s.INI...\n", pText);
			}
			else
				CLog::Print("Error loading preset %s!\n", pText);
			delete pConf;
		}
		VIB_ChDir(VIB_Drive[1], "..");
		return 0;
	}
	return -1;
}

/******************************************************************************/
int CPSApp::GetBytesPerPixel() const
/******************************************************************************/
/*! @brief Returns bytes per pixel in camera image.
*******************************************************************************/
{
	switch (m_CamPixelFormat)
	{
	case CamMode::_1TAP_8BIT:	// For Frontend D1024-40
		return 1;
	case CamMode::_1TAP_10BIT:	// For Frontend D1024-40
	case CamMode::_1TAP_12BIT:
		return 2;
	case CamMode::_2TAP_8BIT:	// For Frontend D1024-80
		return 2;
	case CamMode::_2TAP_10BIT:	// For Frontend D1024-80
	case CamMode::_2TAP_12BIT:
		return 2;
	default:
		return 1;
	}
}

/******************************************************************************/
int CPSApp::ChangeIpAddr(const char *pIpAddr, const char *pSubnet)
/******************************************************************************/
/*! @brief Changes IP-Address in .INI-file.
	@param pIpAddr String with new IP-Address
	@param pSubnet String with new subnet address
	@return Error code
*******************************************************************************/
{
	if (m_EthernetIni == NULL)
		return -1;
	m_EthernetIni->setValue("IPAddress", pIpAddr);
	if (pSubnet[0])
		m_EthernetIni->setValue("IpSubnetMask", pSubnet);
	m_EthernetIni->saveToIni(VIB_Drive[1], "ethernet.ini");
	return 0;
}

/******************************************************************************/
const char *CPSApp::GetSubnetMask() const
/******************************************************************************/
/*! @brief Gets subnet mask from \p ETHERNET.INI file.
	@return Subnet mask string
*******************************************************************************/
{
	if (m_EthernetIni == NULL)
		return NULL;
	return (m_EthernetIni->getValue("IpSubnetMask", "255.255.255.0"));
}

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