/*
 *  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.share;

import java.io.File;
import java.util.Iterator;
import java.util.List;

import javax.xml.bind.JAXBException;

import phex.common.AlternateLocation;
import phex.common.AlternateLocationContainer;
import phex.common.URN;
import phex.host.HostAddress;
import phex.http.Range;
import phex.upload.UploadConstants;
import phex.utils.Logger;
import phex.xml.ObjectFactory;
import phex.xml.XJBAlternateLocation;
import phex.xml.XJBSharedFile;

public class ShareFile
{
    /**
     * The backed file object this share file represents.
     */
    private File file;

    /**
     * The unique file index;
     */
    private int fileIndex;

    /**
     * The file name ( file.getName() )
     */
    private String fileName;

    // TODOTHEX
    /**
     *The serialization of the TigerTree of this file
     */
    //private byte[] serialization=null;
    /**
     *Size of the THEX block
     */
    //private static final double fblockSize = 1024.0;
    //private int levelsTotal= 0;
    /**
     * The file size ( file.length() )
     */
    private Long fileSize;

    /**
     * The urn of the file.
     */
    private URN urn;

    // TODOTHEX
    /**
     *The urn bitprint of the file.
     */
    //private URN urnBitprint;
    /**
     * The absolute file name in lower case for optimization.
     */
    private char[] searchCompareTerm;

    // TODOTHEX
    /**
     * The root THEX of the file.
     */
    //private String rootTigerTree;
    /**
     * The number of times the file was searched for.
     */
    private Integer searchCount;

    /**
     * The number of times the file was requested for upload.
     */
    private Integer uploadCount;

    /**
     * A ArrayList of AlternateLocations of the share file.
     */
    private AlternateLocationContainer alternateLocations;

    /**
     * Creates a new ShareFile with its backed file object.
     * @param aFile the backed file object.
     */
    public ShareFile( File aFile )
    {
        file = aFile;
        fileSize = new Long(file.length());
        searchCount = new Integer(0);
        uploadCount = new Integer(0);
        fileName = file.getName();
        
        FileAdministration fileAdmin = ShareManager.getInstance().getFileAdministration();
        String searchCompareString = fileAdmin.getSharedFilePath( file );
        searchCompareTerm = searchCompareString.toCharArray();
    }

    /**
     * Called by subclass to initialize.
     */
    protected ShareFile(long aFileSize)
    {
        fileSize = new Long(aFileSize);
    }

    /**
     * Returns the file urn.
     * @return the file urn.
     */
    public URN getURN()
    {
        return urn;
    }

    /**
     * @param urn The urn to set.
     */
    public void setURN(URN urn)
    {
        this.urn = urn;
    }
    
    // TODOTHEX
    /**
     * Returns the file urnBitprint.
     * @return the file urnBitprint.
     */
    //public URN getURNBitprint()
    //{
    //    return urnBitprint;
    //}
    // TODOTHEX
    /**
     * Returns the root of the TigerTree
     * @return the root of the TigerTree
     */
    //public String getRootTigerTree()
    //{
    //    return rootTigerTree;
    //}
    /**
     * Returns the sha1 nss value of the urn if available.
     * @return the sha1 nss value of the urn if available.
     */
    public String getSHA1()
    {
        if (urn == null || !urn.isSha1Nid())
        {
            return "";
        }
        return urn.getNamespaceSpecificString();
    }

    /**
     * Returns the backed file object.
     * @return the backed file object.
     */
    public File getFile()
    {
        return file;
    }

    /**
     * Returns the file name without path information.
     * @return the file name without path information.
     */
    public String getFileName()
    {
        return fileName;
    }

    /**
     * Returns the unique file index.
     * @return the unique file index.
     */
    public int getFileIndex()
    {
        return fileIndex;
    }

    /**
     * Sets the file index. It must be unique over all ShareFile object
     * @param index the unique file index.
     */
    public void setFileIndex(int index)
    {
        fileIndex = index;
    }

    /**
     * Returns the file size as a long object.
     * @return the file size as a long object.
     */
    public Long getFileSizeObject()
    {
        return fileSize;
    }

    /**
     * Returns the file size.
     * @return the file size.
     */
    public long getFileSize()
    {
        return fileSize.longValue();
    }

    /**
     * Checks if the requested range is satisfiable.
     * @param range the requested range.
     * @return true if the requested range is satisfiable, false otherwise.
     */
    public short getRangeAvailableStatus(Range range)
    {
        long fileSizeVal = fileSize.longValue();
        long startOffset = range.getStartOffset(fileSizeVal);
        if (startOffset < 0 || startOffset >= fileSizeVal)
        {
            return UploadConstants.RANGE_NOT_SATISFIABLE;
        }
        else
        {
            return UploadConstants.RANGE_AVAILABLE;
        }
    }

    /**
     * Returns the container of all known alternate download locations.
     * @return the container of all known alternate download locations.
     */
    public AlternateLocationContainer getAltLocContainer()
    {
        if (alternateLocations == null)
        {// initialize when first time requested.
            alternateLocations = new AlternateLocationContainer(urn);
        }
        return alternateLocations;
    }

    /**
     * Returns the number of times the file was searched for as an Integer object.
     * @return the number of times the file was searched for as an Integer object.
     */
    public Integer getSearchCountObject()
    {
        return searchCount;
    }

    /**
     * Returns the number of times the file was searched for.
     * @return the number of times the file was searched for.
     */
    public int getSearchCount()
    {
        return searchCount.intValue();
    }

    /**
     * Increments the search counter by one.
     */
    public void incSearchCount()
    {
        searchCount = new Integer(searchCount.intValue() + 1);
        ShareManager.getInstance().getFileAdministration()
            .fireSharedFileChanged(this);
    }

    /**
     * Returns the number of times the file was uploaded as an Integer object.
     * @return the number of times the file was uploaded as an Integer object.
     */
    public Integer getUploadCountObject()
    {
        return uploadCount;
    }

    /**
     * Returns the number of times the file was uploaded.
     * @return the number of times the file was uploaded.
     */
    public int getUploadCount()
    {
        return uploadCount.intValue();
    }

    /**
     * Increments the upload counter by one.
     */
    public void incUploadCount()
    {
        uploadCount = new Integer(uploadCount.intValue() + 1);
        ShareManager.getInstance().getFileAdministration()
            .fireSharedFileChanged(this);
    }

    /**
     * Returns the search compare term as an char[] in lower case for optimization.
     * @return the search compare term as an char[] in lower case for optimization.
     */
    public char[] getSearchCompareTerm()
    {
        return searchCompareTerm;
    }

    /**
     * Updates the searchCount, uploadCount and urn from the cached XMLSharedFile
     * object that is used to make ShareFile data persistend.
     * @param xjbFile the cached XJBSharedFile
     * object that is used to make ShareFile data persistend.
     */
    public void updateFromCache(XJBSharedFile xjbFile)
    {
        searchCount = new Integer(xjbFile.getHitCount());
        uploadCount = new Integer(xjbFile.getUploadCount());
        urn = new URN("urn:sha1:" + xjbFile.getSHA1());
        // TODOTHEX
        //rootTigerTree = xjbFile.getTigerTreeRoot();
        List list = xjbFile.getAltLoc();
        Iterator iterator = list.iterator();
        while (iterator.hasNext())
        {
            try
            {
                XJBAlternateLocation xjbAltLoc = (XJBAlternateLocation) iterator
                    .next();
                String hostAddress = xjbAltLoc.getHostAddress();
                String urn = xjbAltLoc.getURN();
                if (urn != null)
                {
                    AlternateLocation altLoc = new AlternateLocation(
                        new HostAddress(hostAddress), new URN(urn));
                    getAltLocContainer().addAlternateLocation(altLoc);
                }
            }
            catch (Exception exp)
            {
                Logger.logError(exp, "AlternateLocation skipped due to error.");
            }
        }
    }

    ///////////////TODOTHEX////////////////////////
//    public int getNumberOfLeaves()
//    {
//        return (int) (Math.ceil((fileSize.longValue()) / fblockSize));
//    }
//
//    protected int getNumberLevelsTotal()
//    {
//        getNumberOfNodes();
//        return levelsTotal;
//    }
//
//    public int getNumberOfNodes()
//    {
//        int total = 0;
//        levelsTotal = 0;
//        int nodesRow = 0;
//        int numberLeaves = getNumberOfLeaves();
//        total += numberLeaves;
//        nodesRow = numberLeaves;
//        do
//        {
//            levelsTotal++;
//            if (nodesRow % 2 == 0)
//            {
//                nodesRow = nodesRow / 2;
//                total += nodesRow;
//            }
//            else
//            {
//                nodesRow = nodesRow - (nodesRow / 2);
//                total += nodesRow;
//            }
//        }
//        while (nodesRow > 1);
//        levelsTotal++;
//        return total;
//    }
//
//    public int getNumberOfNodesAtLevel10(int levelsTotal)
//    {
//        int total = 0;
//        if (levelsTotal == 0) levelsTotal = getNumberLevelsTotal();
//        int levels = levelsTotal - 10;
//        int nodesRow = 0;
//        int count = 1;
//        int numberLeaves = getNumberOfLeaves();
//        if (levels <= 0) return 0;
//
//        total += numberLeaves;
//        nodesRow = numberLeaves;
//        do
//        {
//            count++;
//            if (nodesRow % 2 == 0)
//            {
//                nodesRow = nodesRow / 2;
//                total += nodesRow;
//            }
//            else
//            {
//                nodesRow = nodesRow - (nodesRow / 2);
//                total += nodesRow;
//            }
//        }
//        while ((nodesRow > 1) && (count <= levels));
//
//        return nodesRow;
//    }
//
//    /* Calculate the TigerTree of the file for THEX support */
//    public String calculateTigerTree() throws IOException,
//        NoSuchAlgorithmException
//    {
//        int serializSize = 0;
//        int levelsLeft = 0;
//        int levelsT = getNumberLevelsTotal();
//        int nodesLevel10 = getNumberOfNodesAtLevel10(levelsT);
//        if (nodesLevel10 == 0)
//        {
//            serializSize = getNumberOfLeaves() * 24;
//            levelsLeft = -1;
//        }
//        else
//        {
//            serializSize = nodesLevel10 * 24;
//            levelsLeft = levelsT - 10;
//        }
//
//        MessageDigest tt = new TigerTree(getFileSize(), serializSize,
//            levelsLeft);
//        FileInputStream fis;
//
//        fis = new FileInputStream(file);
//        int read;
//        byte[] in = new byte[1024];
//        while ((read = fis.read(in)) > -1)
//        {
//            tt.update(in, 0, read);
//            //System.out.println("Mando un bloque");
//        }
//        fis.close();
//        byte[] digest = tt.digest();
//
//        String hash = new BigInteger(1, digest).toString(16);
//        while (hash.length() < 48)
//        {
//            hash = "0" + hash;
//        }
//
//        rootTigerTree = Base32.encode(digest);
//        urnBitprint = new URN("urn:bitprint:" + urn.getSHA1Nss() + "."
//            + rootTigerTree);
//        Thex t = ((TigerTree) tt).getThex();
//        serialization = Base32.decodeBase32(t.getSerialization());
//        saveTHEXinFile(t);
//        tt.reset();
//        return rootTigerTree;
//
//    }
//
//    public byte[] getSerialization()
//    {
//        return serialization;
//    }
//
//    private synchronized void saveTHEXinFile(Thex t)
//    {
//        try
//        {
//            if (!findThexData(getSHA1()))
//            {
//                File file = ServiceManager.getTHEXFile();
//                RandomAccessFile out = new RandomAccessFile(file, "rw");
//                out.seek(out.length());
//
//                out.writeBytes(getSHA1() + ":" + t.getRoot() + "\n");
//                out.writeBytes(t.getSerialization() + "\n");
//                out.writeBytes("\n");
//
//                out.close();
//            }
//        }
//        catch (IOException exp)
//        {
//            Logger.logError(exp);
//        }
//    }
//
//    private boolean findThexData(String sha1)
//    {
//        String line = null;
//        boolean thexDataFound = false;
//        //serialization string
//        //String serializationLine=null;
//        try
//        {
//
//            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))
//                {
//                    thexDataFound = true;
//                    fis.close();
//                    break;
//                    //serializationLine = lnr.readLine();
//                    //byte[] serialization = Base32.decodeBase32(serializationLine);
//
//                }
//                else
//                {
//                    line = lnr.readLine();
//                    line = lnr.readLine();
//                }
//            }
//            fis.close();
//        }
//        catch (IOException exp)
//        {
//            Logger.logError(exp);
//
//            return false;
//        }
//        return thexDataFound;
//    }

    ///////////////ENDTHEX////////////////////////

    public XJBSharedFile createXJBSharedFile() throws JAXBException
    {
        XJBSharedFile xjbFile = ObjectFactory.createXJBSharedFile();
        xjbFile.setFileName(file.getAbsolutePath());
        xjbFile.setSHA1(getSHA1());
        // TODOTHEX
        //xjbFile.setTigerTreeRoot(rootTigerTree);
        xjbFile.setLastModified(file.lastModified());
        xjbFile.setHitCount(searchCount.intValue());
        xjbFile.setUploadCount(uploadCount.intValue());

        if (alternateLocations != null)
        {
            alternateLocations.createXJBAlternateLocationList(xjbFile
                .getAltLoc());
        }
        return xjbFile;
    }

    public String toString()
    {
        return super.toString() + " " + fileName + "  " + fileIndex;
    }
}
