#include "FileDialog.h"

#include <ShObjIdl.h>

#include "Text.h"
#pragma comment(lib, "Ole32.lib")
#pragma comment(linker, \
    "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

void Framework::InitDialog()
{
    CoInitialize(0);
}

using namespace Framework;

// Contents of the FileDialog class from FileDialog.h
// Constructor
FileDialog::FileDialog()
    : ReferenceCounter()
{
    typeName = new RCArray<Text>();
    type = new RCArray<Text>();
    fileIndex = 1;
}

// Destructor
FileDialog::~FileDialog()
{
    typeName->release();
    type->release();
}

// non-constant
void FileDialog::removeFileTypes()
{
    typeName->clear();
    type->clear();
}

void FileDialog::addFileType(const char* name, const char* typ)
{
    addFileType(new Text(name), new Text(typ));
}

void FileDialog::addFileType(Text* name, Text* typ)
{
    typeName->add(name);
    type->add(typ);
}

void FileDialog::setFileTypeSelection(int i)
{
    fileIndex = i + 1;
}

// constant
Text* FileDialog::show(bool open) const
{
    IFileDialog* pfd = NULL;
    CoInitialize(NULL);
    HRESULT hr = 0;
    if (open)
    {
        CoCreateInstance(CLSID_FileOpenDialog,
            NULL,
            CLSCTX_INPROC_SERVER,
            IID_PPV_ARGS(&pfd));
    }
    else
    {
        CoCreateInstance(CLSID_FileSaveDialog,
            NULL,
            CLSCTX_INPROC_SERVER,
            IID_PPV_ARGS(&pfd));
    }
    if (SUCCEEDED(hr))
    {
        DWORD dwFlags;
        hr = pfd->GetOptions(&dwFlags);
        if (SUCCEEDED(hr))
        {
            hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
            if (SUCCEEDED(hr))
            {
                int anz = type->getEntryCount();
                COMDLG_FILTERSPEC* c_rgSaveTypes = 0;
                if (!anz)
                {
                    c_rgSaveTypes = new COMDLG_FILTERSPEC[1];
                    c_rgSaveTypes[0] = {L"Alle Files", L"*.*"};
                    anz = 1;
                }
                else
                {
                    c_rgSaveTypes = new COMDLG_FILTERSPEC[anz];
                    for (int i = 0; i < anz; i++)
                    {
                        wchar_t* n
                            = new wchar_t[typeName->z(i)->getLength() + 1];
#pragma warning(disable : 4996)
                        mbstowcs(n,
                            typeName->z(i)->getText(),
                            typeName->z(i)->getLength() + 1);
                        wchar_t* t = new wchar_t[type->z(i)->getLength() + 1];
#pragma warning(disable : 4996)
                        mbstowcs(t,
                            type->z(i)->getText(),
                            type->z(i)->getLength() + 1);
                        c_rgSaveTypes[i].pszName = n;
                        c_rgSaveTypes[i].pszSpec = t;
                    }
                }
                hr = pfd->SetFileTypes(anz, c_rgSaveTypes);
                if (SUCCEEDED(hr))
                {
                    hr = pfd->SetFileTypeIndex(fileIndex);
                    if (SUCCEEDED(hr))
                    {
                        Text txt = "";
                        for (int i = 0; i < anz; i++)
                        {
                            if (!type->z(i)->has(".*"))
                            {
                                Text* tmp = type->z(i)->getTeilText(
                                    type->z(i)->positionOf(".") + 1);
                                txt.append(*tmp);
                                tmp->release();
                                txt += ";";
                            }
                        }
                        if (txt.getLength() > 0)
                            txt.remove(txt.getLength() - 1);
                        wchar_t* defEnd = new wchar_t[txt.getLength() + 1];
#pragma warning(disable : 4996)
                        mbstowcs(defEnd, txt, txt.getLength() + 1);
                        hr = pfd->SetDefaultExtension(defEnd);
                        if (SUCCEEDED(hr))
                        {
                            hr = pfd->Show(NULL);
                            if (SUCCEEDED(hr))
                            {
                                IShellItem* psiResult;
                                hr = pfd->GetResult(&psiResult);
                                if (SUCCEEDED(hr))
                                {
                                    PWSTR pszFilePath = NULL;
                                    hr = psiResult->GetDisplayName(
                                        SIGDN_FILESYSPATH, &pszFilePath);
                                    if (SUCCEEDED(hr))
                                    {
                                        char* txt
                                            = new char[wcslen(pszFilePath) + 1];
#pragma warning(disable : 4996)
                                        wcstombs(txt,
                                            pszFilePath,
                                            wcslen(pszFilePath) + 1);
                                        Text* ret = new Text(txt);
                                        delete[] txt;
                                        psiResult->Release();
                                        delete[] defEnd;
                                        for (int i = 0; i < anz; i++)
                                        {
                                            delete[] c_rgSaveTypes[i].pszName;
                                            delete[] c_rgSaveTypes[i].pszSpec;
                                        }
                                        delete[] c_rgSaveTypes;
                                        pfd->Release();
                                        return ret;
                                    }
                                    psiResult->Release();
                                }
                            }
                        }
                        delete[] defEnd;
                    }
                }
                for (int i = 0; i < anz; i++)
                {
                    delete[] c_rgSaveTypes[i].pszName;
                    delete[] c_rgSaveTypes[i].pszSpec;
                }
                delete[] c_rgSaveTypes;
            }
        }
        pfd->Release();
    }
    return 0;
}

// Contents of the FileDialogTh class from FileDialog.h
// Constructor
FileDialogTh::FileDialogTh()
    : Thread()
{
    dialog = new FileDialog();
    ret = 0;
    open = 0;
}

// Destructor
FileDialogTh::~FileDialogTh()
{
    waitForThread(5000);
    if (run) ende();
    dialog->release();
    if (ret) ret->release();
}

// non-constant
void FileDialogTh::setOpen(bool b)
{
    open = b;
}

void FileDialogTh::removeFileTypes()
{
    dialog->removeFileTypes();
}

void FileDialogTh::addFileType(const char* name, const char* typ)
{
    dialog->addFileType(name, typ);
}

void FileDialogTh::addFileType(Text* name, Text* typ)
{
    dialog->addFileType(name, typ);
}

void FileDialogTh::setFileTypeSelection(int i)
{
    dialog->setFileTypeSelection(i);
}

void FileDialogTh::thread()
{
    if (ret) ret = (Text*)ret->release();
    ret = dialog->show(open);
}

// constant
Text* FileDialogTh::getPfad() const
{
    return ret ? dynamic_cast<Text*>(ret->getThis()) : 0;
}

Text* FileDialogTh::zPfad() const
{
    return ret;
}