
// CadConvertAppDlg.cpp : implementation file
//

#include "stdafx.h"
#include "CadConvertApp.h"
#include "CadConvertAppDlg.h"
#include "afxdialogex.h"
#include "CadFileFormat.h"
#include <CadConvert/CadConvert.h>
#include "string_format.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CAboutDlg dialog used for App About

class CAboutDlg : public CDialogEx
{
public:
  CAboutDlg();

  // Dialog Data
#ifdef AFX_DESIGN_TIME
  enum { IDD = IDD_ABOUTBOX };
#endif

protected:
  virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
  DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
  CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CCadConvertAppDlg dialog



CCadConvertAppDlg::CCadConvertAppDlg(CWnd* pParent /*=NULL*/)
  : CDialogEx(IDD_CADCONVERTAPP_DIALOG, pParent)
  , _inputFilename(_T(""))
  , _outputFilename(_T(""))
{
  m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CCadConvertAppDlg::DoDataExchange(CDataExchange* pDX)
{
  CDialogEx::DoDataExchange(pDX);
  DDX_Text(pDX, IDC_INPUT_FILENAME, _inputFilename);
  DDX_Text(pDX, IDC_OUTPUT_FILENAME, _outputFilename);
  DDX_Control(pDX, IDC_MESSAGES, _messagesEdit);
}

BEGIN_MESSAGE_MAP(CCadConvertAppDlg, CDialogEx)
  ON_WM_SYSCOMMAND()
  ON_WM_PAINT()
  ON_WM_QUERYDRAGICON()
  ON_BN_CLICKED(IDC_BROWSE_INPUT, &CCadConvertAppDlg::OnBnClickedBrowseInput)
  ON_BN_CLICKED(IDC_BROWSE_OUTPUT, &CCadConvertAppDlg::OnBnClickedBrowseOutput)
  ON_BN_CLICKED(IDC_CONVERT, &CCadConvertAppDlg::OnClickedConvert)
  ON_EN_CHANGE(IDC_INPUT_FILENAME, &CCadConvertAppDlg::OnEnChangeInputFilename)
  ON_EN_CHANGE(IDC_OUTPUT_FILENAME, &CCadConvertAppDlg::OnEnChangeOutputFilename)
END_MESSAGE_MAP()


// CCadConvertAppDlg message handlers

BOOL CCadConvertAppDlg::OnInitDialog()
{
  CDialogEx::OnInitDialog();

  // Add "About..." menu item to system menu.

  // IDM_ABOUTBOX must be in the system command range.
  ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  ASSERT(IDM_ABOUTBOX < 0xF000);

  CMenu* pSysMenu = GetSystemMenu(FALSE);
  if (pSysMenu != NULL)
  {
    BOOL bNameValid;
    CString strAboutMenu;
    bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
    ASSERT(bNameValid);
    if (!strAboutMenu.IsEmpty())
    {
      pSysMenu->AppendMenu(MF_SEPARATOR);
      pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
    }
  }

  // Set the icon for this dialog.  The framework does this automatically
  //  when the application's main window is not a dialog
  SetIcon(m_hIcon, TRUE);			// Set big icon
  SetIcon(m_hIcon, FALSE);		// Set small icon

  CProgressCtrl *pProgressCtrl = (CProgressCtrl *)GetDlgItem(IDC_PROGRESS);
  if (pProgressCtrl != nullptr)
  {
    pProgressCtrl->SetRange32(0, ProgressControlRange);
  }

  EnableDisableControls();

  return TRUE;  // return TRUE  unless you set the focus to a control
}

void CCadConvertAppDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
  if ((nID & 0xFFF0) == IDM_ABOUTBOX)
  {
    CAboutDlg dlgAbout;
    dlgAbout.DoModal();
  }
  else
  {
    CDialogEx::OnSysCommand(nID, lParam);
  }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CCadConvertAppDlg::OnPaint()
{
  if (IsIconic())
  {
    CPaintDC dc(this); // device context for painting

    SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

    // Center icon in client rectangle
    int cxIcon = GetSystemMetrics(SM_CXICON);
    int cyIcon = GetSystemMetrics(SM_CYICON);
    CRect rect;
    GetClientRect(&rect);
    int x = (rect.Width() - cxIcon + 1) / 2;
    int y = (rect.Height() - cyIcon + 1) / 2;

    // Draw the icon
    dc.DrawIcon(x, y, m_hIcon);
  }
  else
  {
    CDialogEx::OnPaint();
  }
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CCadConvertAppDlg::OnQueryDragIcon()
{
  return static_cast<HCURSOR>(m_hIcon);
}

void CCadConvertAppDlg::OnBnClickedBrowseInput()
{
  std::wstring fileFilter;
  for (int fileFormat = 0; fileFormat < NUM_FILE_FORMATS; fileFormat++)
  {
    AddFileFormatFilter(fileFilter, static_cast<CadFileFormat>(fileFormat));
  }

  // add all types filter
  static const wchar_t *pAllFilesFilter = L"All Files (*.*)|*.*||";
  fileFilter += pAllFilesFilter;

  CFileDialog fileDialog(TRUE, NULL, NULL, OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_ENABLESIZING, fileFilter.c_str());
  OPENFILENAME &ofn = fileDialog.GetOFN();
  // default to All Files filter
  ofn.nFilterIndex = NUM_FILE_FORMATS + 1;
  if (fileDialog.DoModal() == IDOK)
  {
    _inputFilename = fileDialog.GetPathName();

    wchar_t drive[_MAX_DRIVE];
    wchar_t dir[_MAX_DIR];
    wchar_t fname[_MAX_FNAME];
    wchar_t ext[_MAX_EXT];
    _wsplitpath_s(_inputFilename, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT);
    _outputFilename = CString(drive) + dir + fname + L".igs";

    UpdateData(FALSE);
    EnableDisableControls();
  }
}

void CCadConvertAppDlg::OnBnClickedBrowseOutput()
{
  std::wstring fileFilter;
  for (int fileFormat = 0; fileFormat < NUM_FILE_FORMATS; fileFormat++)
  {
    AddFileFormatFilter(fileFilter, static_cast<CadFileFormat>(fileFormat));
  }

  // add all types filter
  static const wchar_t *pAllFilesFilter = L"All Files (*.*)|*.*||";
  fileFilter += pAllFilesFilter;

  CFileDialog fileDialog(FALSE, NULL, NULL, OFN_PATHMUSTEXIST | OFN_ENABLESIZING, fileFilter.c_str());
  OPENFILENAME &ofn = fileDialog.GetOFN();
  // default to All Files filter
  ofn.nFilterIndex = NUM_FILE_FORMATS + 1;
  if (fileDialog.DoModal() == IDOK)
  {
    _outputFilename = fileDialog.GetPathName();
    UpdateData(FALSE);
  }
}

bool CCadConvertAppDlg::ProcessingCallback(double percentComplete, const wchar_t* pMessage, void* pData)
{
  if (pData == nullptr)
  {
    return true;
  }
  CCadConvertAppDlg *pCadConvertAppDlg = reinterpret_cast<CCadConvertAppDlg*>(pData);
  if (pCadConvertAppDlg->_showOutput == true && pMessage != nullptr)
  {
    pCadConvertAppDlg->AddMessage(pMessage);
  }
  pCadConvertAppDlg->UpdateProgress(percentComplete);
  return true;
}

void CCadConvertAppDlg::UpdateProgress(double percentComplete)
{
  CProgressCtrl *pProgressCtrl = (CProgressCtrl *)GetDlgItem(IDC_PROGRESS);
  if (pProgressCtrl != nullptr)
  {
    int currentPos = pProgressCtrl->GetPos();
    int pos = int(ProgressControlRange * percentComplete / 100.0 + 0.00001);
    if (pos != currentPos)
    {
      pProgressCtrl->SetPos(pos);
    }
  }
}

void CCadConvertAppDlg::OnClickedConvert()
{
  CWaitCursor waitCursor;

  CButton *pButton = static_cast<CButton*>(GetDlgItem(IDC_SHOW_OUTPUT));
  _showOutput = (pButton->GetCheck() ? true : false);
  UpdateData(TRUE);
  std::wstring message;
  string_format(message, L"Converting '%s' to '%s'...", _inputFilename, _outputFilename);
  AddMessage(message.c_str());

  std::chrono::time_point<std::chrono::system_clock> start = std::chrono::system_clock::now();

  CadConvert::setProcessingCallback(ProcessingCallback, this);
  CadConvert::Result result = CadConvert::convertCadFile(_inputFilename, _outputFilename);

  std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::now();

  if (result == CadConvert::Result::Success)
  {
    AddMessage(L"Succeeded");
  }
  else
  {
    AddMessage(L"Failed");
  }

  std::chrono::duration<double> elapsed_seconds = end - start;
  string_format(message, L"Elapsed time: %.2lf seconds", elapsed_seconds.count());
  AddMessage(message.c_str());
}

void CCadConvertAppDlg::EnableDisableControls(void)
{
  UpdateData(TRUE);
  struct _stat buffer;
  int inputResult = _wstat(_inputFilename, &buffer);

  wchar_t drive[_MAX_DRIVE];
  wchar_t dir[_MAX_DIR];
  wchar_t fname[_MAX_FNAME];
  wchar_t ext[_MAX_EXT];
  _wsplitpath_s(_outputFilename, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT);
  wchar_t outputPath[_MAX_PATH];
  wcscpy_s(outputPath, _countof(outputPath), drive);
  wcscat_s(outputPath, _countof(outputPath), dir);

  int outputResult = _wstat(outputPath, &buffer);

  bool enableConvert = (inputResult == 0 && outputResult == 0);
  CWnd *pWnd = GetDlgItem(IDC_CONVERT);
  if (pWnd != nullptr)
  {
    pWnd->EnableWindow(enableConvert);
  }
}

void CCadConvertAppDlg::OnEnChangeInputFilename()
{
  EnableDisableControls();
}

void CCadConvertAppDlg::OnEnChangeOutputFilename()
{
  EnableDisableControls();
}

void CCadConvertAppDlg::AddMessage(const wchar_t *pMessage)
{
  if (::IsWindow(m_hWnd))
  {
    const int numLines = _messagesEdit.GetLineCount();
    static const int maxMessageLines = 500;
    if (numLines >= maxMessageLines)
    {
      // reached maximum line length, delete the lines at the top
      const int endIndex = _messagesEdit.LineIndex(numLines - maxMessageLines + 1);
      CString text;
      _messagesEdit.GetWindowText(text);
      text = text.Mid(endIndex);
      _messagesEdit.SetWindowText(text);
    }
    CString text;
    _messagesEdit.GetWindowText(text);
    if (_messagesEdit.GetWindowTextLength() > 0)
    {
      text += L"\r\n";
    }
    text += pMessage;
    _messagesEdit.SetWindowText(text);

    // scroll the edit control so that the last line of text is visible
    const int nFirstVisible = _messagesEdit.GetLineCount();
    _messagesEdit.LineScroll(nFirstVisible);
  }
}
