/*******************************************************************************
 *
 *  (C) COPYRIGHT MICROSOFT CORPORATION, 2000
 *
 *  TITLE:       UNIQFILE.H
 *
 *  VERSION:     1.0
 *
 *  AUTHOR:      ShaunIv
 *
 *  DATE:        7/7/2000
 *
 *  DESCRIPTION: Creates a list of existing files in a directory, and ensures
 *               that there new ones are not duplicates of these.
 *
 *******************************************************************************/
#ifndef __UNIQFILE_H_INCLUDED
#define __UNIQFILE_H_INCLUDED

#include <windows.h>
#include <simstr.h>
#include <wiacrc32.h>

class CFileUniquenessInformation
{
public:
    CSimpleString m_strFileName;    // The full path to the file
    ULONGLONG     m_nFileSize;      // The size of the file
    mutable DWORD m_dwCrc;          // The file's CRC.  0 means uninitialized.  It is mutable because it can change in the accessor function

public:
    explicit CFileUniquenessInformation( LPCTSTR pszFileName=NULL, ULONGLONG nFileSize=0, DWORD dwCrc=0 )
      : m_strFileName(pszFileName),
        m_nFileSize(nFileSize),
        m_dwCrc(dwCrc)
    {
    }
    CFileUniquenessInformation( CFileUniquenessInformation &other )
      : m_strFileName(other.FileName()),
        m_nFileSize(other.FileSize()),
        m_dwCrc(other.Crc(false))
    {
    }
    CFileUniquenessInformation &operator=( const CFileUniquenessInformation &other )
    {
        if (this != &other)
        {
            m_strFileName = other.FileName();
            m_nFileSize = other.FileSize();
            m_dwCrc = other.Crc(false);
        }
        return *this;
    }
    const CSimpleString &FileName(void) const
    {
        return m_strFileName;
    }
    const ULONGLONG FileSize(void) const
    {
        return m_nFileSize;
    }
    DWORD Crc( bool bCalculate = true ) const
    {
        //
        // Only calculate it if we have to
        //
        if (!m_dwCrc && bCalculate)
        {
            m_dwCrc = WiaCrc32::GenerateCrc32File(m_strFileName);
        }
        return m_dwCrc;
    }
};

class CFileUniquenessList
{
private:
    CSimpleDynamicArray<CFileUniquenessInformation> m_FileList;

private:
    CFileUniquenessList( const CFileUniquenessList & );
    CFileUniquenessList &operator=( const CFileUniquenessList & );

public:
    CFileUniquenessList( LPCTSTR pszDirectory = NULL )
    {
        if (pszDirectory && lstrlen(pszDirectory))
        {
            InitializeFileList(pszDirectory);
        }
    }
    void InitializeFileList( LPCTSTR pszDirectory )
    {
        //
        // Empty the file list
        //
        m_FileList.Destroy();

        //
        // Save the directory name
        //
        CSimpleString strDirectory = pszDirectory;

        //
        // Make sure we have a trailing backslash
        //
        if (!strDirectory.MatchLastCharacter(TEXT('\\')))
        {
            strDirectory += TEXT("\\");
        }

        //
        //  Find all of the files in this directory
        //
        WIN32_FIND_DATA Win32FindData = {0};
        HANDLE hFind = FindFirstFile( strDirectory + CSimpleString(TEXT("*.*")), &Win32FindData );
        if (hFind != INVALID_HANDLE_VALUE)
        {
            BOOL bContinue = TRUE;
            while (bContinue)
            {
                //
                // Add the file to the list
                //
                ULARGE_INTEGER nFileSize;
                nFileSize.LowPart = Win32FindData.nFileSizeLow;
                nFileSize.HighPart = Win32FindData.nFileSizeHigh;
                m_FileList.Append( CFileUniquenessInformation( strDirectory + CSimpleString(Win32FindData.cFileName), nFileSize.QuadPart ));
                bContinue = FindNextFile( hFind, &Win32FindData );
            }
            FindClose(hFind);
        }
    }
    int FindIdenticalFile( LPCTSTR pszFileName, bool bAddIfUnsuccessful )
    {
        //
        // Assume failure
        //
        int nIndex = -1;

        //
        // Open the file for reading
        //
        HANDLE hFile = CreateFile( pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
        if (INVALID_HANDLE_VALUE != hFile)
        {
            //
            // Get the file size and make sure we didn't have an error
            //
            ULARGE_INTEGER nFileSize;
            nFileSize.LowPart = GetFileSize( hFile, &nFileSize.HighPart );
            if (nFileSize.LowPart != static_cast<DWORD>(-1) || GetLastError() == NO_ERROR)
            {
                //
                // We are only going to generate this file's CRC if we have to
                //
                DWORD dwCrc = 0;

                //
                // Loop through all of the files in this list
                //
                for (int i=0;i<m_FileList.Size();i++)
                {
                    //
                    // Look for ones that have have size
                    //
                    if (m_FileList[i].FileSize() == nFileSize.QuadPart)
                    {
                        //
                        // If we haven't calculated this file's CRC, do so now and save it
                        //
                        if (!dwCrc)
                        {
                            dwCrc = WiaCrc32::GenerateCrc32Handle(hFile);
                        }

                        //
                        // If these files have the same size and CRC, they are identical, so quit the loop
                        //
                        if (m_FileList[i].Crc() == dwCrc)
                        {
                            nIndex = i;
                            break;
                        }
                    }
                }

                //
                // If we didn't find it in the list, add it if the caller requested it
                //
                if (nIndex == -1 && bAddIfUnsuccessful)
                {
                    m_FileList.Append( CFileUniquenessInformation( pszFileName, nFileSize.QuadPart, dwCrc ) );
                }
            }

            //
            // Close the file
            //
            CloseHandle(hFile);
        }
        return nIndex;
    }
    CSimpleString GetFileName( int nIndex )
    {
        //
        // Get the file name at index nIndex
        //
        CSimpleString strResult;
        if (nIndex >= 0 && nIndex < m_FileList.Size())
        {
            strResult = m_FileList[nIndex].FileName();
        }
        return strResult;
    }
};


#endif //__UNIQFILE_H_INCLUDED