/*
 *  PHEX - The pure-java Gnutella-servent.
 *  Copyright (C) 2001 - 2004 Gregor Koukkoullis ( phex <at> kouk <dot> de )
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 *  --- CVS Information ---
 *  $Id: UpdateCheckRunner.java,v 1.6 2004/06/19 18:34:23 gregork Exp $
 */
package phex.update;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.List;

import javax.xml.bind.JAXBException;

import org.apache.commons.lang.SystemUtils;

import phex.common.*;
import phex.event.UpdateNotificationListener;
import phex.gui.common.GUIRegistry;
import phex.share.*;
import phex.share.FileAdministration;
import phex.statistic.*;
import phex.statistic.StatisticProvider;
import phex.utils.*;
import phex.utils.Logger;
import phex.xml.*;

/**
 * 
 * @author gkoukkoullis
 */
public class UpdateCheckRunner implements Runnable
{
    private static final String UPDATE_CHECK_URL = "http://phex.kouk.de/update/update.php";
    //private static final String UPDATE_CHECK_URL = "http://phex.stgkoukkoumob:85/update/update.php";
    
    
    private Throwable updateCheckError;
    private UpdateNotificationListener listener;
    private String releaseVersion;
    private String betaVersion;
    
    public UpdateCheckRunner( UpdateNotificationListener updateListener )
    {
        listener = updateListener;
    }
    
    public String getReleaseVersion()
    {
        return releaseVersion;
    }

    public String getBetaVersion()
    {
        return betaVersion;
    }

    /**
     * Returns a possible Throwable that could be thrown during the update check
     * or null if no error was caught.
     * @return a possible Throwable that could be thrown during the update check
     * or null if no error was caught.
     */
    public Throwable getUpdateCheckError()
    {
        return updateCheckError;
    }
    
    /**
     * @see java.lang.Runnable#run()
     */
    public void run()
    {
        try
        {
            performUpdateCheck();
        }
        catch ( Throwable exp )
        {
            updateCheckError = exp;
        }
    }
    
    private void performUpdateCheck()
    {
        URL url;
        XJBPhex xjbPhex;
        try
        {
            url = new URL( UPDATE_CHECK_URL );
            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
            connection.setUseCaches( false );
            connection.setRequestProperty( "User-Agent", Environment.getPhexVendor() );
            connection.setRequestMethod( "POST" );
            connection.setDoOutput( true );
            connection.setRequestProperty( "Content-Type",
                "text/xml; charset=UTF-8" );
                        
            OutputStream outStream = connection.getOutputStream();
            byte[] data = buildXMLUpdateRequest();
            if ( data == null )
            {
                throw new IOException( "Missing XML update data" );
            }
            outStream.write( data );
            
            // dont need to buffer stream already done by Properties.load()
            InputStream inStream = connection.getInputStream();
            xjbPhex = XMLBuilder.readXJBPhexFromStream( inStream );
        }
        catch ( java.net.MalformedURLException exp )
        {
            updateCheckError = exp;
            Logger.logError( exp );
            throw new RuntimeException( );
        }
        catch ( UnknownHostException exp )
        {
            // can't find way to host
            // this maybe means we have no internet connection
            updateCheckError = exp;
            return;
        }
        catch ( SocketException exp )
        {
            // can't connect... maybe a proxy is in the way...
            updateCheckError = exp;
            return;
        }
        catch ( IOException exp )
        {
            updateCheckError = exp;
            Logger.logWarning( exp );
            return;
        }
        catch ( JAXBException exp )
        {
            updateCheckError = exp;
            Logger.logWarning( exp );
            return;
        }

        ServiceManager.sCfg.lastUpdateCheckTime = System.currentTimeMillis();

        // if you don't care about a notification
        // the check is done anyway even if you don't want a notification
        // to collect statistic of the number of Phex clients in the network.
        if ( !ServiceManager.sCfg.showUpdateNotification )
        {
            ServiceManager.sCfg.save();
            return;
        }
        
        XJBUpdateResponse response = xjbPhex.getUpdateResponse();
        List versionList = response.getVersionList();
        Iterator versionIterator = versionList.iterator();
        XJBUpdateResponse.VersionType latestReleaseVersion = null;
        XJBUpdateResponse.VersionType latestBetaVersion = null;
        
        while ( versionIterator.hasNext() )
        {
            XJBUpdateResponse.VersionType currentVersion =
                (XJBUpdateResponse.VersionType)versionIterator.next();
            if ( currentVersion.isBeta() )
            {
                if ( latestBetaVersion == null || VersionUtils.compare(
                    currentVersion.getId(), latestBetaVersion.getId() ) > 0 )
                {
                    latestBetaVersion = currentVersion;
                }
            }
            else
            {
                if ( latestReleaseVersion == null || VersionUtils.compare(
                    currentVersion.getId(), latestReleaseVersion.getId() ) > 0 )
                {
                    latestReleaseVersion = currentVersion;
                }
            }
        }
        
        
        betaVersion = "0";
        releaseVersion = "0";
        if ( latestBetaVersion != null )
        {
            betaVersion = latestBetaVersion.getId();
        }
        if ( latestReleaseVersion != null )
        {
            releaseVersion = latestReleaseVersion.getId();
        }
        
        int releaseCompare = 0;
        int betaCompare = 0;
        betaCompare = VersionUtils.compare( betaVersion,
            VersionUtils.getProgramVersion() );
        releaseCompare = VersionUtils.compare( releaseVersion,
            VersionUtils.getProgramVersion() );
        
        if ( releaseCompare <= 0 && betaCompare <= 0 )
        {
            ServiceManager.sCfg.save();
            return;
        }
        
        betaCompare = VersionUtils.compare( betaVersion,
            ServiceManager.sCfg.lastBetaUpdateCheckVersion );        
        releaseCompare = VersionUtils.compare( releaseVersion,
            ServiceManager.sCfg.lastUpdateCheckVersion );

        int verDiff = VersionUtils.compare( betaVersion,
            releaseVersion );

        boolean triggerUpdateNotification = false;
        if ( releaseCompare > 0 )
        {
            ServiceManager.sCfg.lastUpdateCheckVersion = releaseVersion;
            triggerUpdateNotification = true;
        }
        if ( betaCompare > 0 )
        {
            ServiceManager.sCfg.lastBetaUpdateCheckVersion = betaVersion;
            triggerUpdateNotification = true;
        }

        if ( verDiff > 0 )
        {
            // reset release version since beta is more up-to-date
            releaseVersion = null;
        }
        else
        {
            // reset beta version since release is the one to go.
            betaVersion = null;
        }

        ServiceManager.sCfg.save();
        if ( triggerUpdateNotification )
        {
            fireUpdateNotification();
        }
    }
    
    private void fireUpdateNotification()
    {
        listener.updateNotification( this );
    }
    
    private byte[] buildXMLUpdateRequest()
    {
        try
        {
            XJBPhex xjbPhex = ObjectFactory.createPhexElement();
            XJBUpdateRequest xjbRequest = ObjectFactory.createXJBUpdateRequest();
            xjbPhex.setUpdateRequest( xjbRequest );
            
            xjbRequest.setCurrentVersion( VersionUtils.getProgramVersion() );
            xjbRequest.setLafUsed( GUIRegistry.getInstance().getUsedLAFClass() );
            xjbRequest.setJavaVersion( System.getProperty( "java.version" ) );
            xjbRequest.setOperatingSystem( SystemUtils.OS_NAME );
            
            Cfg cfg = ServiceManager.sCfg;
            xjbRequest.setHostid( cfg.mProgramClientID.toHexString() );
            xjbRequest.setShowBetaInfo( cfg.showBetaUpdateNotification );
            xjbRequest.setLastInfoId( cfg.lastShownUpdateInfoId );
            
            String lastCheckVersion;
            if ( VersionUtils.compare( cfg.lastUpdateCheckVersion,
                 cfg.lastBetaUpdateCheckVersion ) > 0 )
            {
                lastCheckVersion = cfg.lastUpdateCheckVersion;
            }
            else
            {
                lastCheckVersion = cfg.lastBetaUpdateCheckVersion;
            }
            xjbRequest.setLastCheckVersion( lastCheckVersion );
            
            StatisticsManager statMgr = StatisticsManager.getInstance();
            
            StatisticProvider uptimeProvider = statMgr.getStatisticProvider(
                StatisticsManager.UPTIME_PROVIDER );
            xjbRequest.setAvgUptime(
                ((LongObj)uptimeProvider.getAverageValue()).value );
                
            StatisticProvider downloadProvider = statMgr.getStatisticProvider(
                StatisticProviderConstants.TOTAL_DOWNLOAD_COUNT_PROVIDER );
            xjbRequest.setDownloadCount(
                (int)((LongObj)downloadProvider.getValue()).value );
                
            StatisticProvider uploadProvider = statMgr.getStatisticProvider(
                StatisticProviderConstants.TOTAL_UPLOAD_COUNT_PROVIDER );
            xjbRequest.setUploadCount(
                (int)((LongObj)uploadProvider.getValue()).value );
                
            FileAdministration fileAdmin = ShareManager.getInstance().getFileAdministration();
            xjbRequest.setSharedFiles( fileAdmin.getFileCount() );
            xjbRequest.setSharedSize( fileAdmin.getTotalFileSizeInKb() );
            
            return XMLBuilder.serializeToBytes( xjbPhex );
        }
        catch ( JAXBException exp )
        {
            Logger.logError( exp );
            return null;
        }
    }
}
