package bt.data;

import bt.BtException;
import bt.metainfo.Torrent;
import bt.metainfo.TorrentFile;
import bt.service.IConfigurationService;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class DataDescriptor implements IDataDescriptor {

    private DataAccessFactory dataAccessFactory;
    private IConfigurationService configurationService;

    private Torrent torrent;
    private List<IChunkDescriptor> chunkDescriptors;

    public DataDescriptor(DataAccessFactory dataAccessFactory, IConfigurationService configurationService,
                          Torrent torrent) {

        this.dataAccessFactory = dataAccessFactory;
        this.configurationService = configurationService;
        this.torrent = torrent;

        init();
    }

    private void init() {

        List<TorrentFile> torrentFiles = torrent.getFiles();

        int filesCount = torrentFiles.size();
        long totalSize = torrent.getSize();
        long chunkSize = torrent.getChunkSize();

        List<IChunkDescriptor> chunkDescriptors = new ArrayList<>((int) Math.ceil(totalSize / chunkSize) + 1);
        Iterator<byte[]> chunkHashes = torrent.getChunkHashes().iterator();
        DataAccess[] files = new DataAccess[filesCount];

        long chunkOffset = 0,
             totalSizeOfFiles = 0;

        int firstFileInChunkIndex = 0;

        for (int currentFileIndex = 0; currentFileIndex < filesCount; currentFileIndex++) {

            TorrentFile torrentFile = torrentFiles.get(currentFileIndex);

            long fileSize = torrentFile.getSize();
            files[currentFileIndex] = dataAccessFactory.getOrCreateDataAccess(torrent, torrentFile);
            totalSizeOfFiles += fileSize;

            if (totalSizeOfFiles >= chunkSize) {

                do {
                    long limitInCurrentFile = chunkSize - (totalSizeOfFiles - fileSize);

                    if (!chunkHashes.hasNext()) {
                        // TODO: this should probably be handled in DefaultTorrent builder
                        throw new BtException("Wrong number of chunk hashes in the torrent: too few");
                    }

                    chunkDescriptors.add(new ChunkDescriptor(
                            Arrays.copyOfRange(files, firstFileInChunkIndex, currentFileIndex + 1),
                            chunkOffset, limitInCurrentFile, chunkHashes.next(), configurationService.getTransferBlockSize()
                    ));

                    firstFileInChunkIndex = currentFileIndex;
                    chunkOffset = limitInCurrentFile;

                    totalSizeOfFiles -= chunkSize;

                // if surplus is bigger than the chunk size,
                // then we need to catch up and create more than one chunk
                } while (totalSizeOfFiles >= chunkSize);

                if (totalSizeOfFiles == 0) {
                    // no bytes left in the current file,
                    // new chunk will begin with the next file
                    firstFileInChunkIndex++;
                    chunkOffset = 0;
                } else if (currentFileIndex == filesCount - 1) {
                    // create chunk for the remainder of the last file
                    chunkDescriptors.add(new ChunkDescriptor(
                            Arrays.copyOfRange(files, firstFileInChunkIndex, currentFileIndex + 1),
                            chunkOffset, fileSize, chunkHashes.next(), configurationService.getTransferBlockSize()
                    ));
                }
            }
        }

        if (chunkHashes.hasNext()) {
            throw new BtException("Wrong number of chunk hashes in the torrent: too many");
        }

        this.chunkDescriptors = chunkDescriptors;
    }

    public List<IChunkDescriptor> getChunkDescriptors() {
        return chunkDescriptors;
    }
}

@@ -1031,24 +1031,201 @@
 unkSize();%0A%0A
+        long transferBlockSize = configurationService.getTransferBlockSize();%0A        if (transferBlockSize %3E chunkSize) %7B%0A            transferBlockSize = chunkSize;%0A        %7D%0A%0A
         List
@@ -1323,16 +1323,16 @@
 ) + 1);%0A
-
         
@@ -2601,72 +2601,25 @@
 (), 
-configurationService.getTransferBlockSize()%0A                    
+transferBlockSize
 ));%0A
@@ -3362,24 +3362,219 @@
 e last file%0A
+                    long remaining = fileSize - chunkOffset;%0A                    if (transferBlockSize %3E remaining) %7B%0A                        transferBlockSize = remaining;%0A                    %7D%0A
             
@@ -3798,72 +3798,25 @@
 (), 
-configurationService.getTransferBlockSize()%0A                    
+transferBlockSize
 ));%0A

Fix for case when last chunk's size is lesser than transfer block size