/*
 *  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
 */
package phex.upload;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.util.HashSet;
import java.util.Set;

import phex.common.AlternateLocationContainer;
import phex.common.ServiceManager;
import phex.common.URN;
import phex.common.bandwidth.BandwidthController;
import phex.common.bandwidth.BandwidthManager;
import phex.connection.Connection;
import phex.download.swarming.SWDownloadFile;
import phex.download.swarming.SwarmingManager;
import phex.host.HostAddress;
import phex.http.GnutellaHeaderNames;
import phex.http.GnutellaRequest;
import phex.http.HTTPHeader;
import phex.http.HTTPHeaderNames;
import phex.http.HTTPMessageException;
import phex.http.HTTPProcessor;
import phex.http.HTTPRangeSet;
import phex.http.Range;
import phex.http.HTTPRequest;
import phex.http.HTTPResponse;
import phex.http.XQueueParameters;
import phex.share.FileAdministration;
import phex.share.PartialShareFile;
import phex.share.ShareFile;
import phex.share.ShareManager;
import phex.statistic.SimpleStatisticProvider;
import phex.statistic.StatisticProviderConstants;
import phex.statistic.StatisticsManager;
import phex.utils.GnutellaInputStream;
import phex.utils.IOUtil;
import phex.utils.Logger;

/**
 * The UploadEngine is handling the process of uploading a file. This includes
 * evaluating and responding to the HTTPRequest.
 * 
 * @author Gregor Koukkoullis
 *  
 */
public class UploadEngine
{
    /**
     * The upload buffer size.
     */
    private static final int BUFFER_LENGTH = 1024;

    /**
     * The upload file access reader.
     */
    private RandomAccessFile raFile;

    /**
     * The socket to upload to.
     */
    private Socket socket;

    /**
     * The stream to upload over.
     */
    private OutputStream outStream;

    /**
     * The stream to read over.
     */
    private GnutellaInputStream inStream;

    /**
     * Indicates whether the upload is queued or not.
     */
    private boolean isUploadQueued;

    /**
     * Represents the timestamp that must be passed before the client is allowed
     * to request again.
     */
    private long minNextPollTime;

    /**
     * The start offset of the upload.
     */
    private long startOffset;

    /**
     * The end offset of the upload ( inclusive )
     */
    private long endOffset;

    /**
     * The offset of the upload file to start the upload from.
     */
    private long fileStartOffset;

    /**
     * The current HTTPRequest.
     */
    private HTTPRequest httpRequest;

    /**
     * The upload info object of the current upload.
     */
    private UploadState uploadState;

    /**
     * Indicates whether the connection to the host ip is counted or not.
     */
    private boolean isIPCounted;

    /**
     * The address of the downloading host.
     */
    private HostAddress hostAddress;

    /**
     * This is a List holding all AltLocs already send to this connection during
     * this session. It is used to make sure the same AltLocs are not send twice
     * to the same connection.
     */
    private Set sendAltLocSet;

    /**
     * Flag indicating if the upload is counted inside this upload engine.
     */
    public boolean isUploadCounted;

    /**
     * Indicates whether the connection is persistent or not. Like Keep-Alive
     * connections.
     */
    private boolean isPersistentConnection;

    // TODOTHEX
    private boolean isThexRequest;

    /**
     * The ShareFile currently used for upload.
     */
    private ShareFile uploadShareFile;

    public UploadEngine( Connection connection,
        HTTPRequest httpRequest) throws IOException
    {
        this.socket = connection.getSocket();
        this.inStream = connection.getInputStream();
        connection.setBandwidthController(BandwidthManager.getInstance()
            .getUploadBandwidthController());
        outStream = socket.getOutputStream();
        this.httpRequest = httpRequest;
        isIPCounted = false;
        isUploadCounted = false;
        sendAltLocSet = new HashSet();
    }

    public void startUpload() throws IOException
    {
        Logger.logMessage(Logger.FINE, Logger.UPLOAD,
            "Upload Engine starts upload.");
        try
        {
            do
            {
                try
                {
                    boolean accepted = evaluateHTTPRequest();
                    if (!accepted)
                    {
                        return;
                    }

                    if (!isUploadQueued)
                    {
                        // TODOTHEX
                        //if(isThexRequest)
                        //{
                        //   GnutellaRequest gRequest= checkThexRequest();
                        //   ShareFile shareFile = findShareFile( gRequest );
                        //   if(shareFile!=null) sendDIMEMessage(shareFile);
                        //   else throw new IOException("shareFile doesn't
                        // exits");
                        //}
                        //else
                        //{
                        sendBinaryData();
                        //}
                    }
                }
                catch (IOException exp)
                {
                    // in the case of evaluateHTTPRequest() and sendBinaryData()
                    // we handle a IOException as a aborted status
                    uploadState.setStatus(UploadConstants.STATUS_ABORTED);
                    throw exp;
                }

                if (isPersistentConnection)
                {
                    // in case of readNextHTTPRequest() we handle a IOException
                    // not
                    // as a aborted status since it could mean the connection is
                    // not
                    // kept alive.
                    readNextHTTPRequest();
                }
            }
            while (isPersistentConnection);
        }
        catch (Exception exp)
        {// catch all thats left...
            uploadState.setStatus(UploadConstants.STATUS_ABORTED);
            Logger.logWarning(exp);
        }
        finally
        {
            stopUpload();
            UploadManager uploadMgr = UploadManager.getInstance();
            if (isIPCounted)
            {
                uploadMgr.releaseUploadIP(hostAddress);
            }
            // set to null to give free for gc
            if (uploadState != null)
            {
                uploadState.setUploadEngine(null);
            }

            if (isUploadQueued)
            {
                uploadMgr.removeQueuedUpload(uploadState);
            }
        }
    }

    public void stopUpload()
    {
        IOUtil.closeQuietly( inStream );
        IOUtil.closeQuietly( outStream );

        if (raFile != null)
        {
            try
            {
                raFile.getFD().sync();
            }
            catch (IOException exp)
            {// ignore...
            }
            try
            {
                raFile.close();
            }
            catch (IOException exp)
            {
            }
        }

        if (socket != null)
        {
            try
            {
                socket.close();
            }
            catch (IOException exp)
            {
            }
        }
    }

    private void sendBinaryData() throws IOException
    {
            Logger.logMessage(Logger.FINE, Logger.UPLOAD, "About to send binary range: " + startOffset + " to " + endOffset);

        // the upload is actually starting now..
        // if not yet done we are counting this upload.
        if (!isUploadCounted)
        {
            // count upload even if upload is just starting and not finished
            // yet.
            // swarming uploads fail often anyway.
            uploadShareFile.incUploadCount();
            // Increment the completed uploads count
            StatisticsManager statMgr = StatisticsManager.getInstance();
            SimpleStatisticProvider provider = (SimpleStatisticProvider) statMgr
                .getStatisticProvider(StatisticProviderConstants.SESSION_UPLOAD_COUNT_PROVIDER);
            provider.increment(1);
            isUploadCounted = true;
        }

        uploadState.setStatus(UploadConstants.STATUS_UPLOADING);
        // open file
        File sourceFile = uploadState.getUploadFile();
        raFile = new RandomAccessFile(sourceFile, "r");
        raFile.seek(fileStartOffset);

        BandwidthController throttleController = BandwidthManager.getInstance()
            .getUploadBandwidthController();

        long lengthToUpload = endOffset - startOffset + 1;
        long lengthUploaded = 0;
        int lengthRead = -1;
        int likeToSend;
        // TODO1 the buffer should be pre created with a default buffer size.
        //       initializing always a new buffer in loop is expensive in 
        //       Java and memory consuming.
        byte[] buffer = new byte[BUFFER_LENGTH];
        while (lengthToUpload > 0)
        {
            // make sure we dont send more then requested
            likeToSend = (int) Math.min(lengthToUpload, (long) BUFFER_LENGTH);
            // we may be throttled to less than this amount
            int ableToSend = throttleController.demandBandwidth (likeToSend);
            Logger.logMessage(Logger.FINER, Logger.UPLOAD, "Reading in " + ableToSend + " bytes at " + raFile.getFilePointer() + " from " + sourceFile.getName());
            lengthRead = raFile.read(buffer, 0, ableToSend);
            //  lengthRead should equal ableToSend. I can't see a reason
            //  why it wouldn't....
            if (lengthRead == -1)
            {
                break;
            }

            outStream.write(buffer, 0, lengthRead);

            lengthToUpload -= lengthRead;
            lengthUploaded += lengthRead;

            uploadState.setTransferredDataSize(lengthUploaded);
        }
        uploadState.setStatus(UploadConstants.STATUS_COMPLETED);
    }

    /**
     * Evaluates the http request and send appropiate http responses. Returns
     * true if the request was accepted and the upload can continue.
     * 
     * @return true if the request was accepted and the upload can continue.
     * @throws IOException
     */
    private boolean evaluateHTTPRequest() throws IOException
    {
        Logger.logMessage(Logger.FINE, Logger.UPLOAD, "HTTP Request: "
            + httpRequest.buildHTTPRequestString());

        GnutellaRequest gRequest = httpRequest.getGnutellaRequest();
        // TODOTHEX
        isThexRequest = false;
        if (gRequest == null)
        {
            // TODOTHEX
            //gRequest= checkThexRequest();
            //if (gRequest == null)
            //{
            throw new IOException("Not a Gnutella file request.");
            //}
            //else
            //    isThexRequest=true;
        }

        HTTPHeader header;
        if (gRequest.getURN() == null)
        {
            header = httpRequest
                .getHeader(GnutellaHeaderNames.X_GNUTELLA_CONTENT_URN);
            if (header != null)
            {
                if (URN.isValidURN(header.getValue()))
                {
                    URN urn = new URN(header.getValue());
                    gRequest.setContentURN(urn);
                }
            }
        }

        int port = -1;
        header = httpRequest.getHeader(GnutellaHeaderNames.LISTEN_IP);
        if (header == null)
        {
            header = httpRequest.getHeader(GnutellaHeaderNames.X_MY_ADDRESS);
        }
        if (header != null)
        {
            // parse port
            port = HostAddress.parsePort(header.getValue());
        }
        if (port <= 0)
        {
            port = socket.getPort();
        }
        hostAddress = new HostAddress(socket.getInetAddress().getHostAddress(),
            port);
        UploadManager uploadMgr = UploadManager.getInstance();

        HTTPResponse httpResponse;
        if (!isIPCounted)
        {
            boolean succ = uploadMgr.validateAndCountIP(hostAddress);
            if (!succ)
            {
                httpResponse = new HTTPResponse((short) 503,
                    "Upload Limit Reached for IP", true);
                sendHTTPResponse(httpResponse);
                return false;
            }
            isIPCounted = true;
        }

        ShareFile requestedShareFile = findShareFile(gRequest);
        if (requestedShareFile == null)
        {
            httpResponse = new HTTPResponse((short) 404, "File not found", true);
            sendHTTPResponse(httpResponse);
            return false;
        }

        isUploadQueued = false;
        if (uploadMgr.isHostBusy())
        {
            header = httpRequest.getHeader(GnutellaHeaderNames.X_QUEUE);
            if (header == null || !ServiceManager.sCfg.allowUploadQueuing
                || uploadMgr.isQueueLimitReached())
            {// queueing is not supported
                httpResponse = new HTTPResponse((short) 503,
                    "Upload Limit Reached", true);
                addAltLocResponseHeader(httpResponse, requestedShareFile);
                sendHTTPResponse(httpResponse);
                return false;
            }
            isUploadQueued = true;
        }

        HTTPHeader rangeHeader = null;
        HTTPRangeSet uploadRange = null;
        Range uploadRangeEntry = null;
        if (!isThexRequest)
        {
            rangeHeader = httpRequest.getHeader(HTTPHeaderNames.RANGE);
            if (rangeHeader != null)
            {
                uploadRange = HTTPRangeSet.parseHTTPRangeSet(rangeHeader
                    .getValue());
                if (uploadRange == null)
                {
                    // this is not 416 Requested Range Not Satisfiable since
                    // we have a parsing error on the requested range.
                    httpResponse = new HTTPResponse((short) 500,
                        "Requested Range Not Parseable", true);
                    addAltLocResponseHeader(httpResponse, requestedShareFile);
                    sendHTTPResponse(httpResponse);
                    return false;
                }
            }
            else
            {
                uploadRange = new HTTPRangeSet(0, HTTPRangeSet.NOT_SET);
            }
            uploadRangeEntry = uploadRange.getFirstRange();
            short rangeStatus = requestedShareFile
                .getRangeAvailableStatus(uploadRangeEntry);

            if (rangeStatus != UploadConstants.RANGE_AVAILABLE)
            {
                if (rangeStatus == UploadConstants.RANGE_NOT_AVAILABLE)
                {
                    httpResponse = new HTTPResponse((short) 503,
                        "Requested Range Not Available", true);
                }
                else
                //case: if ( rangeStatus ==
                // UploadConstants.RANGE_NOT_SATISFIABLE )
                {
                    httpResponse = new HTTPResponse((short) 416,
                        "Requested Range Not Satisfiable", true);
                }
                if (requestedShareFile instanceof PartialShareFile)
                {
                    // TODO we could check if the partial file is progressing
                    // and
                    // return a 416 when the range will not come available soon.
                    PartialShareFile pShareFile = (PartialShareFile) requestedShareFile;
                    httpResponse.addHeader(new HTTPHeader(
                        GnutellaHeaderNames.X_AVAILABLE_RANGES, pShareFile
                            .buildXAvailableRangesString()));
                }
                addAltLocResponseHeader(httpResponse, requestedShareFile);
                sendHTTPResponse(httpResponse);
                return false;

            }
        }
        // everything is right... collect upload infos
        String vendor = null;
        header = httpRequest.getHeader(HTTPHeaderNames.USER_AGENT);
        if (header != null)
        {
            vendor = header.getValue();
        }
        else
        {
            vendor = "";
        }

        // check for persistent connection...
        // a connection is assumed to be persistent if its a HTTP 1.1 connection
        // with no 'Connection: close' header. Or a HTTP connection with
        // Connection: Keep-Alive header.
        header = httpRequest.getHeader(HTTPHeaderNames.CONNECTION);
        if (HTTPRequest.HTTP_11.equals(httpRequest.getHTTPVersion()))
        {
            if (header != null && header.getValue().equalsIgnoreCase("CLOSE"))
            {
                isPersistentConnection = false;
            }
            else
            {
                isPersistentConnection = true;
            }
        }
        else
        {
            if (header != null
                && header.getValue().equalsIgnoreCase("KEEP-ALIVE"))
            {
                isPersistentConnection = true;
            }
            else
            {
                isPersistentConnection = false;
            }
        }

        if (isUploadQueued)
        {
            // queueing is supported
            int queuePosition;
            if (uploadState == null)
            {
                uploadState = new UploadState(hostAddress, vendor);
                queuePosition = uploadMgr.addQueuedUpload(uploadState) + 1;
            }
            else
            {
                uploadState.update(hostAddress, vendor);
                queuePosition = uploadMgr.getQueuedPosition(uploadState) + 1;
            }

            uploadState.setStatus(UploadConstants.STATUS_QUEUED);

            int queueLength = uploadMgr.getUploadQueueSize();
            int uploadLimit = ServiceManager.sCfg.mMaxUpload;
            int pollMin = ServiceManager.sCfg.minUploadQueuePollTime;
            int pollMax = ServiceManager.sCfg.maxUploadQueuePollTime;

            httpResponse = new HTTPResponse((short) 503, "Remotely Queued",
                true);
            addAltLocResponseHeader(httpResponse, requestedShareFile);

            XQueueParameters xQueueParas = new XQueueParameters(queuePosition,
                queueLength, uploadLimit, pollMin, pollMax);
            httpResponse.addHeader(new HTTPHeader(GnutellaHeaderNames.X_QUEUE,
                xQueueParas.buildHTTPString()));
            sendHTTPResponse(httpResponse);

            socket.setSoTimeout(pollMax * 1000);
            minNextPollTime = System.currentTimeMillis() + pollMin * 1000;
            return true;
        }

        // standard upload...

        HTTPHeader additionalResponseHeader = null;
        long contentLength = 0;
        if (!isThexRequest)
        {
            if (requestedShareFile instanceof PartialShareFile)
            {
                PartialShareFile pShareFile = (PartialShareFile) requestedShareFile;

                // call adjusts uploadRangeEntry to fit...
                pShareFile.findFittingPartForRange(uploadRangeEntry);
                fileStartOffset = pShareFile.getFileStartOffset();
                additionalResponseHeader = new HTTPHeader(
                    GnutellaHeaderNames.X_AVAILABLE_RANGES, pShareFile
                        .buildXAvailableRangesString());
            }
            else
            {
                fileStartOffset = uploadRangeEntry
                    .getStartOffset(requestedShareFile.getFileSize());
            }
            startOffset = uploadRangeEntry.getStartOffset(requestedShareFile
                .getFileSize());
            endOffset = uploadRangeEntry.getEndOffset(requestedShareFile
                .getFileSize());
            contentLength = endOffset - startOffset + 1;
        }

        URN sharedFileURN = requestedShareFile.getURN();
        // TODOTHEX
        //String rootTigerTree = shareFile.getRootTigerTree();
        if (uploadState == null)
        {
            if (isThexRequest)
            {
                uploadState = new UploadState(hostAddress, vendor);
            }
            else
            {
                uploadState = new UploadState(hostAddress, vendor,
                    requestedShareFile.getFileName(), sharedFileURN,
                    requestedShareFile.getFile(), contentLength);
            }
            uploadMgr.addUploadState(uploadState);
        }
        else
        {
            if (isThexRequest)
            {
                uploadState.update(hostAddress, vendor);
            }
            else
            {
                uploadState.update(hostAddress, vendor, requestedShareFile
                    .getFileName(), sharedFileURN,
                    requestedShareFile.getFile(), contentLength);
            }
        }
        uploadState.setUploadEngine(this);

        // form ok response...
        if (!isThexRequest)
        {
            if (startOffset == 0
                && endOffset == requestedShareFile.getFileSize() - 1)
            {
                httpResponse = new HTTPResponse((short) 200, "OK", true);
            }
            else
            {
                httpResponse = new HTTPResponse((short) 206, "Partial Content",
                    true);
            }

            if (additionalResponseHeader != null)
            {
                httpResponse.addHeader(additionalResponseHeader);
            }
        }
        else
        {
            httpResponse = new HTTPResponse((short) 200, "OK", true);
        }

        // TODO for browser request we might like to return explicite content
        // types:
        // contentType = MimeTypeMapping.getMimeTypeForExtension( ext );
        httpResponse.addHeader(new HTTPHeader(HTTPHeaderNames.CONTENT_TYPE,
            "application/binary"));
        if (!isThexRequest)
        {
            httpResponse.addHeader(new HTTPHeader(
                HTTPHeaderNames.CONTENT_LENGTH, String.valueOf(contentLength)));

            httpResponse.addHeader(new HTTPHeader(
                HTTPHeaderNames.CONTENT_RANGE, "bytes " + startOffset + "-"
                    + endOffset + "/" + requestedShareFile.getFileSize()));
        }
        if (isPersistentConnection)
        {
            httpResponse.addHeader(new HTTPHeader(HTTPHeaderNames.CONNECTION,
                "Keep-Alive"));
        }
        else
        {
            httpResponse.addHeader(new HTTPHeader(HTTPHeaderNames.CONNECTION,
                "close"));
        }

        if (sharedFileURN != null)
        {
            httpResponse.addHeader(new HTTPHeader(
                GnutellaHeaderNames.X_GNUTELLA_CONTENT_URN, sharedFileURN
                    .getAsString()));
        }
        if (!isThexRequest)
        {
            addAltLocResponseHeader(httpResponse, requestedShareFile);
        }
        if ((sharedFileURN != null) && (!isThexRequest))
        {
            // collect alternate locations from request...
            AlternateLocationContainer altLocContainer = requestedShareFile
                .getAltLocContainer();
            HTTPHeader[] headers = httpRequest
                .getHeaders(GnutellaHeaderNames.ALT_LOC);
            altLocContainer.addFromUriResHTTPHeaders(headers);
            headers = httpRequest.getHeaders(GnutellaHeaderNames.X_ALT_LOC);
            altLocContainer.addFromUriResHTTPHeaders(headers);
            headers = httpRequest.getHeaders(GnutellaHeaderNames.X_ALT);
            altLocContainer.addFromCompactIpHTTPHeaders(headers, sharedFileURN);
        }

        // TODOTHEX
//        if ((sharedFileURN != null) && (rootTigerTree != null))
//        {
//            httpResponse.addHeader(new HTTPHeader(
//                GnutellaHeaderNames.X_THEX_URI, "/uri-res/N2R?"
//                    + sharedFileURN.getAsString() + ";" + rootTigerTree));
//            System.out.println("ACABO DE MANDARLE LA CABECERA MIA");
//        }
        sendHTTPResponse(httpResponse);
        socket.setSoTimeout(ServiceManager.sCfg.mSocketTimeout);
        // check if we need to count again..
        // this is the case if we have no PartialShareFile (same
        // PartialShareFile
        // is never equal again, since its always a new instance) and the
        // ShareFile
        // is not equal to the requested file.
        if (uploadShareFile != null
            && !(requestedShareFile instanceof PartialShareFile)
            && uploadShareFile != requestedShareFile)
        {
            isUploadCounted = false;
        }
        uploadShareFile = requestedShareFile;
        minNextPollTime = -1;
        return true;
    }

    // TODOTHEX
//    private GnutellaRequest checkThexRequest() throws IOException
//    {
//        String request = httpRequest.buildHTTPRequestString();
//        Logger.logMessage(Logger.FINE, Logger.UPLOAD, "THEX HTTP Request: "
//            + request);
//
//        if (!(httpRequest.isTHEXGnutellaRequest()))
//        {
//            return null;
//        }
//        GnutellaRequest gRequest = httpRequest.getTHEXGnutellaRequest();
//        if (gRequest == null)
//        {
//            return null;
//        }
//
//        return gRequest;
//    }

    private void addAltLocResponseHeader(HTTPResponse httpResponse,
        ShareFile shareFile)
    {
        HTTPHeader header;
        AlternateLocationContainer altLocContainer = shareFile
            .getAltLocContainer();
        header = altLocContainer.getAltLocHTTPHeaderForAddress(
            GnutellaHeaderNames.X_ALT, hostAddress, sendAltLocSet);
        if (header != null)
        {
            httpResponse.addHeader(header);
        }
    }

    private void readNextHTTPRequest() throws IOException
    {
        try
        {
            httpRequest = HTTPProcessor.parseHTTPRequest(inStream);
            if (minNextPollTime > 0
                && System.currentTimeMillis() < minNextPollTime)
            {// the request came too soon. Disconnect faulty client...
                throw new IOException("Queued host is requesting too soon.");
            }
        }
        catch (HTTPMessageException exp)
        {
            throw new IOException("Invalid HTTP Message: " + exp.getMessage());
        }
    }

    private void sendHTTPResponse(HTTPResponse httpResponse) throws IOException
    {
        String httpResponseStr = httpResponse.buildHTTPResponseString();
        Logger.logMessage(Logger.FINE, Logger.UPLOAD, "HTTP Response: "
            + httpResponseStr);
        outStream.write(httpResponseStr.getBytes());
    }

    private ShareFile findShareFile(GnutellaRequest gRequest)
    {
        ShareFile shareFile = null;

        FileAdministration fileAdmin = ShareManager.getInstance()
            .getFileAdministration();

        // first check for a URN
        URN requestURN = gRequest.getURN();
        if (requestURN != null)
        {// get request contains urn
            if (!(requestURN.isSha1Nid()))
            {
                requestURN = new URN("urn:sha1:" + requestURN.getSHA1Nss());
            }
            shareFile = fileAdmin.getFileByURN(requestURN);
            // look for partials..
            if (shareFile == null && ServiceManager.sCfg.arePartialFilesShared)
            {
                SwarmingManager swMgr = SwarmingManager.getInstance();
                SWDownloadFile dwFile = swMgr.getDownloadFileByURN(requestURN);
                if (dwFile != null)
                {
                    shareFile = new PartialShareFile(dwFile);
                }
            }
        }
        // file index is -1 when parsing was wrong
        else if (gRequest.getFileIndex() != -1)
        {
            int index = gRequest.getFileIndex();
            shareFile = fileAdmin.getFileByIndex(index);
            if (shareFile != null)
            {
                String shareFileName = shareFile.getFileName();
                // if filename dosn't match
                if (!gRequest.getFileName().equalsIgnoreCase(shareFileName))
                {
                    Logger.logMessage(Logger.FINEST, Logger.DOWNLOAD,
                        "Requested index '" + index + "' with filename '"
                            + shareFileName
                            + "' dosn't match request filename '"
                            + gRequest.getFileName() + "'.");
                    shareFile = null;
                }
            }
            else
            {
                // TODO currently this will not work right because the file hash
                // contains
                // the full path name informations of the file. But we only look
                // for the
                // filename.
                // TODO this should be also used if the index returns a file
                // with
                // a different filename then the requested filename
                if (gRequest.getFileName() != null)
                {
                    shareFile = fileAdmin.getFileByName(gRequest.getFileName());
                }
            }
        }
        return shareFile;
    }

    // TODOTHEX
//    private byte[] findThexData(URN contentURN, String rootTigerTree)
//    {
//        String line = null;
//        boolean thexDataFound = false;
//        //serialization string
//        String serializationLine = null;
//        byte[] serialization = null;
//        try
//        {
//            String sha1 = contentURN.getSHA1Nss();
//            File file = ServiceManager.getTHEXFile();
//            FileInputStream fis = new FileInputStream(file);
//            LineNumberReader lnr = new LineNumberReader(new InputStreamReader(
//                fis));
//            while ((line = lnr.readLine()) != null)
//            {
//                if ((line.substring(0, 32)).equals(sha1))
//                {
//                    String root = line.substring(33);
//                    if (root.equals(rootTigerTree))
//                    {
//                        serializationLine = lnr.readLine();
//                        serialization = Base32.decodeBase32(serializationLine);
//                        break;
//                    }
//                }
//                else
//                {
//                    line = lnr.readLine();
//                    line = lnr.readLine();
//                }
//            }
//
//        }
//        catch (IOException exp)
//        {
//            Logger.logError(exp);
//        }
//        return serialization;
//    }
//
//    private void sendDIMEMessage(ShareFile shareFile) throws IOException
//    {
//        //I have to select the serialization of this shareFile
//
//        URN urn = shareFile.getURN();
//        String rootTigerTree = shareFile.getRootTigerTree();
//        byte[] serialization = findThexData(urn, rootTigerTree);
//
//        //We get the THEX metadata
//        ThexHashTree hashTree = new ThexHashTree();
//        hashTree.setFileSize("" + shareFile.getFileSize());
//        hashTree.setFileSegmentSize("1024");
//        hashTree.setDigestAlgorithm("http://open-content.net/spec/digest/tiger");
//        hashTree.setDigestOutputSize("24");
//        hashTree.setSerializedTreeDepth("10");
//        hashTree
//            .setSerializedTreeType("http://open-content.net/spec/thex/breadthfirst");
//        hashTree
//            .setSerializedTreeUri("uuid:09233523-345b-4351-b623-5dsf35sgs5d6");
//
//        byte[] metadata = ThexHashTreeCodec.generateThexHashTreeXML(hashTree);
//        System.out.println("Size of metadata is: " + metadata.length);
//        System.out
//            .println("MetaData in base32 are: " + Base32.encode(metadata));
//        DimeGenerator.generateDIMEMessage(metadata, serialization, outStream);
//
//        System.out.println("Message DIME generated");
//    }
    //////////////////////// THEX /////////////////////////
}
