package org.bunkr.cli.commands;

import org.bunkr.ArchiveInfoContext;
import org.bunkr.cli.passwords.PasswordProvider;
import org.bunkr.cli.CLI;
import org.bunkr.exceptions.CLIException;
import org.bunkr.inventory.FileInventoryItem;
import org.bunkr.inventory.IFFTraversalTarget;
import org.bunkr.inventory.InventoryPather;
import org.bunkr.streams.input.MultilayeredInputStream;
import net.sourceforge.argparse4j.impl.Arguments;
import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser;

import java.io.*;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.util.Arrays;

/**
 * Creator: benmeier
 * Created At: 2015-12-08
 */
public class ExportFileCommand implements ICLICommand
{
    public static final String ARG_PATH = "path";
    public static final String ARG_DESTINATION_FILE = "destination";
    public static final String ARG_IGNORE_INTEGRITY_CHECK = "ignoreintegrity";

    @Override
    public void buildParser(Subparser target)
    {
        target.help("read or export a file from the archive");
        target.addArgument("path")
                .dest(ARG_PATH)
                .type(String.class)
                .help("source path in the archive");
        target.addArgument("destination")
                .dest(ARG_DESTINATION_FILE)
                .type(Arguments.fileType().acceptSystemIn())
                .help("file to export to or - for stdout");
        target.addArgument("--ignore-integrity-error")
                .dest(ARG_IGNORE_INTEGRITY_CHECK)
                .type(Boolean.class)
                .action(Arguments.storeTrue())
                .help("Ignore integrity check error caused by data corruption");
    }

    @Override
    public void handle(Namespace args) throws Exception
    {
        PasswordProvider passProv = makePasswordProvider(args);
        ArchiveInfoContext aic = new ArchiveInfoContext(args.get(CLI.ARG_ARCHIVE_PATH), passProv);
        IFFTraversalTarget target = InventoryPather.traverse(aic.getInventory(), args.getString(ARG_PATH));
        if (!target.isAFile()) throw new CLIException("'%s' is not a file.", args.getString(ARG_PATH));

        FileInventoryItem targetFile = (FileInventoryItem) target;

        File inputFile = args.get(ARG_DESTINATION_FILE);
        boolean checkHash = (! args.getBoolean(ARG_IGNORE_INTEGRITY_CHECK));
        if (inputFile.getPath().equals("-"))
        {
            writeBlockFileToStream(aic, targetFile, System.out, checkHash);
        }
        else
        {
            if (inputFile.exists()) throw new CLIException("'%s' already exists. Will not overwrite.", inputFile.getCanonicalPath());
            FileChannel fc = new RandomAccessFile(inputFile, "rw").getChannel();
            try(OutputStream contentOutputStream = Channels.newOutputStream(fc))
            {
                writeBlockFileToStream(aic, targetFile, contentOutputStream, checkHash);
            }
        }
    }

    private void writeBlockFileToStream(ArchiveInfoContext ctxt, FileInventoryItem targetFile, OutputStream os, boolean checkHash)
            throws IOException
    {
        try (MultilayeredInputStream ms = new MultilayeredInputStream(ctxt, targetFile))
        {
            ms.setCheckHashOnFinish(checkHash);
            byte[] buffer = new byte[8196];
            int n;
            while ((n = ms.read(buffer)) != -1)
            {
                os.write(buffer, 0, n);
            }
            Arrays.fill(buffer, (byte) 0);
        }
    }
}

@@ -180,16 +180,64 @@
 eption;%0A
+import org.bunkr.exceptions.IntegrityHashError;%0A
 import o
@@ -1834,32 +1834,58 @@
 Exception%0A    %7B%0A
+        try%0A        %7B%0A    
         Password
@@ -1940,16 +1940,20 @@
         
+   
+ 
 ArchiveI
@@ -2035,16 +2035,20 @@
 sProv);%0A
+    
         
@@ -2151,24 +2151,28 @@
 ));%0A        
+   
+ 
 if (!target.
@@ -2260,24 +2260,28 @@
 );%0A%0A        
+    
 FileInventor
@@ -2332,24 +2332,28 @@
 t;%0A%0A        
+  
+  
 File inputFi
@@ -2397,16 +2397,20 @@
         
+    
 boolean 
@@ -2423,17 +2423,16 @@
 ash = (!
- 
 args.get
@@ -2469,32 +2469,36 @@
 HECK));%0A        
+    
 if (inputFile.ge
@@ -2518,34 +2518,42 @@
 s(%22-%22))%0A        
+   
+ 
 %7B%0A
+    
             writ
@@ -2612,34 +2612,42 @@
 kHash);%0A        
-%7D%0A
+    %7D%0A    
         else%0A   
@@ -2643,34 +2643,42 @@
    else%0A        
+  
-%7B%0A
+  %7B%0A    
             if (
@@ -2696,16 +2696,36 @@
 xists())
+%0A                   
  throw n
@@ -2823,24 +2823,28 @@
             
+  
+  
 FileChannel 
@@ -2912,19 +2912,24 @@
         
+    
 try
+ 
 (OutputS
@@ -2990,34 +2990,41 @@
 c))%0A            
-%7B%0A
+    %7B%0A   
                 
@@ -3015,32 +3015,33 @@
                 
+ 
 writeBlockFileTo
@@ -3101,33 +3101,147 @@
 h);%0A            
-%7D
+    %7D%0A            %7D%0A        %7D%0A        catch (IntegrityHashError e)%0A        %7B%0A            throw new CLIException(e);
 %0A        %7D%0A    %7D

handle integrity error as cliexception. This also marks the end of issue 1. Fixes #1.