/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (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.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is Mozilla Communicator client code.
 *
 * The Initial Developer of the Original Code is Netscape Communications
 * Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 */

#include "nsSoftwareUpdate.h"
#include "nsXPInstallManager.h"
#include "nsInstallTrigger.h"
#include "nsInstallVersion.h"
#include "nsIDOMInstallTriggerGlobal.h"

#include "nscore.h"
#include "nsIFactory.h"
#include "nsISupports.h"
#include "nsIScriptGlobalObject.h"

#include "nsIPref.h"

#include "nsRepository.h"
#include "nsIServiceManager.h"

#include "nsSpecialSystemDirectory.h"

#include "VerReg.h"

static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kIFactoryIID, NS_IFACTORY_IID);
static NS_DEFINE_IID(kIScriptObjectOwnerIID, NS_ISCRIPTOBJECTOWNER_IID);

static NS_DEFINE_IID(kIInstallTrigger_IID, NS_IDOMINSTALLTRIGGERGLOBAL_IID);
static NS_DEFINE_IID(kIInstallTrigger_CID, NS_SoftwareUpdateInstallTrigger_CID);






nsInstallTrigger::nsInstallTrigger()
{
    mScriptObject   = nsnull;
    NS_INIT_REFCNT();
}

nsInstallTrigger::~nsInstallTrigger()
{
}

NS_IMETHODIMP 
nsInstallTrigger::QueryInterface(REFNSIID aIID,void** aInstancePtr)
{
    if (aInstancePtr == NULL)
    {
        return NS_ERROR_NULL_POINTER;
    }

    // Always NULL result, in case of failure
    *aInstancePtr = NULL;

    if ( aIID.Equals(kIScriptObjectOwnerIID))
    {
        *aInstancePtr = (void*) ((nsIScriptObjectOwner*)this);
        AddRef();
        return NS_OK;
    }
    else if ( aIID.Equals(kIInstallTrigger_IID) )
    {
        *aInstancePtr = (void*) ((nsIDOMInstallTriggerGlobal*)this);
        AddRef();
        return NS_OK;
    }
    else if ( aIID.Equals(kISupportsIID) )
    {
        *aInstancePtr = (void*)(nsISupports*)(nsIScriptObjectOwner*)this;
        AddRef();
        return NS_OK;
    }

     return NS_NOINTERFACE;
}

NS_IMPL_ADDREF(nsInstallTrigger)
NS_IMPL_RELEASE(nsInstallTrigger)



NS_IMETHODIMP 
nsInstallTrigger::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject)
{
    NS_PRECONDITION(nsnull != aScriptObject, "null arg");
    nsresult res = NS_OK;
    
    if (nsnull == mScriptObject) 
    {
         nsIScriptGlobalObject *global = aContext->GetGlobalObject();

        res = NS_NewScriptInstallTriggerGlobal(  aContext, 
                                                (nsISupports *)(nsIDOMInstallTriggerGlobal*)this, 
                                                (nsISupports *)global, 
                                                &mScriptObject);
        NS_IF_RELEASE(global);

    }
  

    *aScriptObject = mScriptObject;
    return res;
}

NS_IMETHODIMP 
nsInstallTrigger::SetScriptObject(void *aScriptObject)
{
  mScriptObject = aScriptObject;
  return NS_OK;
}

static NS_DEFINE_IID(kPrefsIID, NS_IPREF_IID);
static NS_DEFINE_IID(kPrefsCID,  NS_PREF_CID);

NS_IMETHODIMP    
nsInstallTrigger::UpdateEnabled(PRBool* aReturn)
{
    nsIPref * prefs;
    
    nsresult rv = nsServiceManager::GetService(kPrefsCID, 
                                               kPrefsIID,
                                               (nsISupports**) &prefs);


    if ( NS_SUCCEEDED(rv) )
    {
        rv = prefs->GetBoolPref( (const char*) XPINSTALL_ENABLE_PREF, aReturn);

        if (NS_FAILED(rv))
        {
            *aReturn = PR_FALSE;
        }

        NS_RELEASE(prefs);
    }
    else
    {
        *aReturn = PR_FALSE;  /* no prefs manager.  set to false */
    }

    return NS_OK;
}

NS_IMETHODIMP
nsInstallTrigger::Install(nsXPITriggerInfo* aTrigger, PRBool* aReturn)
{
    nsresult rv;
    *aReturn = PR_FALSE;

    PRBool enabled;
    UpdateEnabled(&enabled);
    if (!enabled) 
    {
        delete aTrigger;
        return NS_OK;
    }

    nsXPInstallManager *mgr = new nsXPInstallManager();
    if (mgr)
    {
        // The Install manager will delete itself when done
        rv = mgr->InitManager( aTrigger );
        if (NS_SUCCEEDED(rv))
            *aReturn = PR_TRUE;
    }
    else
    {
        delete aTrigger;
        rv = NS_ERROR_OUT_OF_MEMORY;
    }


    return rv;
}


NS_IMETHODIMP    
nsInstallTrigger::StartSoftwareUpdate(const nsString& aURL, PRInt32 aFlags, PRBool* aReturn)
{
    PRBool enabled;
    nsresult rv = NS_ERROR_OUT_OF_MEMORY;
    *aReturn = PR_FALSE;

    UpdateEnabled(&enabled);
    if (!enabled)
        return NS_OK;

    // The Install manager will delete itself when done, once we've called
    // InitManager. Before then **WE** must delete it
    nsXPInstallManager *mgr = new nsXPInstallManager();
    if (mgr)
    {
        nsXPITriggerInfo* trigger = new nsXPITriggerInfo();
        if ( trigger )
        {
            nsXPITriggerItem* item = new nsXPITriggerItem(0,aURL.GetUnicode());
            if (item)
            {
                trigger->Add( item );
                // The Install manager will delete itself when done
                rv = mgr->InitManager( trigger );
                *aReturn = PR_TRUE;
            }
            else
            {
                rv = NS_ERROR_OUT_OF_MEMORY;
                delete trigger;
                delete mgr;
            }
        }
        else
        {
            rv = NS_ERROR_OUT_OF_MEMORY;
            delete mgr;
        }
    }

    return rv;
}

NS_IMETHODIMP    
nsInstallTrigger::ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, PRInt32 aDiffLevel, const nsString& aVersion, PRInt32 aMode, PRInt32* aReturn)
{
    nsInstallVersion inVersion;
    inVersion.Init(aVersion);
    return ConditionalSoftwareUpdate(aURL, aRegName, aDiffLevel, &inVersion, aMode, aReturn);
}

NS_IMETHODIMP    
nsInstallTrigger::ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, PRInt32 aDiffLevel, nsIDOMInstallVersion* aVersion, PRInt32 aMode, PRInt32* aReturn)
{
    PRBool needJar = PR_FALSE;

    PRBool enabled;

    UpdateEnabled(&enabled);
    if (!enabled)
        return NS_OK;

    if (aURL.IsEmpty() || aVersion == nsnull)
    {
        needJar = PR_TRUE;
    }
    else
    {
        char * regNameCString = aRegName.ToNewCString();

        REGERR status = VR_ValidateComponent( regNameCString );
        
        if ( status == REGERR_NOFIND || status == REGERR_NOFILE )
        {
            // either component is not in the registry or it's a file
            // node and the physical file is missing
            needJar = PR_TRUE;
        }
        else
        {
            VERSION oldVersion;
            PRInt32 diffValue;

            status = VR_GetVersion( regNameCString, &oldVersion );
            nsInstallVersion oldInstallVersion;

            oldInstallVersion.Init(oldVersion.major, 
                                oldVersion.minor, 
                                oldVersion.release, 
                                oldVersion.build);


            if ( status != REGERR_OK )
                needJar = PR_TRUE;
            else if ( aDiffLevel < 0 )
            {
                aVersion->CompareTo(&oldInstallVersion, &diffValue); 
                needJar = (diffValue <= aDiffLevel);
            }
            else
            {
                aVersion->CompareTo(&oldInstallVersion, &diffValue);
                needJar = (diffValue >= aDiffLevel);
            }
        }
    }

    if (needJar)
        return StartSoftwareUpdate(aURL, aMode, aReturn);
    else
        *aReturn = 0;

    return NS_OK;
}

NS_IMETHODIMP    
nsInstallTrigger::ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, nsIDOMInstallVersion* aVersion, PRInt32 aMode, PRInt32* aReturn)
{
    return ConditionalSoftwareUpdate(aURL, aRegName, BLD_DIFF, aVersion, aMode, aReturn);
}

NS_IMETHODIMP    
nsInstallTrigger::ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, const nsString& aVersion, PRInt32 aMode, PRInt32* aReturn)
{
    nsInstallVersion inVersion;
    inVersion.Init(aVersion);
    return ConditionalSoftwareUpdate(aURL, aRegName, BLD_DIFF, &inVersion, aMode, aReturn);
}

NS_IMETHODIMP    
nsInstallTrigger::ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, const nsString& aVersion, PRInt32* aReturn)
{
    nsInstallVersion inVersion;
    inVersion.Init(aVersion);;
    return ConditionalSoftwareUpdate(aURL, aRegName, BLD_DIFF, &inVersion, 0, aReturn);
}

NS_IMETHODIMP    
nsInstallTrigger::ConditionalSoftwareUpdate(const nsString& aURL, const nsString& aRegName, nsIDOMInstallVersion* aVersion, PRInt32* aReturn)
{
    return ConditionalSoftwareUpdate(aURL, aRegName, BLD_DIFF, aVersion, 0, aReturn);
}

NS_IMETHODIMP    
nsInstallTrigger::CompareVersion(const nsString& aRegName, PRInt32 aMajor, PRInt32 aMinor, PRInt32 aRelease, PRInt32 aBuild, PRInt32* aReturn)
{
    nsInstallVersion inVersion;
    inVersion.Init(aMajor, aMinor, aRelease, aBuild);

    return CompareVersion(aRegName, &inVersion, aReturn);
}

NS_IMETHODIMP    
nsInstallTrigger::CompareVersion(const nsString& aRegName, const nsString& aVersion, PRInt32* aReturn)
{
    nsInstallVersion inVersion;
    inVersion.Init(aVersion);

    return CompareVersion(aRegName, &inVersion, aReturn);
}

NS_IMETHODIMP    
nsInstallTrigger::CompareVersion(const nsString& aRegName, nsIDOMInstallVersion* aVersion, PRInt32* aReturn)
{
    *aReturn = EQUAL;  // assume failure.

    PRBool enabled;

    UpdateEnabled(&enabled);
    if (!enabled)
        return NS_OK;

    VERSION              cVersion;
    char*                tempCString;
    REGERR               status;
    nsInstallVersion     regNameVersion;
    
    tempCString = aRegName.ToNewCString();

    status = VR_GetVersion( tempCString, &cVersion );

    /* if we got the version */
    if ( status == REGERR_OK ) 
    {
        if ( VR_ValidateComponent( tempCString ) == REGERR_NOFILE ) 
        {
            regNameVersion.Init(0,0,0,0);
        }
        else
        {
            regNameVersion.Init(cVersion.major, 
                                cVersion.minor, 
                                cVersion.release, 
                                cVersion.build);
        }
    }
    else
        regNameVersion.Init(0,0,0,0);
        
    regNameVersion.CompareTo( aVersion, aReturn );

    if (tempCString)
        Recycle(tempCString);
    
    return NS_OK;
}

NS_IMETHODIMP    
nsInstallTrigger::GetVersion(const nsString& component, nsString& version)
{
    PRBool enabled;

    UpdateEnabled(&enabled);
    if (!enabled)
        return NS_OK;

    VERSION              cVersion;
    char*                tempCString;
    REGERR               status;
    
    tempCString = component.ToNewCString();

    status = VR_GetVersion( tempCString, &cVersion );

    version.Truncate();

    /* if we got the version */
    if ( status == REGERR_OK && VR_ValidateComponent( tempCString ) == REGERR_OK) 
    {
        nsInstallVersion regNameVersion;
        
        regNameVersion.Init(cVersion.major, 
                            cVersion.minor, 
                            cVersion.release, 
                            cVersion.build);

        regNameVersion.ToString(version);
    }
    
    if (tempCString)
         Recycle(tempCString);

    return NS_OK;
}


// this will take a nsIURI, and create a temporary file.  If it is local, we just us it.
 
void
nsInstallTrigger::CreateTempFileFromURL(const nsString& aURL, nsString& tempFileString)
{
    // Checking to see if the url is local

    if ( aURL.EqualsIgnoreCase("file:/", 6) )
    {       
        tempFileString.Assign( nsNSPRPath(nsFileURL(aURL)) );
    }
    else
    {
        nsSpecialSystemDirectory tempFile(nsSpecialSystemDirectory::OS_TemporaryDirectory);
    
        PRInt32 result = aURL.RFindChar('/');
        if (result != -1)
        {    
            nsString jarName;
                       
            aURL.Right(jarName, (aURL.Length() - result) );
            
            PRInt32 argOffset = jarName.RFindChar('?');

            if (argOffset != -1)
            {
                // we need to remove ? and everything after it
                jarName.Truncate(argOffset);
            }
            
            
            tempFile += jarName;
        }
        else
        {   
            tempFile += "xpinstall.jar";
        }

        tempFile.MakeUnique();

        tempFileString.Assign( nsNSPRPath( nsFilePath(tempFile) ) );
    }
}


