//--------------------------------------------------------------------------------------
// File: SDKmisc.cpp
//
// Various helper functionality that is shared between SDK samples
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// http://go.microsoft.com/fwlink/?LinkId=320437
//--------------------------------------------------------------------------------------
#include "dxut.h"
#include "SDKmisc.h"
#include "DXUTres.h"

#include "DXUTGui.h"

#include "DDSTextureLoader.h"
#include "WICTextureLoader.h"
#include "ScreenGrab.h"

using namespace DirectX;

//--------------------------------------------------------------------------------------
// Global/Static Members
//--------------------------------------------------------------------------------------
CDXUTResourceCache& WINAPI DXUTGetGlobalResourceCache()
{
    // Using an accessor function gives control of the construction order
    static CDXUTResourceCache* s_cache = nullptr;
    if ( !s_cache )
    {
#if defined(DEBUG) || defined(_DEBUG)
        int flag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
        _CrtSetDbgFlag( flag & ~_CRTDBG_ALLOC_MEM_DF );
#endif
        s_cache = new CDXUTResourceCache;
#if defined(DEBUG) || defined(_DEBUG)
        _CrtSetDbgFlag( flag );
#endif
    }
    return *s_cache;
}


//--------------------------------------------------------------------------------------
// Internal functions forward declarations
//--------------------------------------------------------------------------------------
bool DXUTFindMediaSearchTypicalDirs( _Out_writes_(cchSearch) WCHAR* strSearchPath, 
                                     _In_ int cchSearch, 
                                     _In_ LPCWSTR strLeaf, 
                                     _In_ const WCHAR* strExePath,
                                     _In_ const WCHAR* strExeName );
bool DXUTFindMediaSearchParentDirs( _Out_writes_(cchSearch) WCHAR* strSearchPath, 
                                    _In_ int cchSearch, 
                                    _In_ const WCHAR* strStartAt, 
                                    _In_ const WCHAR* strLeafName );

INT_PTR CALLBACK DisplaySwitchToREFWarningProc( _In_ HWND hDlg, _In_ UINT message, _In_ WPARAM wParam, _In_ LPARAM lParam );


//--------------------------------------------------------------------------------------
// Shared code for samples to ask user if they want to use a REF device or quit
//--------------------------------------------------------------------------------------
void WINAPI DXUTDisplaySwitchingToREFWarning()
{
    if( DXUTGetShowMsgBoxOnError() )
    {
        DWORD dwSkipWarning = 0, dwRead = 0, dwWritten = 0;
        HANDLE hFile = nullptr;

        // Read previous user settings
        WCHAR strPath[MAX_PATH];
        if ( SUCCEEDED(SHGetFolderPath(DXUTGetHWND(), CSIDL_LOCAL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, strPath)) )
        {
            wcscat_s( strPath, MAX_PATH, L"\\DXUT\\SkipRefWarning.dat" );
            if( ( hFile = CreateFile( strPath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0,
                                      nullptr ) ) != INVALID_HANDLE_VALUE )
            {
                (void)ReadFile( hFile, &dwSkipWarning, sizeof( DWORD ), &dwRead, nullptr );
                CloseHandle( hFile );
            }
        }

        if( dwSkipWarning == 0 )
        {
            // Compact code to create a custom dialog box without using a template in a resource file.
            // If this dialog were in a .rc file, this would be a lot simpler but every sample calling this function would
            // need a copy of the dialog in its own .rc file. Also MessageBox API could be used here instead, but 
            // the MessageBox API is simpler to call but it can't provide a "Don't show again" checkbox
            typedef struct
            {
                DLGITEMTEMPLATE a;
                WORD b;
                WORD c;
                WORD d;
                WORD e;
                WORD f;
            } DXUT_DLG_ITEM;
            typedef struct
            {
                DLGTEMPLATE a;
                WORD b;
                WORD c;
                WCHAR   d[2];
                WORD e;
                WCHAR   f[16];
                DXUT_DLG_ITEM i1;
                DXUT_DLG_ITEM i2;
                DXUT_DLG_ITEM i3;
                DXUT_DLG_ITEM i4;
                DXUT_DLG_ITEM i5;
            } DXUT_DLG_DATA;

            DXUT_DLG_DATA dtp =
            {
                {WS_CAPTION | WS_POPUP | WS_VISIBLE | WS_SYSMENU | DS_ABSALIGN | DS_3DLOOK | DS_SETFONT |
                    DS_MODALFRAME | DS_CENTER,0,5,0,0,269,82},0,0,L" ",8,L"MS Shell Dlg 2",
                { {WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE,0,7,7,24,24,0x100},0xFFFF,0x0082,0,0,0}, // icon
                { {WS_CHILD | WS_VISIBLE,0,40,7,230,25,0x101},0xFFFF,0x0082,0,0,0}, // static text
                { {WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON,0,80,39,50,14,IDYES},0xFFFF,0x0080,0,0,0}, // Yes button
                { {WS_CHILD | WS_VISIBLE | WS_TABSTOP,0,133,39,50,14,IDNO},0xFFFF,0x0080,0,0,0}, // No button
                { {WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_CHECKBOX,0,7,59,70,16,IDIGNORE},0xFFFF,0x0080,0,0,0}, // checkbox
            };

            LPARAM lParam;
            lParam = 11;
            int nResult = ( int )DialogBoxIndirectParam( DXUTGetHINSTANCE(), ( DLGTEMPLATE* )&dtp, DXUTGetHWND(),
                                                         DisplaySwitchToREFWarningProc, lParam );

            if( ( nResult & 0x80 ) == 0x80 ) // "Don't show again" checkbox was checked
            {
                // Save user settings
                dwSkipWarning = 1;
                if ( SUCCEEDED(SHGetFolderPath(DXUTGetHWND(), CSIDL_LOCAL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, strPath)) )
                {
                    wcscat_s( strPath, MAX_PATH, L"\\DXUT" );
                    CreateDirectory( strPath, nullptr );
                    wcscat_s( strPath, MAX_PATH, L"\\SkipRefWarning.dat" );
                    if( ( hFile = CreateFile( strPath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0,
                                              nullptr ) ) != INVALID_HANDLE_VALUE )
                    {
                        WriteFile( hFile, &dwSkipWarning, sizeof( DWORD ), &dwWritten, nullptr );
                        CloseHandle( hFile );
                    }
                }
            }

            // User choose not to continue
            if( ( nResult & 0x0F ) == IDNO )
                DXUTShutdown( 1 );
        }
    }
}


//--------------------------------------------------------------------------------------
// MsgProc for DXUTDisplaySwitchingToREFWarning() dialog box
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
INT_PTR CALLBACK DisplaySwitchToREFWarningProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
    switch( message )
    {
        case WM_INITDIALOG:
            // Easier to set text here than in the DLGITEMTEMPLATE
            SetWindowText( hDlg, DXUTGetWindowTitle() );
            SendMessage( GetDlgItem( hDlg, 0x100 ), STM_SETIMAGE, IMAGE_ICON, ( LPARAM )LoadIcon( 0, IDI_QUESTION ) );
            WCHAR sz[512];
            swprintf_s( sz, 512,
                             L"This program needs to use the Direct3D %zu reference device.  This device implements the entire Direct3D %zu feature set, but runs very slowly.  Do you wish to continue?", lParam, lParam );
            SetDlgItemText( hDlg, 0x101, sz );
            SetDlgItemText( hDlg, IDYES, L"&Yes" );
            SetDlgItemText( hDlg, IDNO, L"&No" );
            SetDlgItemText( hDlg, IDIGNORE, L"&Don't show again" );
            break;

        case WM_COMMAND:
            switch( LOWORD( wParam ) )
            {
                case IDIGNORE:
                    CheckDlgButton( hDlg, IDIGNORE, ( IsDlgButtonChecked( hDlg,
                                                                          IDIGNORE ) == BST_CHECKED ) ? BST_UNCHECKED :
                                    BST_CHECKED );
                    EnableWindow( GetDlgItem( hDlg, IDNO ), ( IsDlgButtonChecked( hDlg, IDIGNORE ) != BST_CHECKED ) );
                    break;
                case IDNO:
                    EndDialog( hDlg, ( IsDlgButtonChecked( hDlg, IDIGNORE ) == BST_CHECKED ) ? IDNO | 0x80 : IDNO |
                               0x00 ); return TRUE;
                case IDCANCEL:
                case IDYES:
                    EndDialog( hDlg, ( IsDlgButtonChecked( hDlg, IDIGNORE ) == BST_CHECKED ) ? IDYES | 0x80 : IDYES |
                               0x00 ); return TRUE;
            }
            break;
    }
    return FALSE;
}


//--------------------------------------------------------------------------------------
// Returns pointer to static media search buffer
//--------------------------------------------------------------------------------------
WCHAR* DXUTMediaSearchPath()
{
    static WCHAR s_strMediaSearchPath[MAX_PATH] =
    {
        0
    };
    return s_strMediaSearchPath;

}


//--------------------------------------------------------------------------------------
LPCWSTR WINAPI DXUTGetMediaSearchPath()
{
    return DXUTMediaSearchPath();
}


//--------------------------------------------------------------------------------------
HRESULT WINAPI DXUTSetMediaSearchPath( _In_z_ LPCWSTR strPath )
{
    HRESULT hr;

    WCHAR* s_strSearchPath = DXUTMediaSearchPath();

    hr = wcscpy_s( s_strSearchPath, MAX_PATH, strPath );
    if( SUCCEEDED( hr ) )
    {
        // append slash if needed
        size_t ch = 0;
        ch = wcsnlen( s_strSearchPath, MAX_PATH);
        if( SUCCEEDED( hr ) && s_strSearchPath[ch - 1] != L'\\' )
        {
            hr = wcscat_s( s_strSearchPath, MAX_PATH, L"\\" );
        }
    }

    return hr;
}


//--------------------------------------------------------------------------------------
// Tries to find the location of a SDK media file
//       cchDest is the size in WCHARs of strDestPath.  Be careful not to 
//       pass in sizeof(strDest) on UNICODE builds.
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT WINAPI DXUTFindDXSDKMediaFileCch( WCHAR* strDestPath, int cchDest, 
                                          LPCWSTR strFilename )
{
    bool bFound;
    WCHAR strSearchFor[MAX_PATH];

    if( !strFilename || strFilename[0] == 0 || !strDestPath || cchDest < 10 )
        return E_INVALIDARG;

    // Get the exe name, and exe path
    WCHAR strExePath[MAX_PATH] =
    {
        0
    };
    WCHAR strExeName[MAX_PATH] =
    {
        0
    };
    WCHAR* strLastSlash = nullptr;
    GetModuleFileName( nullptr, strExePath, MAX_PATH );
    strExePath[MAX_PATH - 1] = 0;
    strLastSlash = wcsrchr( strExePath, TEXT( '\\' ) );
    if( strLastSlash )
    {
        wcscpy_s( strExeName, MAX_PATH, &strLastSlash[1] );

        // Chop the exe name from the exe path
        *strLastSlash = 0;

        // Chop the .exe from the exe name
        strLastSlash = wcsrchr( strExeName, TEXT( '.' ) );
        if( strLastSlash )
            *strLastSlash = 0;
    }

    // Typical directories:
    //      .\
    //      ..\
    //      ..\..\
    //      %EXE_DIR%\
    //      %EXE_DIR%\..\
    //      %EXE_DIR%\..\..\
    //      %EXE_DIR%\..\%EXE_NAME%
    //      %EXE_DIR%\..\..\%EXE_NAME%

    // Typical directory search
    bFound = DXUTFindMediaSearchTypicalDirs( strDestPath, cchDest, strFilename, strExePath, strExeName );
    if( bFound )
        return S_OK;

    // Typical directory search again, but also look in a subdir called "\media\" 
    swprintf_s( strSearchFor, MAX_PATH, L"media\\%ls", strFilename );
    bFound = DXUTFindMediaSearchTypicalDirs( strDestPath, cchDest, strSearchFor, strExePath, strExeName );
    if( bFound )
        return S_OK;

    WCHAR strLeafName[MAX_PATH] =
    {
        0
    };

    // Search all parent directories starting at .\ and using strFilename as the leaf name
    wcscpy_s( strLeafName, MAX_PATH, strFilename );
    bFound = DXUTFindMediaSearchParentDirs( strDestPath, cchDest, L".", strLeafName );
    if( bFound )
        return S_OK;

    // Search all parent directories starting at the exe's dir and using strFilename as the leaf name
    bFound = DXUTFindMediaSearchParentDirs( strDestPath, cchDest, strExePath, strLeafName );
    if( bFound )
        return S_OK;

    // Search all parent directories starting at .\ and using "media\strFilename" as the leaf name
    swprintf_s( strLeafName, MAX_PATH, L"media\\%ls", strFilename );
    bFound = DXUTFindMediaSearchParentDirs( strDestPath, cchDest, L".", strLeafName );
    if( bFound )
        return S_OK;

    // Search all parent directories starting at the exe's dir and using "media\strFilename" as the leaf name
    bFound = DXUTFindMediaSearchParentDirs( strDestPath, cchDest, strExePath, strLeafName );
    if( bFound )
        return S_OK;

    // On failure, return the file as the path but also return an error code
    wcscpy_s( strDestPath, cchDest, strFilename );

    return DXUTERR_MEDIANOTFOUND;
}


//--------------------------------------------------------------------------------------
// Search a set of typical directories
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
bool DXUTFindMediaSearchTypicalDirs( WCHAR* strSearchPath, int cchSearch, LPCWSTR strLeaf, 
                                     const WCHAR* strExePath, const WCHAR* strExeName )
{
    // Typical directories:
    //      .\
    //      ..\
    //      ..\..\
    //      %EXE_DIR%\
    //      %EXE_DIR%\..\
    //      %EXE_DIR%\..\..\
    //      %EXE_DIR%\..\%EXE_NAME%
    //      %EXE_DIR%\..\..\%EXE_NAME%
    //      DXSDK media path

    // Search in .\  
    wcscpy_s( strSearchPath, cchSearch, strLeaf );
    if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
        return true;

    // Search in ..\  
    swprintf_s( strSearchPath, cchSearch, L"..\\%ls", strLeaf );
    if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
        return true;

    // Search in ..\..\ 
    swprintf_s( strSearchPath, cchSearch, L"..\\..\\%ls", strLeaf );
    if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
        return true;

    // Search in ..\..\ 
    swprintf_s( strSearchPath, cchSearch, L"..\\..\\%ls", strLeaf );
    if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
        return true;

    // Search in the %EXE_DIR%\ 
    swprintf_s( strSearchPath, cchSearch, L"%ls\\%ls", strExePath, strLeaf );
    if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
        return true;

    // Search in the %EXE_DIR%\..\ 
    swprintf_s( strSearchPath, cchSearch, L"%ls\\..\\%ls", strExePath, strLeaf );
    if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
        return true;

    // Search in the %EXE_DIR%\..\..\ 
    swprintf_s( strSearchPath, cchSearch, L"%ls\\..\\..\\%ls", strExePath, strLeaf );
    if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
        return true;

    // Search in "%EXE_DIR%\..\%EXE_NAME%\".  This matches the DirectX SDK layout
    swprintf_s( strSearchPath, cchSearch, L"%ls\\..\\%ls\\%ls", strExePath, strExeName, strLeaf );
    if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
        return true;

    // Search in "%EXE_DIR%\..\..\%EXE_NAME%\".  This matches the DirectX SDK layout
    swprintf_s( strSearchPath, cchSearch, L"%ls\\..\\..\\%ls\\%ls", strExePath, strExeName, strLeaf );
    if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
        return true;

    // Search in media search dir 
    WCHAR* s_strSearchPath = DXUTMediaSearchPath();
    if( s_strSearchPath[0] != 0 )
    {
        swprintf_s( strSearchPath, cchSearch, L"%ls%ls", s_strSearchPath, strLeaf );
        if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
            return true;
    }

    return false;
}


//--------------------------------------------------------------------------------------
// Search parent directories starting at strStartAt, and appending strLeafName
// at each parent directory.  It stops at the root directory.
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
bool DXUTFindMediaSearchParentDirs( WCHAR* strSearchPath, int cchSearch, const WCHAR* strStartAt, 
                                    const WCHAR* strLeafName )
{
    WCHAR strFullPath[MAX_PATH] =
    {
        0
    };
    WCHAR strFullFileName[MAX_PATH] =
    {
        0
    };
    WCHAR strSearch[MAX_PATH] =
    {
        0
    };
    WCHAR* strFilePart = nullptr;

    if ( !GetFullPathName( strStartAt, MAX_PATH, strFullPath, &strFilePart ) )
        return false;

#pragma warning( disable : 6102 )
    while( strFilePart && *strFilePart != '\0' )
    {
        swprintf_s( strFullFileName, MAX_PATH, L"%ls\\%ls", strFullPath, strLeafName );
        if( GetFileAttributes( strFullFileName ) != 0xFFFFFFFF )
        {
            wcscpy_s( strSearchPath, cchSearch, strFullFileName );
            return true;
        }

        swprintf_s( strSearch, MAX_PATH, L"%ls\\..", strFullPath );
        if ( !GetFullPathName( strSearch, MAX_PATH, strFullPath, &strFilePart ) )
            return false;
    }

    return false;
}


//--------------------------------------------------------------------------------------
// Compiles HLSL shaders
//--------------------------------------------------------------------------------------
#if D3D_COMPILER_VERSION < 46

namespace
{

struct handle_closer { void operator()(HANDLE h) { if (h) CloseHandle(h); } };

typedef std::unique_ptr<void, handle_closer> ScopedHandle;

inline HANDLE safe_handle( HANDLE h ) { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }

class CIncludeHandler : public ID3DInclude
    // Not as robust as D3D_COMPILE_STANDARD_FILE_INCLUDE, but it works in most cases
{
private:
    static const unsigned int MAX_INCLUDES = 9;

    struct sInclude
    {
        HANDLE         hFile;
        HANDLE         hFileMap;
        LARGE_INTEGER  FileSize;
        void           *pMapData;
    };

    struct sInclude     m_includeFiles[MAX_INCLUDES];
    size_t              m_nIncludes;
    bool                m_reset;
    WCHAR               m_workingPath[MAX_PATH];

public:
    CIncludeHandler() : m_nIncludes(0), m_reset(false)
    {
        if ( !GetCurrentDirectoryW( MAX_PATH, m_workingPath ) )
            *m_workingPath = 0;

        for ( size_t i = 0; i < MAX_INCLUDES; ++i )
        {
            m_includeFiles[i].hFile = INVALID_HANDLE_VALUE;
            m_includeFiles[i].hFileMap = INVALID_HANDLE_VALUE;
            m_includeFiles[i].pMapData = nullptr;
        }
    }
    virtual ~CIncludeHandler()
    {
        for ( size_t i = 0; i < m_nIncludes; ++i )
        {
            UnmapViewOfFile( m_includeFiles[i].pMapData );

            if ( m_includeFiles[i].hFileMap != INVALID_HANDLE_VALUE)
                CloseHandle( m_includeFiles[i].hFileMap );

            if ( m_includeFiles[i].hFile != INVALID_HANDLE_VALUE)
                CloseHandle( m_includeFiles[i].hFile );
        }

        m_nIncludes = 0;

        if ( m_reset && *m_workingPath )
        {
            SetCurrentDirectoryW( m_workingPath );
        }
    }

    STDMETHOD(Open( D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes ) )
    {
        UNREFERENCED_PARAMETER(IncludeType);
        UNREFERENCED_PARAMETER(pParentData);

        size_t incIndex = m_nIncludes+1;

        // Make sure we have enough room for this include file
        if ( incIndex >= MAX_INCLUDES )
            return E_FAIL;

        // try to open the file
        m_includeFiles[incIndex].hFile  = CreateFileA( pFileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr );
        if( INVALID_HANDLE_VALUE == m_includeFiles[incIndex].hFile )
        {
            return E_FAIL;
        }

        // Get the file size
        GetFileSizeEx( m_includeFiles[incIndex].hFile, &m_includeFiles[incIndex].FileSize );

        // Use Memory Mapped File I/O for the header data
        m_includeFiles[incIndex].hFileMap = CreateFileMappingA( m_includeFiles[incIndex].hFile, nullptr, PAGE_READONLY, m_includeFiles[incIndex].FileSize.HighPart, m_includeFiles[incIndex].FileSize.LowPart, pFileName);
        if( !m_includeFiles[incIndex].hFileMap )
        {
            if (m_includeFiles[incIndex].hFile != INVALID_HANDLE_VALUE)
                CloseHandle( m_includeFiles[incIndex].hFile );
            return E_FAIL;
        }

        // Create Map view
        *ppData = MapViewOfFile( m_includeFiles[incIndex].hFileMap, FILE_MAP_READ, 0, 0, 0 );
        *pBytes = m_includeFiles[incIndex].FileSize.LowPart;

        // Success - Increment the include file count
        m_nIncludes = incIndex;

        return S_OK;
    }

    STDMETHOD(Close( LPCVOID pData ))
    {
        UNREFERENCED_PARAMETER(pData);
        // Defer Closure until the container destructor 
        return S_OK;
    }

    void SetCWD( LPCWSTR pFileName )
    {
        WCHAR filePath[MAX_PATH];
        wcscpy_s( filePath, MAX_PATH, pFileName );

        WCHAR *strLastSlash = wcsrchr( filePath, L'\\' );
        if( strLastSlash )
        {
            // Chop the exe name from the exe path
            *strLastSlash = 0;
            m_reset = true;
            SetCurrentDirectoryW( filePath );
        }
    }
};

}; // namespace

#endif

_Use_decl_annotations_
HRESULT WINAPI DXUTCompileFromFile( LPCWSTR pFileName,
                                    const D3D_SHADER_MACRO* pDefines,
                                    LPCSTR pEntrypoint, LPCSTR pTarget,
                                    UINT Flags1, UINT Flags2,
                                    ID3DBlob** ppCode )
{
    HRESULT hr;
    WCHAR str[MAX_PATH];
    V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, pFileName ) );

#if defined( DEBUG ) || defined( _DEBUG )
    // Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
    // Setting this flag improves the shader debugging experience, but still allows 
    // the shaders to be optimized and to run exactly the way they will run in 
    // the release configuration of this program.
    Flags1 |= D3DCOMPILE_DEBUG;
#endif

    ID3DBlob* pErrorBlob = nullptr;

#if D3D_COMPILER_VERSION >= 46

    hr = D3DCompileFromFile( str, pDefines, D3D_COMPILE_STANDARD_FILE_INCLUDE,
                             pEntrypoint, pTarget, Flags1, Flags2,
                             ppCode, &pErrorBlob );

#else

    ScopedHandle hFile( safe_handle( CreateFileW( str, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr ) ) );

    if ( !hFile )
        return HRESULT_FROM_WIN32( GetLastError() );

    LARGE_INTEGER FileSize = {};

#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
    FILE_STANDARD_INFO fileInfo;
    if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) )
    {
        return HRESULT_FROM_WIN32( GetLastError() );
    }
    FileSize = fileInfo.EndOfFile;
#else
    GetFileSizeEx( hFile.get(), &FileSize );
#endif

    if (!FileSize.LowPart || FileSize.HighPart > 0)
        return E_FAIL;

    std::unique_ptr<char[]> fxData;
    fxData.reset( new (std::nothrow) char[ FileSize.LowPart ] );
    if ( !fxData )
        return E_OUTOFMEMORY;

    DWORD BytesRead = 0;
    if ( !ReadFile( hFile.get(), fxData.get(), FileSize.LowPart, &BytesRead, nullptr ) )
        return HRESULT_FROM_WIN32( GetLastError() );

    if (BytesRead < FileSize.LowPart)
        return E_FAIL;

    char pSrcName[MAX_PATH];
    int result = WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, str, -1, pSrcName, MAX_PATH, nullptr, FALSE );
    if ( !result )
        return E_FAIL;
    
    const CHAR* pstrName = strrchr( pSrcName, '\\' );
    if (!pstrName)
    {
        pstrName = pSrcName;
    }
    else
    {
        pstrName++;
    }

    std::unique_ptr<CIncludeHandler> includes( new (std::nothrow) CIncludeHandler );
    if ( !includes )
        return E_OUTOFMEMORY;

    includes->SetCWD( str );

    hr = D3DCompile( fxData.get(), BytesRead, pstrName, pDefines, includes.get(),
                     pEntrypoint, pTarget, Flags1, Flags2,
                     ppCode, &pErrorBlob );

#endif

#pragma warning( suppress : 6102 )
    if ( pErrorBlob )
    {
        OutputDebugStringA( reinterpret_cast<const char*>( pErrorBlob->GetBufferPointer() ) );
        pErrorBlob->Release();
    }

    return hr;
}


//--------------------------------------------------------------------------------------
// Texture utilities
//--------------------------------------------------------------------------------------

_Use_decl_annotations_
HRESULT WINAPI DXUTCreateShaderResourceViewFromFile( ID3D11Device* d3dDevice, const wchar_t* szFileName, ID3D11ShaderResourceView** textureView )
{
    if ( !d3dDevice || !szFileName || !textureView )
        return E_INVALIDARG;

    WCHAR str[MAX_PATH];
    HRESULT hr = DXUTFindDXSDKMediaFileCch( str, MAX_PATH, szFileName );
    if ( FAILED(hr) )
        return hr;

    WCHAR ext[_MAX_EXT];
    _wsplitpath_s( str, nullptr, 0, nullptr, 0, nullptr, 0, ext, _MAX_EXT );

    if ( _wcsicmp( ext, L".dds" ) == 0 )
    {
        hr = DirectX::CreateDDSTextureFromFile( d3dDevice, str, nullptr, textureView );
    }
    else
    {
        hr = DirectX::CreateWICTextureFromFile( d3dDevice, nullptr, str, nullptr, textureView );
    }

    return hr;
}

_Use_decl_annotations_
HRESULT WINAPI DXUTCreateTextureFromFile( ID3D11Device* d3dDevice, const wchar_t* szFileName, ID3D11Resource** texture )
{
    if ( !d3dDevice || !szFileName || !texture )
        return E_INVALIDARG;

    WCHAR str[MAX_PATH];
    HRESULT hr = DXUTFindDXSDKMediaFileCch( str, MAX_PATH, szFileName );
    if ( FAILED(hr) )
        return hr;

    WCHAR ext[_MAX_EXT];
    _wsplitpath_s( str, nullptr, 0, nullptr, 0, nullptr, 0, ext, _MAX_EXT );

    if ( _wcsicmp( ext, L".dds" ) == 0 )
    {
        hr = DirectX::CreateDDSTextureFromFile( d3dDevice, str, texture, nullptr );
    }
    else
    {
        hr = DirectX::CreateWICTextureFromFile( d3dDevice, nullptr, str, texture, nullptr );
    }

    return hr;
}

_Use_decl_annotations_
HRESULT WINAPI DXUTSaveTextureToFile( ID3D11DeviceContext* pContext, ID3D11Resource* pSource, bool usedds, const wchar_t* szFileName )
{
    if ( !pContext || !pSource || !szFileName )
        return E_INVALIDARG;

    HRESULT hr;

    if ( usedds )
    {
        hr = DirectX::SaveDDSTextureToFile( pContext, pSource, szFileName );
    }
    else
    {
        hr = DirectX::SaveWICTextureToFile( pContext, pSource, GUID_ContainerFormatBmp, szFileName );
    }

    return hr;
}


//--------------------------------------------------------------------------------------
// Desc: Returns a view matrix for rendering to a face of a cubemap.
//--------------------------------------------------------------------------------------
XMMATRIX WINAPI DXUTGetCubeMapViewMatrix( _In_ DWORD dwFace )
{
    static const XMVECTORF32 s_vLookDir[] =
    {
        { 1.0f, 0.0f, 0.0f, 0.0f },
        { -1.0f, 0.0f, 0.0f, 0.0f },
        { 0.0f, 1.0f, 0.0f, 0.0f },
        { 0.0f, -1.0f, 0.0f, 0.0f },
        { 0.0f, 0.0f, 1.0f, 0.0f },
        { 0.0f, 0.0f, -1.0f, 0.0f },
    };

    static const XMVECTORF32 s_vUpDir[] = 
    {
        { 0.0f, 1.0f, 0.0f, 0.0f },
        { 0.0f, 1.0f, 0.0f, 0.0f },
        { 0.0f, 0.0f, -1.0f, 0.0f },
        { 0.0f, 0.0f, 1.0f, 0.0f },
        { 0.0f, 1.0f, 0.0f, 0.0f },
        { 0.0f, 1.0f, 0.0f, 0.0f },
    };

    static_assert( _countof(s_vLookDir) == _countof(s_vUpDir), "arrays mismatch" );

    if ( dwFace >= _countof(s_vLookDir)
         || dwFace >= _countof(s_vUpDir) )
        return XMMatrixIdentity();

    // Set the view transform for this cubemap surface
    return XMMatrixLookAtLH( g_XMZero, s_vLookDir[ dwFace ], s_vUpDir[ dwFace ] );
}


//======================================================================================
// CDXUTResourceCache
//======================================================================================

CDXUTResourceCache::~CDXUTResourceCache()
{
    OnDestroyDevice();
}

//--------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT CDXUTResourceCache::CreateTextureFromFile( ID3D11Device* pDevice, ID3D11DeviceContext *pContext, LPCWSTR pSrcFile,
                                                   ID3D11ShaderResourceView** ppOutputRV, bool bSRGB )
{
    if ( !ppOutputRV )
        return E_INVALIDARG;

    *ppOutputRV = nullptr;

    for( auto it = m_TextureCache.cbegin(); it != m_TextureCache.cend(); ++it )
    {
        if( !wcscmp( it->wszSource, pSrcFile )
            && it->bSRGB == bSRGB
            && it->pSRV11 )
        {
            it->pSRV11->AddRef();
            *ppOutputRV = it->pSRV11;
            return S_OK;
        }
    }

    WCHAR ext[_MAX_EXT];
    _wsplitpath_s( pSrcFile, nullptr, 0, nullptr, 0, nullptr, 0, ext, _MAX_EXT );

    HRESULT hr;
    if ( _wcsicmp( ext, L".dds" ) == 0 )
    {
        hr = DirectX::CreateDDSTextureFromFileEx( pDevice, pSrcFile, 0,
                                                  D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, bSRGB,
                                                  nullptr, ppOutputRV, nullptr );
    }
    else
    {
        hr = DirectX::CreateWICTextureFromFileEx( pDevice, pContext, pSrcFile, 0,
                                                  D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, bSRGB,
                                                  nullptr, ppOutputRV );
    }

    if ( FAILED(hr) )
        return hr;

    DXUTCache_Texture entry;
    wcscpy_s( entry.wszSource, MAX_PATH, pSrcFile );
    entry.bSRGB = bSRGB;
    entry.pSRV11 = *ppOutputRV;
    entry.pSRV11->AddRef();
    m_TextureCache.push_back( entry );

    return S_OK;
}


//--------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT CDXUTResourceCache::CreateTextureFromFile( ID3D11Device* pDevice, ID3D11DeviceContext *pContext, LPCSTR pSrcFile,
                                                   ID3D11ShaderResourceView** ppOutputRV, bool bSRGB )
{
    WCHAR szSrcFile[MAX_PATH];
    MultiByteToWideChar( CP_ACP, 0, pSrcFile, -1, szSrcFile, MAX_PATH );
    szSrcFile[MAX_PATH - 1] = 0;

    return CreateTextureFromFile( pDevice, pContext, szSrcFile, ppOutputRV, bSRGB );
}


//--------------------------------------------------------------------------------------
// Device event callbacks
//--------------------------------------------------------------------------------------


//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::OnDestroyDevice()
{
    // Release all resources
    for( size_t j = 0; j < m_TextureCache.size(); ++j )
    {
        SAFE_RELEASE( m_TextureCache[ j ].pSRV11 );
    }
    m_TextureCache.clear();
    m_TextureCache.shrink_to_fit();

    return S_OK;
}


//======================================================================================
// CDXUTTextHelper
//======================================================================================

_Use_decl_annotations_
CDXUTTextHelper::CDXUTTextHelper( ID3D11Device* pd3d11Device, ID3D11DeviceContext* pd3d11DeviceContext, CDXUTDialogResourceManager* pManager, int nLineHeight ) :
    m_clr(0, 0, 0, 0),
    m_pt{ 0, 0 },
    m_nLineHeight{},
    m_pd3d11Device(nullptr),
    m_pd3d11DeviceContext(nullptr),
    m_pManager(nullptr)
{
    Init( nLineHeight );

    m_pd3d11Device = pd3d11Device;
    m_pd3d11DeviceContext = pd3d11DeviceContext;
    m_pManager = pManager;
}

CDXUTTextHelper::~CDXUTTextHelper()
{

}


//--------------------------------------------------------------------------------------
void CDXUTTextHelper::Init( _In_ int nLineHeight )
{
    m_clr = XMFLOAT4( 1, 1, 1, 1 );
    m_pt.x = 0;
    m_pt.y = 0;
    m_nLineHeight = nLineHeight;
    m_pd3d11Device = nullptr;
    m_pd3d11DeviceContext = nullptr;
    m_pManager = nullptr; 

    // Create a blend state if a sprite is passed in
}


//--------------------------------------------------------------------------------------
HRESULT CDXUTTextHelper::DrawFormattedTextLine( _In_z_ const WCHAR* strMsg, ... )
{
    WCHAR strBuffer[512];

    va_list args;
    va_start( args, strMsg );
    vswprintf_s( strBuffer, 512, strMsg, args );
    strBuffer[511] = L'\0';
    va_end( args );

    return DrawTextLine( strBuffer );
}


//--------------------------------------------------------------------------------------
HRESULT CDXUTTextHelper::DrawTextLine( _In_z_ const WCHAR* strMsg )
{
    if( !m_pd3d11DeviceContext )
        return DXUT_ERR_MSGBOX( L"DrawTextLine", E_INVALIDARG );

    HRESULT hr = S_OK;
    RECT rc;
    SetRect( &rc, m_pt.x, m_pt.y, 0, 0 );
    DrawText11DXUT( m_pd3d11Device, m_pd3d11DeviceContext, strMsg, rc, m_clr,
                    (float)m_pManager->m_nBackBufferWidth, (float)m_pManager->m_nBackBufferHeight, false );

    if( FAILED( hr ) )
        return DXTRACE_ERR_MSGBOX( L"DrawText", hr );

    m_pt.y += m_nLineHeight;

    return S_OK;
}


//--------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT CDXUTTextHelper::DrawFormattedTextLine( const RECT& rc, const WCHAR* strMsg, ... )
{
    WCHAR strBuffer[512];

    va_list args;
    va_start( args, strMsg );
    vswprintf_s( strBuffer, 512, strMsg, args );
    strBuffer[511] = L'\0';
    va_end( args );

    return DrawTextLine( rc, strBuffer );
}


//--------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT CDXUTTextHelper::DrawTextLine( const RECT& rc, const WCHAR* strMsg )
{
    if( !m_pd3d11DeviceContext )
        return DXUT_ERR_MSGBOX( L"DrawTextLine", E_INVALIDARG );

    HRESULT hr = S_OK;
    DrawText11DXUT( m_pd3d11Device, m_pd3d11DeviceContext, strMsg, rc, m_clr,
                    (float)m_pManager->m_nBackBufferWidth, (float)m_pManager->m_nBackBufferHeight, false );

    if( FAILED( hr ) )
        return DXTRACE_ERR_MSGBOX( L"DrawText", hr );

    m_pt.y += m_nLineHeight;

    return S_OK;
}


//--------------------------------------------------------------------------------------
void CDXUTTextHelper::Begin()
{
    if( m_pd3d11DeviceContext )
    {
        m_pManager->StoreD3D11State( m_pd3d11DeviceContext );
        m_pManager->ApplyRenderUI11( m_pd3d11DeviceContext );
    }


}


//--------------------------------------------------------------------------------------
void CDXUTTextHelper::End()
{
    if( m_pd3d11DeviceContext )
    {
        m_pManager->RestoreD3D11State( m_pd3d11DeviceContext );
    }
}
