package org.j7zip.SevenZip.Archive.SevenZip;

import org.j7zip.Common.BoolVector;
import org.j7zip.SevenZip.HRESULT;
import org.j7zip.SevenZip.Archive.IArchiveExtractCallback;
import org.j7zip.SevenZip.Archive.IInArchive;
import org.j7zip.SevenZip.Archive.Common.OutStreamWithCRC;


class FolderOutStream extends java.io.OutputStream {
    OutStreamWithCRC _outStreamWithHashSpec;
    java.io.OutputStream _outStreamWithHash;
    
    ArchiveDatabaseEx _archiveDatabase;
    BoolVector _extractStatuses;
    int _startIndex;
    int _ref2Offset;
    IArchiveExtractCallback _extractCallback;
    boolean _testMode;
    int _currentIndex;
    boolean _fileIsOpen;
   
    long _filePos;

    public FolderOutStream() {
        _outStreamWithHashSpec = new OutStreamWithCRC();
        _outStreamWithHash = _outStreamWithHashSpec;
    }
    
    public int Init(
            ArchiveDatabaseEx archiveDatabase,
            int ref2Offset,
            int startIndex,
            BoolVector extractStatuses,
            IArchiveExtractCallback extractCallback,
            boolean testMode) throws java.io.IOException {
        _archiveDatabase = archiveDatabase;
        _ref2Offset = ref2Offset;
        _startIndex = startIndex;
        
        _extractStatuses = extractStatuses;
        _extractCallback = extractCallback;
        _testMode = testMode;
        
        _currentIndex = 0;
        _fileIsOpen = false;
        return WriteEmptyFiles();
    }
    
    int OpenFile() throws java.io.IOException {
        int askMode;
        if(_extractStatuses.get(_currentIndex))
            askMode = _testMode ?
                IInArchive.NExtract_NAskMode_kTest :
                IInArchive.NExtract_NAskMode_kExtract;
        else
            askMode = IInArchive.NExtract_NAskMode_kSkip;
        
        
        int index = _startIndex + _currentIndex;
        
        // RINOK(_extractCallback->GetStream(_ref2Offset + index, &realOutStream, askMode));
        java.io.OutputStream [] realOutStream2 = new java.io.OutputStream[1]; // TBD
        int ret = _extractCallback.GetStream(_ref2Offset + index, realOutStream2, askMode);
        if (ret != HRESULT.S_OK) return ret;
         
        java.io.OutputStream realOutStream = realOutStream2[0];
        
        _outStreamWithHashSpec.SetStream(realOutStream);
        _outStreamWithHashSpec.Init();
        if (askMode == IInArchive.NExtract_NAskMode_kExtract &&
                (realOutStream == null)) {
            FileItem fileInfo = _archiveDatabase.Files.get(index);
            if (!fileInfo.IsAnti && !fileInfo.IsDirectory)
                askMode = IInArchive.NExtract_NAskMode_kSkip;
        }
        return _extractCallback.PrepareOperation(askMode);
    }
    
    int WriteEmptyFiles() throws java.io.IOException {
        for(;_currentIndex < _extractStatuses.size(); _currentIndex++) {
            int index = _startIndex + _currentIndex;
            FileItem fileInfo = _archiveDatabase.Files.get(index);
            if (!fileInfo.IsAnti && !fileInfo.IsDirectory && fileInfo.UnPackSize != 0)
                return HRESULT.S_OK;
            int res = OpenFile();
            if (res != HRESULT.S_OK) return res;
            
            res = _extractCallback.SetOperationResult(
                    IInArchive.NExtract_NOperationResult_kOK);
            if (res != HRESULT.S_OK) return res;
            _outStreamWithHashSpec.ReleaseStream();
        }
        return HRESULT.S_OK;
    }
    
    public void write(int b) throws java.io.IOException {
        throw new java.io.IOException("FolderOutStream - write() not implemented");
    }
    
    public void write(byte [] data,int off,  int size) throws java.io.IOException //  UInt32 *processedSize
    {
        int realProcessedSize = 0;
        while(_currentIndex < _extractStatuses.size()) {
            if (_fileIsOpen) {
                int index = _startIndex + _currentIndex;
                FileItem fileInfo = _archiveDatabase.Files.get(index);
                long fileSize = fileInfo.UnPackSize;
                
                long numBytesToWrite2 = (int)(fileSize - _filePos);
                int tmp = size - realProcessedSize;
                if (tmp < numBytesToWrite2) numBytesToWrite2 = tmp;
                
                int numBytesToWrite = (int)numBytesToWrite2;
                
                int processedSizeLocal;
                // int res = _outStreamWithHash.Write((const Byte *)data + realProcessedSize,numBytesToWrite, &processedSizeLocal));
                // if (res != HRESULT.S_OK) throw new java.io.IOException("_outStreamWithHash.Write : " + res); // return res;
                processedSizeLocal = numBytesToWrite;
                _outStreamWithHash.write(data,realProcessedSize + off,numBytesToWrite);
                
                _filePos += processedSizeLocal;
                realProcessedSize += processedSizeLocal;

                if (_filePos == fileSize) {
                    boolean digestsAreEqual;
                    if (fileInfo.IsFileCRCDefined)
                        digestsAreEqual = (fileInfo.FileCRC == _outStreamWithHashSpec.GetCRC());
                    else
                        digestsAreEqual = true;

                    int res = _extractCallback.SetOperationResult(
                            digestsAreEqual ?
                                IInArchive.NExtract_NOperationResult_kOK :
                                IInArchive.NExtract_NOperationResult_kCRCError);
                    if (res != HRESULT.S_OK) throw new java.io.IOException("_extractCallback.SetOperationResult : " + res); // return res;
                    
                    _outStreamWithHashSpec.ReleaseStream();
                    _fileIsOpen = false;
                    _currentIndex++;
                }
                if (realProcessedSize == size) {
                    int res = WriteEmptyFiles();
                    if (res != HRESULT.S_OK) throw new java.io.IOException("WriteEmptyFiles : " + res); // return res;
                    return ;// return realProcessedSize;
                }
            } else {
                int res = OpenFile();
                if (res != HRESULT.S_OK) throw new java.io.IOException("OpenFile : " + res); // return res;
                _fileIsOpen = true;
                _filePos = 0;
            }
        }
        
        // return size;
    }
    
    public int FlushCorrupted(int resultEOperationResult) throws java.io.IOException {
        while(_currentIndex < _extractStatuses.size()) {
            if (_fileIsOpen) {
                int res = _extractCallback.SetOperationResult(resultEOperationResult);
                if (res != HRESULT.S_OK) return res;
                
                _outStreamWithHashSpec.ReleaseStream();
                _fileIsOpen = false;
                _currentIndex++;
            } else {
                int res = OpenFile();
                if (res != HRESULT.S_OK) return res;
                _fileIsOpen = true;
            }
        }
        return HRESULT.S_OK;
    }
    
    public int WasWritingFinished() {
        int val = _extractStatuses.size();
        if (_currentIndex == val)
            return HRESULT.S_OK;
        return HRESULT.E_FAIL;
    }
    
}
