/////////////////////////////////////////////////////////////////////////////
// Name:        BurnDlg.h
// Purpose:     Burn dialog
// Author:      Alex Thuering
// Created:     23.03.2003
// RCS-ID:      $Id: BurnDlg.cpp,v 1.34 2012/06/17 21:56:20 ntalex Exp $
// Copyright:   (c) Alex Thuering
// Licence:     GPL
/////////////////////////////////////////////////////////////////////////////

#include "BurnDlg.h"
#include "Config.h"
#include <wx/utils.h>
#include <wx/dir.h>
#include <wx/filename.h>
#include <wx/progdlg.h>
#include <wxVillaLib/utils.h>

#ifdef HAVE_LIBUDEV
extern "C" {
#include <libudev.h>
}
#elif defined(__WXMSW__)
#include <initguid.h>
#include <devguid.h>
#include <setupapi.h>
#include <ddk/ntdddisk.h>
#include <ddk/ntddscsi.h>
#include <ddk/cfgmgr32.h>
#endif

enum {
	TEMP_DIR_BT_ID = 2000,
	TEMP_DIR_FIELD_ID,
	PREVIEW_CHECK_ID,
	GENERATE_RADIO_BT_ID,
	ISO_RADIO_BT_ID,
	ISO_FILE_FIELD_ID,
	ISO_BT_ID,
	BURN_RADIO_BT_ID,
	RESET_BT_ID
};

BEGIN_EVENT_TABLE(BurnDlg, wxDialog)
    // begin wxGlade: BurnDlg::event_table
    EVT_TEXT(TEMP_DIR_FIELD_ID, BurnDlg::OnTempDirChange)
    EVT_BUTTON(TEMP_DIR_BT_ID, BurnDlg::OnChooseTempDir)
    EVT_RADIOBUTTON(GENERATE_RADIO_BT_ID, BurnDlg::OnRadioBt)
    EVT_RADIOBUTTON(ISO_RADIO_BT_ID, BurnDlg::OnRadioBt)
    EVT_TEXT(ISO_FILE_FIELD_ID, BurnDlg::OnTempDirChange)
    EVT_BUTTON(ISO_BT_ID, BurnDlg::OnChooseIsoFile)
    EVT_RADIOBUTTON(BURN_RADIO_BT_ID, BurnDlg::OnRadioBt)
    EVT_BUTTON(wxID_OK, BurnDlg::OnOk)
    EVT_BUTTON(RESET_BT_ID, BurnDlg::OnReset)
    // end wxGlade
    EVT_ACTIVATE(BurnDlg::OnActivate)
END_EVENT_TABLE()

BurnDlg::BurnDlg(wxWindow* parent, DVD* dvd, Cache* cache): wxDialog(parent, -1, wxEmptyString,
		wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) {
	m_dvd = dvd;
	m_cache = cache;
	
    // begin wxGlade: BurnDlg::BurnDlg
    m_tempDirLabel = new wxStaticText(this, wxID_ANY, _("Temp directory:"));
    m_tempDirText = new wxTextCtrl(this, TEMP_DIR_FIELD_ID, wxEmptyString);
    m_tempDirBt = new wxButton(this, TEMP_DIR_BT_ID, _("..."));
    m_freeSpaceTitle = new wxStaticText(this, wxID_ANY, _("Free:"));
    m_freeSpaceLabel = new wxStaticText(this, wxID_ANY, wxEmptyString);
    m_requiredSpaceTitle = new wxStaticText(this, wxID_ANY, _("Required:"));
    m_requiredSpaceLabel = new wxStaticText(this, wxID_ANY, wxEmptyString);
    m_previewCheck = new wxCheckBox(this, PREVIEW_CHECK_ID, _("preview"));
    m_generateRadioBt = new wxRadioButton(this, GENERATE_RADIO_BT_ID, _("just generate"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
    m_isoRadioBt = new wxRadioButton(this, ISO_RADIO_BT_ID, _("create iso image"));
    m_isoLabel = new wxStaticText(this, wxID_ANY, _("Save to:"));
    m_isoText = new wxTextCtrl(this, ISO_FILE_FIELD_ID, wxEmptyString);
    m_isoBt = new wxButton(this, ISO_BT_ID, _("..."));
    m_isoFreeSpaceTitle = new wxStaticText(this, wxID_ANY, _("Free:"));
    m_isoFreeSpaceLabel = new wxStaticText(this, wxID_ANY, wxEmptyString);
    m_isoRequiredSpaceTitle = new wxStaticText(this, wxID_ANY, _("Required:"));
    m_isoRequiredSpaceLabel = new wxStaticText(this, wxID_ANY, wxEmptyString);
    m_burnRadioBt = new wxRadioButton(this, BURN_RADIO_BT_ID, _("burn"));
    m_formatCheck = new wxCheckBox(this, wxID_ANY, _("format DVD-RW"));
    m_deviceLabel = new wxStaticText(this, wxID_ANY, _("Device:"));
    const wxString *m_deviceChoice_choices = NULL;
#if defined(HAVE_LIBUDEV) || defined(__WXMSW__) || defined(__WXMAC__)
    m_deviceChoice = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0, m_deviceChoice_choices,
    		wxCB_DROPDOWN|wxCB_READONLY);
#else
    m_deviceChoice = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0, m_deviceChoice_choices, wxCB_DROPDOWN);
#endif
    m_speedLabel = new wxStaticText(this, wxID_ANY, _("Speed:"));
    const wxString *m_speedChoice_choices = NULL;
    m_speedChoice = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0, m_speedChoice_choices, wxCB_DROPDOWN);
    m_devPanel = new wxPanel(this, wxID_ANY);
    m_addECCCheck = new wxCheckBox(this, wxID_ANY, _("add error correction data"));
    m_spacer = new wxPanel(this, wxID_ANY);
    m_startBt = new wxButton(this, wxID_OK, _("Start"));
    m_cancelBt = new wxButton(this, wxID_CANCEL, _("Cancel"));
    m_resetBt = new wxButton(this, RESET_BT_ID, _("Reset"));

    set_properties();
    do_layout();
    // end wxGlade
}

#ifdef HAVE_LIBUDEV
void EnumDevices(wxArrayString& devices, wxComboBox* deviceChoice) {
	udev* udev = udev_new();
	if (!udev) {
		wxLogError(wxT("Can't create udev"));
	} else {
		udev_enumerate* enumerate = udev_enumerate_new(udev);
		udev_enumerate_add_match_property(enumerate, "ID_CDROM_DVD", "1");
		udev_enumerate_scan_devices(enumerate);
		udev_list_entry* devs = udev_enumerate_get_list_entry(enumerate);
		udev_list_entry* dev_list_entry;
		udev_list_entry_foreach(dev_list_entry, devs) {
			udev_device* dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(dev_list_entry));
			wxString devNode(udev_device_get_devnode(dev), *wxConvFileName);
			wxString devModel(udev_device_get_property_value(dev, "ID_MODEL"), wxConvISO8859_1);
			devices.Add(devNode);
			deviceChoice->Append(devModel);
		}
	}
}
#elif defined(__WXMSW__)
const int BUFFER_SIZE = 512;

wxString GetDriveString(WCHAR* deviceId) {
	wxString result;
	int nLength = wcslen(deviceId);
	deviceId[nLength] = '\\';
	deviceId[nLength + 1] = 0;
	// get volume mount point for the device path.
	WCHAR volume[BUFFER_SIZE];
	if (GetVolumeNameForVolumeMountPoint(deviceId, volume, BUFFER_SIZE)) {
		TCHAR bufd[BUFFER_SIZE]; 
		TCHAR drive[] = TEXT("C:\\");
		for (TCHAR c = TEXT('C'); c < TEXT('Z');  c++) {
			drive[0] = c;
			if (GetVolumeNameForVolumeMountPoint(drive, bufd, BUFFER_SIZE) && !wcscmp(bufd, volume)) {
				result = wxString(drive, wxConvISO8859_1).Mid(0, 2);
				break;
			}
		}
	}
	deviceId[nLength] = 0;
	return result;
}

typedef struct _SCSI_PASS_THROUGH_WITH_BUFFERS {
	SCSI_PASS_THROUGH Spt;
	ULONG Filler; // realign buffers to double word boundary
	UCHAR SenseBuf[32];
	UCHAR DataBuf[512];
} SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS;

#define SCSIOP_MODE_SENSE 0x1A
#define MODE_PAGE_CAPABILITIES 0x2A

bool IsDvdWriter(HANDLE hDevice) {
	SCSI_PASS_THROUGH_WITH_BUFFERS sptwb;
	ZeroMemory(&sptwb,sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS));
	sptwb.Spt.Length = sizeof (SCSI_PASS_THROUGH);
	sptwb.Spt.PathId = 0;
	sptwb.Spt.TargetId = 1;
	sptwb.Spt.Lun = 0;
	sptwb.Spt.CdbLength = 6;
	sptwb.Spt.SenseInfoLength = 24;
	sptwb.Spt.DataIn = SCSI_IOCTL_DATA_IN;
	sptwb.Spt.DataTransferLength = 192;
	sptwb.Spt.TimeOutValue = 2;
	sptwb.Spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuf);
	sptwb.Spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseBuf);
	sptwb.Spt.Cdb[0] = SCSIOP_MODE_SENSE;
	sptwb.Spt.Cdb[1] = 0x08;
	sptwb.Spt.Cdb[2] = MODE_PAGE_CAPABILITIES;
	sptwb.Spt.Cdb[4] = 192;
	ULONG length = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuf) + sptwb.Spt.DataTransferLength;
	ULONG returned = 0;
	bool status = DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH, &sptwb,
			sizeof(SCSI_PASS_THROUGH), &sptwb, length, &returned, NULL);
	if (status && sptwb.Spt.ScsiStatus == 0) {
		return (sptwb.DataBuf[7] & 0x10) || (sptwb.DataBuf[7] & 0x20);
	}
	return true; // cannot check
}

wxString GetProductId(HANDLE hDevice) {
	STORAGE_PROPERTY_QUERY query;
	query.PropertyId = StorageAdapterProperty;
	query.QueryType = PropertyStandardQuery;
	UCHAR outBuf[512];
	ULONG returnedLength = 0;
	bool status = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(STORAGE_PROPERTY_QUERY),
			&outBuf, 512, &returnedLength, NULL);
	if (!status)
		return wxT("");
	query.PropertyId = StorageDeviceProperty;
	query.QueryType = PropertyStandardQuery;
	status = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(STORAGE_PROPERTY_QUERY),
			&outBuf, 512, &returnedLength, NULL);
	if (!status)
		return wxT("");
	PSTORAGE_DEVICE_DESCRIPTOR  devDesc = (PSTORAGE_DEVICE_DESCRIPTOR) outBuf;
	PUCHAR p = (PUCHAR) outBuf;
	if (devDesc->ProductIdOffset && p[devDesc->ProductIdOffset])
		return wxString((char*) (p + devDesc->ProductIdOffset), wxConvISO8859_1);
	return wxT("");
}

bool GetDeviceHandle(HDEVINFO IntDevInfo, int index, HANDLE& hDevice, wxString& drive) {
	hDevice = INVALID_HANDLE_VALUE;

	SP_DEVICE_INTERFACE_DATA interfaceData;
	interfaceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);
	if (!SetupDiEnumDeviceInterfaces(IntDevInfo, 0, (LPGUID)(&GUID_DEVINTERFACE_CDROM), index, &interfaceData))
		return false;
	
	DWORD reqSize;
	if (!SetupDiGetDeviceInterfaceDetail(IntDevInfo, &interfaceData, NULL, 0, &reqSize, NULL)
			&& GetLastError() != ERROR_INSUFFICIENT_BUFFER)
		return false;
	
	DWORD iddSize = reqSize;
	PSP_DEVICE_INTERFACE_DETAIL_DATA interfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(iddSize);
	if (interfaceDetailData == NULL)
		return false;
	
	bool result = true;
	interfaceDetailData->cbSize = sizeof (SP_INTERFACE_DEVICE_DETAIL_DATA);
	if (SetupDiGetDeviceInterfaceDetail(IntDevInfo, &interfaceData, interfaceDetailData, iddSize, &reqSize, NULL)) {
		drive = GetDriveString(interfaceDetailData->DevicePath);
		if (drive.length() > 0)
			hDevice = CreateFile(interfaceDetailData->DevicePath, GENERIC_READ|GENERIC_WRITE,
					FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
	} else
		result = false;
	
	free(interfaceDetailData);
	return result;
}

void EnumDevices(wxArrayString& devices, wxComboBox* deviceChoice) {
	HDEVINFO hDevInfo = SetupDiGetClassDevs((LPGUID) &GUID_DEVCLASS_CDROM, NULL, NULL, DIGCF_PRESENT);
	if (hDevInfo == INVALID_HANDLE_VALUE) {
		wxLogError(wxT("SetupDiGetClassDevs(GUID_DEVCLASS_CDROM) failed with error: %d"), GetLastError());
		return;
	}

	HDEVINFO hIntDevInfo = SetupDiGetClassDevs((LPGUID) &GUID_DEVINTERFACE_CDROM, NULL, NULL,
			(DIGCF_PRESENT | DIGCF_INTERFACEDEVICE));
	if (hIntDevInfo == INVALID_HANDLE_VALUE) {
		wxLogError(wxT("SetupDiGetClassDevs(GUID_DEVINTERFACE_CDROM) failed with error: %d"), GetLastError());
		return;
	}

	// enumerate all the DVD devices
	int index = 0;
	while (true) {
		HANDLE hDevice;
		wxString drive;
		if (!GetDeviceHandle(hIntDevInfo, index++, hDevice, drive))
			break;
		if (drive.length() == 0)
			continue;
		
		if (hDevice != INVALID_HANDLE_VALUE) {
			wxString productId = GetProductId(hDevice);
			if (IsDvdWriter(hDevice)) {
				devices.Add(drive);
				deviceChoice->Append(drive + wxT(" ") + productId);
			}
			CloseHandle(hDevice);
		} else {
			devices.Add(drive);
			deviceChoice->Append(drive);
		}
	}
	SetupDiDestroyDeviceInfoList(hDevInfo);
	SetupDiDestroyDeviceInfoList(hIntDevInfo);
}
#elif defined(__WXMAC__)
extern "C" void EnumDevices(wxArrayString& devices, wxComboBox* deviceChoice);
#else
void EnumDevices(wxArrayString& devices, wxComboBox* deviceChoice) {
	wxArrayString allDevices;
	wxDir d(wxT("/dev"));
	wxString fname;
	if (d.GetFirst(&fname, wxT("dvd*"), wxDIR_FILES))
		do { allDevices.Add(wxT("/dev/") + fname); } while (d.GetNext(&fname));
	if (d.GetFirst(&fname, wxT("cdrom*"), wxDIR_FILES))
		do { allDevices.Add(wxT("/dev/") + fname); } while (d.GetNext(&fname));
	if (d.GetFirst(&fname, wxT("sg*"), wxDIR_FILES))
		do { allDevices.Add(wxT("/dev/") + fname); } while (d.GetNext(&fname));
	allDevices.Sort();
	// get device name
	wxString scanCmd = s_config.GetBurnScanCmd();
	if (scanCmd.length()) {
		wxProgressDialog* pdlg = new wxProgressDialog(_("Scan devices"), _("Please wait..."),
				allDevices.GetCount() - 1, deviceChoice->GetParent(), wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
		pdlg->Show();
		pdlg->SetFocus();
		for (unsigned int i = 0; i < allDevices.GetCount(); i++) {
			wxString cmd = scanCmd;
			cmd.Replace(wxT("$DEVICE"), allDevices[i]);
			wxArrayString output;
			wxExecute(cmd, output, wxEXEC_SYNC | wxEXEC_NODISABLE);
			if (output.Count() > 0 && output[0].length() > 7 && output[0].SubString(0, 7) == wxT("INQUIRY:")) {
				wxString dev = output[0].AfterFirst(wxT('[')).BeforeLast(wxT(']'));
				dev = allDevices[i] + wxT(" ") + dev.BeforeFirst(wxT(']')).Trim() + wxT(" ")
						+ dev.AfterFirst(wxT('[')).BeforeFirst(wxT(']')).Trim();
				devices.Add(allDevices[i]);
				deviceChoice->Append(dev);
			}
			if (!pdlg->Update(i))
				break;
		}
		pdlg->Hide();
	}
	if (devices.Count() == 0) {
		for (unsigned int i=0; i<allDevices.GetCount(); i++) {
			devices.Add(allDevices[i]);
			deviceChoice->Append(allDevices[i]);
		}
	}
}
#endif //!HAVE_LIBUDEV && !__WXMSW__

void BurnDlg::set_properties() {
    // begin wxGlade: BurnDlg::set_properties
    SetTitle(_("Burn"));
    m_tempDirBt->SetMinSize(wxSize(21, 21));
    m_isoBt->SetMinSize(wxSize(21, 21));
    m_startBt->SetDefault();
    // end wxGlade
    
	m_tempDirText->SetFocus();
	
	int h = m_tempDirText->GetSize().GetHeight() > 21 ? m_tempDirText->GetSize().GetHeight() : 21;
	m_tempDirBt->SetSizeHints(h, h, h, h);
	m_isoBt->SetSizeHints(h, h, h, h);
	
	// set devices
	EnumDevices(m_devices, m_deviceChoice);
	
	m_speedChoice->Append(_T("auto"));
	for (int speed = 1; speed <= 8; speed = speed * 2)
		m_speedChoice->Append(wxString::Format(_T("%dx"), speed));
	
	// check if dvdisaster is installed
	wxString cmd = s_config.GetAddECCCmd();
	if (cmd.length() > 0)
		cmd = cmd[0] == wxT('"') ? cmd.Mid(0, cmd.Mid(1).Find(wxT('"')) + 2) : cmd.BeforeFirst(wxT(' '));
	m_eccCheckEnabled = false;
	if (cmd.length() > 0) {
		wxLogNull log;
		wxArrayString output;
		if (wxExecute(cmd + wxT(" --version"), output, wxEXEC_SYNC | wxEXEC_NODISABLE) == 0)
			m_eccCheckEnabled = true;
	}
	
	UpdateCtrls();
}

void BurnDlg::do_layout() {
    // begin wxGlade: BurnDlg::do_layout
    wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* btSizer = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* panelSizer = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* deviceSizer = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* formatSizer = new wxBoxSizer(wxHORIZONTAL);
    wxFlexGridSizer* isoGrid = new wxFlexGridSizer(2, 3, 0, 0);
    wxBoxSizer* isoFreeSpaceSizer = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* isoSizer = new wxBoxSizer(wxHORIZONTAL);
    wxFlexGridSizer* tempDirGrid = new wxFlexGridSizer(2, 2, 0, 0);
    wxBoxSizer* freeSpaceSizer = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* tempDirSizer = new wxBoxSizer(wxHORIZONTAL);
    tempDirGrid->Add(m_tempDirLabel, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 4);
    tempDirSizer->Add(m_tempDirText, 1, 0, 0);
    tempDirSizer->Add(m_tempDirBt, 0, wxLEFT|wxRIGHT|wxALIGN_CENTER_VERTICAL, 4);
    tempDirGrid->Add(tempDirSizer, 1, wxTOP|wxBOTTOM|wxEXPAND, 4);
    tempDirGrid->Add(10, 10, 0, 0, 0);
    freeSpaceSizer->Add(m_freeSpaceTitle, 0, wxRIGHT, 4);
    freeSpaceSizer->Add(m_freeSpaceLabel, 0, 0, 0);
    freeSpaceSizer->Add(16, 16, 0, 0, 0);
    freeSpaceSizer->Add(m_requiredSpaceTitle, 0, wxRIGHT, 4);
    freeSpaceSizer->Add(m_requiredSpaceLabel, 0, 0, 0);
    tempDirGrid->Add(freeSpaceSizer, 1, wxEXPAND, 0);
    tempDirGrid->AddGrowableCol(1);
    panelSizer->Add(tempDirGrid, 1, wxEXPAND, 0);
    panelSizer->Add(m_previewCheck, 0, wxBOTTOM, 6);
    panelSizer->Add(m_generateRadioBt, 0, 0, 0);
    panelSizer->Add(m_isoRadioBt, 0, 0, 0);
    isoGrid->Add(16, 16, 0, 0, 0);
    isoGrid->Add(m_isoLabel, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 4);
    isoSizer->Add(m_isoText, 1, 0, 0);
    isoSizer->Add(4, 16, 0, 0, 0);
    isoSizer->Add(m_isoBt, 0, wxALIGN_CENTER_VERTICAL, 0);
    isoGrid->Add(isoSizer, 1, wxTOP|wxBOTTOM|wxEXPAND, 4);
    isoGrid->Add(16, 16, 0, 0, 0);
    isoGrid->Add(16, 16, 0, 0, 0);
    isoFreeSpaceSizer->Add(m_isoFreeSpaceTitle, 0, wxRIGHT, 4);
    isoFreeSpaceSizer->Add(m_isoFreeSpaceLabel, 0, 0, 0);
    isoFreeSpaceSizer->Add(16, 16, 0, 0, 0);
    isoFreeSpaceSizer->Add(m_isoRequiredSpaceTitle, 0, wxRIGHT, 4);
    isoFreeSpaceSizer->Add(m_isoRequiredSpaceLabel, 0, 0, 0);
    isoGrid->Add(isoFreeSpaceSizer, 1, wxEXPAND, 0);
    isoGrid->AddGrowableCol(2);
    panelSizer->Add(isoGrid, 1, wxEXPAND, 0);
    panelSizer->Add(m_burnRadioBt, 0, 0, 0);
    formatSizer->Add(16, 8, 0, 0, 0);
    formatSizer->Add(m_formatCheck, 0, wxEXPAND, 0);
    panelSizer->Add(formatSizer, 1, wxEXPAND, 0);
    deviceSizer->Add(16, 8, 0, 0, 0);
    deviceSizer->Add(m_deviceLabel, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 4);
    deviceSizer->Add(m_deviceChoice, 0, wxALIGN_CENTER_VERTICAL, 0);
    deviceSizer->Add(12, 8, 0, 0, 0);
    deviceSizer->Add(m_speedLabel, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 12);
    deviceSizer->Add(m_speedChoice, 0, wxALIGN_CENTER_VERTICAL, 0);
    deviceSizer->Add(m_devPanel, 1, wxEXPAND, 0);
    panelSizer->Add(deviceSizer, 1, wxBOTTOM|wxEXPAND, 4);
    panelSizer->Add(m_addECCCheck, 0, 0, 0);
    mainSizer->Add(panelSizer, 0, wxALL|wxEXPAND, 6);
    mainSizer->Add(m_spacer, 1, wxEXPAND, 0);
    btSizer->Add(50, 20, 1, wxEXPAND, 0);
    btSizer->Add(m_startBt, 0, wxALIGN_CENTER_VERTICAL, 0);
    btSizer->Add(10, 20, 0, 0, 0);
    btSizer->Add(m_cancelBt, 0, wxALIGN_CENTER_VERTICAL, 0);
    btSizer->Add(10, 20, 0, 0, 0);
    btSizer->Add(m_resetBt, 0, wxALIGN_CENTER_VERTICAL, 0);
    btSizer->Add(50, 20, 1, wxEXPAND, 0);
    mainSizer->Add(btSizer, 0, wxLEFT|wxRIGHT|wxBOTTOM|wxEXPAND|wxALIGN_BOTTOM, 10);
    SetSizer(mainSizer);
    mainSizer->Fit(this);
    Layout();
    Centre();
    // end wxGlade
}

wxString BurnDlg::GetDevice() {
	if (m_devices.Count() && m_deviceChoice->GetSelection() >= 0)
		return m_devices[m_deviceChoice->GetSelection()];
	else
		return m_deviceChoice->GetValue();
}

void BurnDlg::SetDevice(wxString device) {
	if (m_devices.Count()) {
		int n = device.length() > 0 ? m_devices.Index(device) : 0;
		if (n >= 0) {
			m_deviceChoice->SetSelection(n);
		} else {
#ifdef HAVE_LIBUDEV
			m_deviceChoice->SetSelection(0);
#else
			m_deviceChoice->SetValue(device);
#endif
		}
	} else
		m_deviceChoice->SetValue(device);
}

int BurnDlg::GetSpeed() {
	long speed = 0;
	if (m_speedChoice->GetSelection() > 0)
		m_speedChoice->GetStringSelection().BeforeFirst(wxT('x')).ToLong(&speed);
	return speed;
}

void BurnDlg::SetSpeed(int speed) {
	if (speed == 0)
		m_speedChoice->SetSelection(0);
	else
		m_speedChoice->SetStringSelection(wxString::Format(_T("%dx"), speed));
}

void BurnDlg::UpdateCtrls(bool def) {
	m_tempDirText->SetValue(s_config.GetTempDir(def));
	UpdateSpaceLabels();
	m_previewCheck->SetValue(s_config.GetPreviewDo(def));
	m_generateRadioBt->SetValue(s_config.GetGenerateDo(def));
	m_isoRadioBt->SetValue(s_config.GetIsoDo(def));
	wxString fname = wxT("dvd");
	if (m_dvd->GetFilename().length())
		wxFileName::SplitPath(m_dvd->GetFilename(), NULL, &fname, NULL);
	m_isoText->SetValue(m_dvd->GetIsoFile().length() > 0 ? m_dvd->GetIsoFile()
			: wxPathOnly(s_config.GetIsoSaveTo(def)) + wxFILE_SEP_PATH + fname + wxT(".iso"));
	m_addECCCheck->SetValue(s_config.GetAddECCDo(def));
	m_burnRadioBt->SetValue(s_config.GetBurnDo(def));
	m_formatCheck->SetValue(s_config.GetFormatDo(def));
	SetDevice(s_config.GetBurnDevice(def));
	SetSpeed(s_config.GetBurnSpeed(def));
#if defined(__WXMSW__) || defined(__WXMAC__)
	bool hasDevices = m_deviceChoice->GetCount() > 0;
#else
	bool hasDevices = true;
#endif
	if (!hasDevices) {
 		if (m_burnRadioBt->GetValue())
 			m_isoRadioBt->SetValue(true);
 		m_burnRadioBt->Enable(false);
	}
	wxCommandEvent evt;
	OnRadioBt(evt);
}

bool BurnDlg::UpdateSpaceLabels(bool showErrors) {
	// required space
	double requiredSpace = ((double)m_dvd->GetRequiredSize(m_cache))/1024/1024;
	if (requiredSpace < 0.1)
		requiredSpace = 0.1;
	wxString text = wxString::Format(wxT("%.1f "), requiredSpace) + _("GB");
	m_requiredSpaceLabel->SetLabel(text);
	
	// free space
	wxString dir = m_tempDirText->GetValue();
	if (!wxDirExists(dir)) // get parent dir 
		dir = wxFileName(dir).GetPath();
	double freeSpace = 0;
	text = _("N/A");
	if (wxDirExists(dir)) {
		wxDiskspaceSize_t pFree;
		wxGetDiskSpace(dir, NULL, &pFree);
		freeSpace = pFree.ToDouble() / 1024 / 1024 / 1024;
		text = wxString::Format(wxT("%.1f "), freeSpace) + _("GB");
	}
	m_freeSpaceLabel->SetLabel(text);
	m_freeSpaceLabel->SetForegroundColour(freeSpace > requiredSpace ? wxColour(0, 128, 0) : *wxRED);
	
	// requrired space for ISO image
	double isoRequiredSpace = ((double)m_dvd->GetSize())/1024/1024;
	if (isoRequiredSpace < 0.1)
		isoRequiredSpace = 0.1;
	text = wxString::Format(wxT("%.1f "), isoRequiredSpace) + _("GB");
	m_isoRequiredSpaceLabel->SetLabel(text);
	
	// free space for ISO image
	dir = wxFileName(m_isoText->GetValue()).GetPath();
	double isoFreeSpace = 0;
	text = _("N/A");
	if (wxDirExists(dir)) {
		wxDiskspaceSize_t pFree;
		wxGetDiskSpace(dir, NULL, &pFree);
		isoFreeSpace = pFree.ToDouble() / 1024 / 1024 / 1024;
		text = wxString::Format(wxT("%.1f "), isoFreeSpace) + _("GB");
	}
	m_isoFreeSpaceLabel->SetLabel(text);
	m_isoFreeSpaceLabel->SetForegroundColour(isoFreeSpace > isoRequiredSpace ? wxColour(0, 128, 0) : *wxRED);
	Layout();
	
	bool hasFreeSpace = freeSpace > requiredSpace;
	if (DoCreateIso()) {
		// check if there is enough space for ISO
		if (freeSpace == isoFreeSpace) { // ISO will be saved on the same disk/partition
			// cache can be deleted after generation of dvd structure => max space = dvd * 2 or cache + dvd
			double totalRequired = requiredSpace > isoRequiredSpace * 2 ? requiredSpace : isoRequiredSpace * 2;
			m_freeSpaceLabel->SetForegroundColour(freeSpace > totalRequired ? wxColour(0, 128, 0) : *wxRED);
			m_isoFreeSpaceLabel->SetForegroundColour(freeSpace > totalRequired ? wxColour(0, 128, 0) : *wxRED);
			hasFreeSpace = freeSpace > totalRequired;
		} else { // ISO will be saved on another disk/partition
			if (showErrors && hasFreeSpace && isoFreeSpace <= isoRequiredSpace) {
				wxMessageBox(_("There is not enough space to store ISO file."), _("Burn"),
						wxOK|wxICON_ERROR, this);
				return false;
			}
		}
	}
	if (showErrors && !hasFreeSpace)
		wxMessageBox(_("There is not enough space on temporary directory."), _("Burn"),
				wxOK|wxICON_ERROR, this);
	return hasFreeSpace;
}

void BurnDlg::OnChooseTempDir(wxCommandEvent& event) {
	wxDirDialog dlg(this, _("Choose a directory"), GetTempDir(), wxDD_NEW_DIR_BUTTON);
	if (dlg.ShowModal() != wxID_OK)
		return;
	m_tempDirText->SetValue(dlg.GetPath() + wxFILE_SEP_PATH);
}

void BurnDlg::OnRadioBt(wxCommandEvent& event) {
	UpdateSpaceLabels();
	m_isoLabel->Enable(DoCreateIso());
	m_isoText->Enable(DoCreateIso());
	m_isoBt->Enable(DoCreateIso());
	m_isoFreeSpaceTitle->Enable(DoCreateIso());
	m_isoFreeSpaceLabel->Enable(DoCreateIso());
	m_isoRequiredSpaceTitle->Enable(DoCreateIso());
	m_isoRequiredSpaceLabel->Enable(DoCreateIso());
	m_formatCheck->Enable(DoBurn());
	m_deviceLabel->Enable(DoBurn());
	m_deviceChoice->Enable(DoBurn());
	m_speedLabel->Enable(DoBurn());
	m_speedChoice->Enable(DoBurn());
	m_addECCCheck->Enable(m_eccCheckEnabled && (DoCreateIso() || DoBurn()));
}

void BurnDlg::OnChooseIsoFile(wxCommandEvent& event) {
	wxFileDialog dlg(this, _("Choose a file to save iso image"),
			wxPathOnly(GetIsoFile()), _T("dvd.iso"), _T("*.iso"), wxFD_SAVE);
	if (dlg.ShowModal() != wxID_OK)
		return;
	m_isoText->SetValue(dlg.GetPath());
}

void BurnDlg::OnActivate(wxActivateEvent &event) {
	UpdateSpaceLabels();
}

void BurnDlg::OnTempDirChange(wxCommandEvent &event) {
	UpdateSpaceLabels();
}
// wxGlade: add BurnDlg event handlers

void BurnDlg::OnReset(wxCommandEvent& event) {
	UpdateCtrls(true);
}

void BurnDlg::OnOk(wxCommandEvent& event) {
	if (m_isoRadioBt->GetValue()) {
		wxString isoFile = GetIsoFile().Trim().Trim(false);
		if (isoFile.length() == 0) {
			wxMessageBox(_("Please enter file name to save ISO"), _("Burn"), wxOK|wxICON_ERROR, this);
			return;
		}
		if (wxFileExists(isoFile) && wxMessageBox(wxString::Format(
				_("File '%s' already exist. Do you want to overwrite it?"), isoFile.c_str()),
				_("Burn"), wxYES_NO|wxICON_QUESTION, this) == wxNO)
			return;
	}
	if (m_burnRadioBt->GetValue()) {
		wxString device = GetDevice().Trim().Trim(false);
		if (device.length() == 0) {
			wxMessageBox(_("Please enter device name"), _("Burn"), wxOK|wxICON_ERROR, this);
			return;
		}
#ifdef __WXMSW__
		if (device.Find(wxT(':')) != 1 || device.length()> 3
				|| (device.length() == 3 && device.GetChar(2) != wxT('\\'))) {
			wxMessageBox(_("Invalid device name"), _("Burn"), wxOK|wxICON_ERROR, this);
			return;
		}
		SetDevice(device.SubString(0,1).MakeUpper());
#endif
	}
	if (!UpdateSpaceLabels(true)) {
		return;
	}
	wxString tmpDir = m_tempDirText->GetValue();
	if (m_cache->IsInitialized() && m_cache->GetTempDir() != tmpDir && m_cache->GetCount() > 0
			&& wxMessageBox(wxString::Format(
					_("There are %d transcoded file(s) in the cache. They\nwill be removed if you change temporary directory."), m_cache->GetCount()),
					_("Burn"), wxOK|wxCANCEL|wxICON_INFORMATION, this) == wxCANCEL)
		return;
	if (!wxDirExists(tmpDir) && !wxMkdir(tmpDir)) {
		wxMessageBox(wxString::Format(_("Can't create directory '%s'"), tmpDir.c_str()), _("Burn"),
				wxOK|wxICON_ERROR, this);
		return;
	}
	if (!m_cache->SetTempDir(tmpDir))
		return;
	s_config.SetTempDir(tmpDir);
	s_config.SetPreviewDo(m_previewCheck->GetValue());
	s_config.SetGenerateDo(m_generateRadioBt->GetValue());
	s_config.SetIsoDo(m_isoRadioBt->GetValue());
	if (m_isoRadioBt->GetValue()) {
		m_dvd->SetIsoFile(GetIsoFile());
		s_config.SetIsoSaveTo(wxPathOnly(GetIsoFile()) + wxFILE_SEP_PATH);
	}
	s_config.SetAddECCDo(m_addECCCheck->GetValue());
	s_config.SetBurnDo(m_burnRadioBt->GetValue());
	s_config.SetFormatDo(m_formatCheck->GetValue());
	s_config.SetBurnDevice(GetDevice());
	s_config.SetBurnSpeed(GetSpeed());
	s_config.Flush();
	EndModal(wxID_OK);
}
