/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



#undef UNICODE
#undef _UNICODE

#define _WIN32_WINDOWS 0x0410

#ifdef _MSC_VER
#pragma warning(push, 1) /* disable warnings within system headers */
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <msiquery.h>
#include <shellapi.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include <malloc.h>
#include <assert.h>
#include <string.h>

#ifdef UNICODE
#define _UNICODE
#define _tstring	wstring
#else
#define _tstring	string
#endif
#include <tchar.h>
#include <string>

/** creates a temporary folder with a unique name.

    The returned string is a file URL.
*/
// static std::_tstring createTempFolder()
// {
//     BOOL bExist = FALSE;
//     TCHAR szTempName[MAX_PATH]; 
//     do
//     {
//         bExist = FALSE;
//         // Get the temp path.
//         TCHAR lpPathBuffer[MAX_PATH];
//         DWORD dwRetVal = GetTempPath(MAX_PATH, lpPathBuffer);
//         if (dwRetVal > MAX_PATH || (dwRetVal == 0))
//         {
//             //fprintf (stderr, "GetTempPath failed with error %d.\n", GetLastError());
//             return TEXT("");
//         }          
//         // Create a temporary file.
//         UINT uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files
//                                        "upg",        // temp file name prefix 
//                                        0,            // create unique name 
//                                        szTempName);  // buffer for name 
//         if (uRetVal == 0)
//         {
//             //fprintf (stderr, "GetTempFileName failed with error %d.\n", GetLastError());
//             return TEXT("");
//         }
//         //Delete the file
//         BOOL bDel = DeleteFile(szTempName);    
//         if (FALSE == bDel)
//         {
//             //fprintf(stderr, "Could not delete temp file. Error %d.\n", GetLastError());
//             return TEXT("");
//         }
//         // Create the directory
//         BOOL bDir = CreateDirectory(szTempName, NULL);
//         if (FALSE == bDir)
//         {
//             DWORD error =GetLastError();
//             if (ERROR_ALREADY_EXISTS == error)
//             {
//                 bExist = TRUE;
//             }
//             else
//             {
//                 //fprintf(stderr, "CreateDirectory failed with error %d.\n", error);
//                 return TEXT("");
//             }
//         }
//     } while(bExist);
    
//     std::_tstring cur(szTempName);
//     //make a file URL from the path
//     std::_tstring ret(TEXT("file:///"));
//     for (std::_tstring::iterator i = cur.begin(); i != cur.end(); i++)
//     {
//         if (*i == '\\')
//             ret.append(TEXT("/"));
//         else
//             ret.push_back(*i);
//     }
// //    MessageBox(NULL, ret.c_str(), "createTempFolder", MB_OK);
//     return ret.c_str();
// }

/** deletes the temporary folder.
    The argument must be a file URL.
*/
// static void deleteTempFolder(const std::_tstring& sTempFolder)
// {
//     if (sTempFolder.size() == 0)
//         return;
//     //convert the file URL to a path
//     const std::_tstring path(sTempFolder.substr(8));
//     std::_tstring path2;
// //    MessageBox(NULL, path.c_str(), "del1", MB_OK);        
//     for (std::_tstring::const_iterator i = path.begin(); i != path.end(); i++)
//     {
//         if (*i == '/')
//             path2.append(TEXT("\\"));
//         else
//             path2.push_back(*i);
//     }

//     //We need a null terminated string with two nulls in the end
//     //for the SHFILEOPSTRUCT
//     const TCHAR * szTemp = path2.c_str();
//     size_t size = path2.size();
//     TCHAR * szTemp2 = new TCHAR[size + 2];
//     ZeroMemory(szTemp2, (size + 2) * sizeof(TCHAR));
//     memcpy(szTemp2, szTemp, size * sizeof(TCHAR));   
    
// //    MessageBox(NULL, szTemp2, "del3", MB_OK);
//     SHFILEOPSTRUCT operation = 
//         {
//             NULL,
//             FO_DELETE,
//             szTemp2, 
//             NULL,
//             FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR,
//             FALSE,
//             NULL,
//             NULL
//         };

//     SHFileOperation( &operation);
//     delete [] szTemp2;
// }



static std::_tstring GetMsiProperty( MSIHANDLE handle, const std::_tstring& sProperty )
{
    std::_tstring result;
    TCHAR szDummy[1] = TEXT("");
    DWORD nChars = 0;

    if ( MsiGetProperty( handle, sProperty.c_str(), szDummy, &nChars ) == ERROR_MORE_DATA )
    {
        DWORD nBytes = ++nChars * sizeof(TCHAR);
        LPTSTR buffer = reinterpret_cast<LPTSTR>(_alloca(nBytes));
        ZeroMemory( buffer, nBytes );
        MsiGetProperty(handle, sProperty.c_str(), buffer, &nChars);
        result = buffer;
    }

    return result;
}

/* creates a child process which is specified in lpCommand.

  out_exitCode is the exit code of the child process
  

**/
static BOOL ExecuteCommand( LPCTSTR lpCommand, DWORD * out_exitCode)
{
    BOOL                fSuccess = FALSE;
    STARTUPINFO         si;
    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);

    fSuccess = CreateProcess(
        NULL,
        (LPTSTR)lpCommand,
        NULL,
        NULL,
        FALSE,
        0,
        NULL,
        NULL,
        &si,
        &pi
        );

    if ( fSuccess )
    {
        WaitForSingleObject( pi.hProcess, INFINITE );
        
        if (!GetExitCodeProcess( pi.hProcess, out_exitCode))
            fSuccess = FALSE;
        
        CloseHandle( pi.hProcess );
        CloseHandle( pi.hThread );
    }

    return fSuccess;
}

static BOOL RemoveCompleteDirectory( std::_tstring sPath )
{
    bool bDirectoryRemoved = true;

    std::_tstring mystr;
    std::_tstring sPattern = sPath + TEXT("\\") + TEXT("*.*");
    WIN32_FIND_DATA aFindData;

    // Finding all content in sPath

    HANDLE hFindContent = FindFirstFile( sPattern.c_str(), &aFindData );

    if ( hFindContent != INVALID_HANDLE_VALUE )
    {
        bool fNextFile = false;

        do
        {
            std::_tstring sFileName = aFindData.cFileName;
            std::_tstring sCurrentDir = TEXT(".");
            std::_tstring sParentDir = TEXT("..");

            mystr = "Current short file: " + sFileName;
            // MessageBox(NULL, mystr.c_str(), "Current Content", MB_OK);
        
            if (( strcmp(sFileName.c_str(),sCurrentDir.c_str()) != 0 ) &&
                ( strcmp(sFileName.c_str(),sParentDir.c_str()) != 0 ))
            {
                std::_tstring sCompleteFileName = sPath + TEXT("\\") + sFileName;

                if ( aFindData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY )
                {
                    bool fSuccess = RemoveCompleteDirectory(sCompleteFileName);
                    if ( fSuccess )
                    {
                        mystr = "Successfully removed content of dir " + sCompleteFileName;
                        // MessageBox(NULL, mystr.c_str(), "Removed Directory", MB_OK);
                    }
                    else
                    {
                        mystr = "An error occurred during removing content of " + sCompleteFileName;
                        // MessageBox(NULL, mystr.c_str(), "Error removing directory", MB_OK);
                    }
                }
                else
                {
                    bool fSuccess = DeleteFile( sCompleteFileName.c_str() );
                    if ( fSuccess )
                    {
                        mystr = "Successfully removed file " + sCompleteFileName;
                        // MessageBox(NULL, mystr.c_str(), "Removed File", MB_OK);
                    }
                    else
                    {
                        mystr = "An error occurred during removal of file " + sCompleteFileName;
                        // MessageBox(NULL, mystr.c_str(), "Error removing file", MB_OK);
                    }
                }
            }

            fNextFile = FindNextFile( hFindContent, &aFindData );

        } while ( fNextFile );

        FindClose( hFindContent );

        // empty directory can be removed now
        // RemoveDirectory is only successful, if the last handle to the directory is closed
        // -> first removing content -> closing handle -> remove empty directory

        bool fRemoveDirSuccess = RemoveDirectory(sPath.c_str());

        if ( fRemoveDirSuccess )
        {
            mystr = "Successfully removed dir " + sPath;
            // MessageBox(NULL, mystr.c_str(), "Removed Directory", MB_OK);
        }
        else
        {
            mystr = "An error occurred during removal of empty directory " + sPath;
            // MessageBox(NULL, mystr.c_str(), "Error removing directory", MB_OK);
            bDirectoryRemoved = false;
        }
    }
    
    return bDirectoryRemoved;
}

extern "C" UINT __stdcall RegisterExtensions(MSIHANDLE handle)
{
    // std::_tstring sInstDir = GetMsiProperty( handle, TEXT("INSTALLLOCATION") );
    std::_tstring sInstDir = GetMsiProperty( handle, TEXT("CustomActionData") );
    std::_tstring sUnoPkgFile = sInstDir + TEXT("program\\unopkg.exe");
    std::_tstring mystr;

    WIN32_FIND_DATA aFindFileData;
    bool registrationError = false;

    // Find unopkg.exe
    HANDLE hFindUnopkg = FindFirstFile( sUnoPkgFile.c_str(), &aFindFileData );

    if ( hFindUnopkg != INVALID_HANDLE_VALUE )
    {
        // unopkg.exe exists in program directory
        std::_tstring sCommand = "\"" + sUnoPkgFile + "\" sync";
        
        DWORD exitCode = 0;
        bool fSuccess = ExecuteCommand( sCommand.c_str(), & exitCode);
        
//          if ( fSuccess )
//          {
//              mystr = "Executed successfully!";
//              MessageBox(NULL, mystr.c_str(), "Command", MB_OK);
//          }
//          else
//          {
//              mystr = "An error occurred during execution!";
//              MessageBox(NULL, mystr.c_str(), "Command", MB_OK);
//          }

        if ( ! fSuccess )
        {
            mystr = "ERROR: An error occurred during registration of extensions!";
            MessageBox(NULL, mystr.c_str(), "ERROR", MB_OK); 
            registrationError = true;       
        }
        
        FindClose( hFindUnopkg );
    }
    // else
    // {
    //     mystr = "Error: Did not find " + sUnoPkgFile;
    //     MessageBox(NULL, mystr.c_str(), "Command", MB_OK);
    // }

    if ( registrationError )
    {
        return 1;
    }
    else
    {
        return ERROR_SUCCESS;
    }
}


extern "C" UINT __stdcall RemoveExtensions(MSIHANDLE handle)
{
    std::_tstring mystr;

    // Finding the product with the help of the propery FINDPRODUCT,
    // that contains a Windows Registry key, that points to the install location.

    TCHAR szValue[8192];
    DWORD nValueSize = sizeof(szValue);
    HKEY  hKey;
    std::_tstring sInstDir;
    
    std::_tstring sProductKey = GetMsiProperty( handle, TEXT("FINDPRODUCT") );
    //MessageBox( NULL, sProductKey.c_str(), "Titel", MB_OK );
    
    if ( ERROR_SUCCESS == RegOpenKey( HKEY_CURRENT_USER,  sProductKey.c_str(), &hKey ) )
    {
        if ( ERROR_SUCCESS == RegQueryValueEx( hKey, TEXT("INSTALLLOCATION"), NULL, NULL, (LPBYTE)szValue, &nValueSize ) )
        {
            sInstDir = szValue;
        }
        RegCloseKey( hKey );
    }
    else if ( ERROR_SUCCESS == RegOpenKey( HKEY_LOCAL_MACHINE,  sProductKey.c_str(), &hKey ) )
    {
        if ( ERROR_SUCCESS == RegQueryValueEx( hKey, TEXT("INSTALLLOCATION"), NULL, NULL, (LPBYTE)szValue, &nValueSize ) )
        {
            sInstDir = szValue;
        }
        RegCloseKey( hKey );
    }
    else
    {
        return ERROR_SUCCESS;
    }

    // Removing complete directory "Basis\presets\bundled"

    std::_tstring sCacheDir = sInstDir + TEXT("share\\prereg\\bundled");
    
    bool fSuccess = RemoveCompleteDirectory( sCacheDir );

//     if ( fSuccess )
//     {
//         mystr = "Executed successfully!";
//          MessageBox(NULL, mystr.c_str(), "Main methode", MB_OK);
//     }
//     else
//     {
//         mystr = "An error occurred during execution!";
//         MessageBox(NULL, mystr.c_str(), "Main methode", MB_OK);
//     }

    return ERROR_SUCCESS;
}
