//=====================================================================
// Copyright 2020-2024 (c), Advanced Micro Devices, Inc. All rights reserved.
// Copyright 2008 (c), ATI Technologies Inc. All rights reserved.
//=====================================================================
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// UtilFuncs.cpp : Source file for utility functions
//

#include "utilfuncs.h"

#ifndef ASSERT
#include <assert.h>
#define ASSERT assert
#endif  // !ASSERT

#ifdef _WIN32
// Windows Header Files:
#include <shobjidl.h>
#include <shlwapi.h>
#include <tchar.h>
#include <windows.h>
#pragma warning(disable : 4201)  //nameless struct/union
#endif

void SwizzleBytes(void* src, unsigned long numBytes)
{
    unsigned char tmp[8];  // large enough to hold a double
    unsigned long max, i;

    ASSERT(src);
    if ((numBytes == 0) || (numBytes == 1) || (numBytes > 8))
        return;

    char* pSrc = (char*)src;
    max        = numBytes - 1;
    for (i = 0; i < numBytes; i++)
        tmp[(max - i)] = pSrc[i];

    for (i = 0; i < numBytes; i++)
        pSrc[i] = tmp[i];
}

float HalfToFloat(uint16_t h)
{
    union FP32
    {
        uint32_t u;
        float    f;
        struct
        {
            unsigned Mantissa : 23;
            unsigned Exponent : 8;
            unsigned Sign : 1;
        };
    };

    const FP32 magic      = {(254 - 15) << 23};
    const FP32 was_infnan = {(127 + 16) << 23};

    FP32 o;
    o.u = (h & 0x7fff) << 13;  // exponent/mantissa bits
    o.f *= magic.f;            // exponent adjust
    if (o.f >= was_infnan.f)   // check Inf/NaN
        o.u |= 255 << 23;
    o.u |= (h & 0x8000) << 16;  // sign bit
    return o.f;
}

void getFileNameExt(const char* FilePathName, char* fnameExt, int maxbuffsize)
{
#ifdef _WIN32
    char drive[_MAX_DRIVE];
    char dir[_MAX_DIR];
    char ext[_MAX_EXT];
    char fname[_MAX_FNAME];
    _splitpath_s(FilePathName, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT);
    snprintf(fnameExt, maxbuffsize, "%s%s", fname, ext);
#else
    std::string fname = CMP_GetFileNameAndExt(FilePathName);
    snprintf(fnameExt, maxbuffsize, "%s", fname.c_str());
#endif
}

#ifdef _WIN32

typedef struct
{
    TCHAR* pszName;
    HWND   hWnd;
} FTLWData;

#define MAX_WINDOW_TEXT 256

//BOOL CALLBACK FTLWProc(HWND hwnd, LPARAM lParam)
//{
//    if(hwnd && lParam)
//    {
//        FTLWData* pFTLWData = reinterpret_cast<FTLWData*>(lParam);
//
//        TCHAR szWindowText[MAX_WINDOW_TEXT];
//        if(GetWindowText(hwnd, szWindowText, MAX_WINDOW_TEXT))
//        {
//            if(_tcsncmp(szWindowText, pFTLWData->pszName, _tcslen(pFTLWData->pszName)) == 0)
//            {
//                pFTLWData->hWnd = hwnd;
//                return FALSE;
//            }
//        }
//    }
//    return TRUE;
//}

//HWND FindTopLevelWindow(TCHAR* pszName)
//{
//    if(pszName == NULL || *pszName == NULL)
//        return NULL;
//
//    FTLWData ftlwData;
//    ftlwData.pszName = pszName;
//    ftlwData.hWnd = NULL;
//    EnumWindows(FTLWProc, reinterpret_cast<LPARAM>(&ftlwData));
//
//    return ftlwData.hWnd;
//}

bool writeObjFileState(std::string filename, std::string state)
{
    std::ofstream file;
    try
    {
        file.open(filename, std::ios::app);
        file << state << std::endl;
        file.close();
    }
    catch (...)
    {
        return false;
    }

    return true;
}

std::string readObjFileState(std::string filename)
{
    std::ifstream objfile(filename, std::ios::in);

    if (objfile)
    {
        //get file size
        objfile.seekg(0, objfile.end);
        std::streamoff length = objfile.tellg();
        objfile.seekg(0, objfile.beg);

        // char c = '\0';
        std::string lastLine = "";
        //loop from the end of the file, skip endline and endfile characters
        //last line suppose consist of 14 characters for cmpcopy of obj
        for (int i = (int)length - 2; i > 0; i -= (int)CMP_STATENUM)
        {
            objfile.seekg(i);
            //read last line
            std::getline(objfile, lastLine);
            if (lastLine == CMP_COPY)
            {
                return CMP_COPY;
            }
            if (lastLine == CMP_PROCESSED)
            {
                return CMP_PROCESSED;
            }
            // break out of the loop if last line is passed (14 characters + 2 (EOF and EOL))
            if (i < length - CMP_STATENUM - 2)
            {
                return CMP_ORIGINAL;
            }
        }
    }

    return CMP_FILE_ERROR;
}

#endif

#ifdef __AFX_H__
CString GetDirectory(const CString& strPath)
{
    int nIndex = strPath.ReverseFind('\\');

    if (nIndex <= 0)  // No extension
        return CString(_T(""));
    else
    {
        CString strDirectory(strPath.Left(nIndex + 1));
        return strDirectory;
    }
}

CString GetFilename(const CString& strPath)
{
    int nIndex = strPath.ReverseFind('\\');

    if (nIndex == -1)  // No extension
        return CString(_T(""));
    else
    {
        CString strFilename(strPath.Mid(nIndex + 1));
        return strFilename;
    }
}

CString GetExtension(const CString& strFilename)
{
    int nIndex = strFilename.ReverseFind('.');

    if (nIndex == -1)  // No extension
        return CString(_T(""));
    else
    {
        CString strExt(strFilename.Mid(nIndex + 1));
        strExt.MakeLower();
        return strExt;
    }
}

CString RemoveExtension(const CString& strFilename)
{
    int nIndex = strFilename.ReverseFind('.');

    if (nIndex == -1)  // No extension
        return strFilename;
    else
        return strFilename.Left(nIndex);
}

bool FileExists(const CString& strFilename)
{
    if (strFilename.IsEmpty())
        return FALSE;

    // SHGetFileInfo fails when forward slashes are used so replace them with back slashes
    CString strFixedFilename = strFilename;
    strFixedFilename.Replace('/', '\\');

    SHFILEINFO shFileInfo;
    if (SHGetFileInfo(strFixedFilename, 0, &shFileInfo, sizeof(shFileInfo), SHGFI_ATTRIBUTES))
        if (!(shFileInfo.dwAttributes & SFGAO_FOLDER))
            return TRUE;
    return FALSE;
}

bool DirectoryExists(const CString& strDirectory)
{
    if (strDirectory.IsEmpty())
        return FALSE;

    // SHGetFileInfo fails when forward slashes are used so replace them with back slashes
    CString strFixedDirectory = strDirectory;
    strFixedDirectory.Replace('/', '\\');

    SHFILEINFO shFileInfo;
    if (SHGetFileInfo(strFixedDirectory, 0, &shFileInfo, sizeof(shFileInfo), SHGFI_ATTRIBUTES))
        if (shFileInfo.dwAttributes & SFGAO_FOLDER)
            return TRUE;
    return FALSE;
}

BOOL IsFullPath(const CString& strPath)
{
    return DirectoryExists(GetDirectory(strPath));
}

static const char* f_pszInvalidNames[] = {
    _T("CON"),  _T("PRN"),  _T("AUX"),  _T("CLOCK$"), _T("NUL"),  _T("COM0"), _T("COM1"), _T("COM2"), _T("COM3"),
    _T("COM4"), _T("COM5"), _T("COM6"), _T("COM7"),   _T("COM8"), _T("COM9"), _T("LPT0"), _T("LPT1"), _T("LPT2"),
    _T("LPT3"), _T("LPT4"), _T("LPT5"), _T("LPT6"),   _T("LPT7"), _T("LPT8"), _T("LPT9"),
};

static const DWORD f_dwInvalidNames = sizeof(f_pszInvalidNames) / sizeof(f_pszInvalidNames[0]);

BOOL IsValidName(const CString& strName)
{
    for (DWORD i = 0; i < f_dwInvalidNames; i++)
        if (strName.CompareNoCase(f_pszInvalidNames[i]) == 0)
            return FALSE;
    return TRUE;
}

BOOL IsValidSubPath(CString strPath)
{
    int nToken = max(strPath.ReverseFind('\\'), strPath.ReverseFind('/'));

    if (nToken == strPath.GetLength())
        return IsValidSubPath(strPath.Left(nToken - 1));

    if (nToken > 0)
    {
        if (!IsValidName(strPath.Mid(nToken + 1)))
            return FALSE;

        return IsValidSubPath(strPath.Left(nToken));
    }

    return TRUE;
}

BOOL IsValidPath(const CString& strPath)
{
    if (!PathSearchAndQualify(strPath, NULL, 0))
        return FALSE;

    if (strPath.FindOneOf(_T("\"|<>*?")) != -1)
        return FALSE;

    if (strPath.Find(':', 2) != -1)
        return FALSE;

    return IsValidSubPath(strPath);
}

BOOL CreateDirectory(const CString& strDirectory)
{
    int nIndex = strDirectory.ReverseFind('\\');
    if (nIndex == -1)
        nIndex = strDirectory.ReverseFind('/');

    if (nIndex > 2)
    {
        CString strSubDirectory = strDirectory.Mid(0, nIndex);
        CreateDirectory(strSubDirectory);
    }

    if (CreateDirectory(strDirectory, NULL))
        return TRUE;
    else if (GetLastError() == ERROR_ALREADY_EXISTS)
        return TRUE;
    else
        return FALSE;
}

const TCHAR* GetCurrentFilter(const TCHAR* szFilter, int nIndex)
{
    ASSERT(szFilter);
    ASSERT(szFilter[0]);
    ASSERT(nIndex >= 0);

    while (szFilter && szFilter[0] && --nIndex)
    {
        szFilter += _tcslen(szFilter) + 1;
        szFilter += _tcslen(szFilter) + 1;
    }

    if (nIndex == 0)
        return szFilter;

    return NULL;
}

BOOL GetFilterListFromOFN(CStringList& strFilterList, const OPENFILENAME& ofn)
{
    ASSERT(ofn.lpstrFilter);
    ASSERT(ofn.lpstrFilter[0]);
    ASSERT(ofn.nFilterIndex > 0);

    if (ofn.lpstrFilter && ofn.lpstrFilter[0] && ofn.nFilterIndex > 0)
    {
        TCHAR* pszFilter = const_cast<TCHAR*>(ofn.lpstrFilter);
        int    nIndex    = ofn.nFilterIndex;
        while (pszFilter && pszFilter[0] && --nIndex)
        {
            pszFilter += _tcslen(pszFilter) + 1;
            pszFilter += _tcslen(pszFilter) + 1;
        }
        if (pszFilter)
        {
            pszFilter += _tcslen(pszFilter) + 1;
            TCHAR* pszToken = NULL;
            pszFilter       = _tcstok_s(pszFilter, _T(";"), &pszToken);
            while (pszFilter)
            {
                strFilterList.AddTail(pszFilter);
                pszFilter = _tcstok_s(NULL, _T(";"), &pszToken);
            }

            return TRUE;
        }
    }
    return FALSE;
}

void GetComboText(const CComboBox& combo, CString& rString)
{
    if (combo.GetCurSel() != -1)
        combo.GetLBText(combo.GetCurSel(), rString);
    else
        combo.GetWindowText(rString);
}

#ifdef _WIN32
int GetUsableTitleWidth(HWND hWnd)
{
    if (hWnd == NULL)
        return 0;

    CRect rect;
    ::GetWindowRect(hWnd, rect);

    WINDOWINFO wi;
    wi.cbSize = sizeof(wi);
    GetWindowInfo(hWnd, &wi);

    int nWidth = rect.Width() - (wi.cxWindowBorders * 2);

    DWORD dwButtonSize = GetSystemMetrics(SM_CXSIZE) + 2;
    if (!(wi.dwExStyle & WS_EX_TOOLWINDOW))
        nWidth -= dwButtonSize;

    if (wi.dwStyle & WS_SYSMENU)
        nWidth -= dwButtonSize;

    if (wi.dwStyle & WS_MAXIMIZEBOX)
        nWidth -= dwButtonSize;

    if (wi.dwStyle & WS_MINIMIZEBOX)
        nWidth -= dwButtonSize;

    return nWidth;
}
#endif
#endif  // __AFX_H__
