package org.ethereum.manager;


import org.ethereum.config.SystemProperties;
import org.ethereum.core.*;
import org.ethereum.util.ExecutorPipeline;
import org.ethereum.util.Functional;
import org.ethereum.validator.BlockHeaderValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.FileInputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;

@Component
public class BlockLoader {
    private static final Logger logger = LoggerFactory.getLogger("blockqueue");

    @Autowired
    private BlockHeaderValidator headerValidator;

    @Autowired
    SystemProperties config;

    @Autowired
    private BlockchainImpl blockchain;

    Scanner scanner = null;

    DateFormat df = new SimpleDateFormat("HH:mm:ss.SSSS");

    private void blockWork(Block block) {
        if (block.getNumber() >= blockchain.getBestBlock().getNumber() || blockchain.getBlockByHash(block.getHash()) == null) {

            if (block.getNumber() > 0 && !isValid(block.getHeader())) {
                throw new RuntimeException();
            }

            ImportResult result = blockchain.tryToConnect(block);
            System.out.println(df.format(new Date()) + " Imported block " + block.getShortDescr() + ": " + result + " (prework: "
                    + exec1.getQueue().size() + ", work: " + exec2.getQueue().size() + ", blocks: " + exec1.getOrderMap().size() + ")");

        } else {

            if (block.getNumber() % 10000 == 0)
                System.out.println("Skipping block #" + block.getNumber());
        }
    }

    ExecutorPipeline<Block, Block> exec1;
    ExecutorPipeline<Block, ?> exec2;

    public void loadBlocks() {
        exec1 = new ExecutorPipeline(8, 1000, true, new Functional.Function<Block, Block>() {
            @Override
            public Block apply(Block b) {
                for (Transaction tx : b.getTransactionsList()) {
                    tx.getSender();
                }
                return b;
            }
        }, new Functional.Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) {
                logger.error("Unhandled exception: ", throwable);
            }
        });

        exec2 = exec1.add(1, 1000, new Functional.Consumer<Block>() {
            @Override
            public void accept(Block block) {
                try {
                    blockWork(block);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        String fileSrc = config.blocksLoader();
        try {
            FileInputStream inputStream = null;
            inputStream = new FileInputStream(fileSrc);
            scanner = new Scanner(inputStream, "UTF-8");

            System.out.println("Loading blocks: " + fileSrc);

            while (scanner.hasNextLine()) {

                byte[] blockRLPBytes = Hex.decode(scanner.nextLine());
                Block block = new Block(blockRLPBytes);

                exec1.push(block);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }


        try {
            exec1.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        blockchain.flush();

        System.out.println(" * Done * ");
    }

    private boolean isValid(BlockHeader header) {

        if (!headerValidator.validate(header)) {

            if (logger.isErrorEnabled())
                headerValidator.logErrors(logger);

            return false;
        }

        return true;
    }
}

@@ -127,61 +127,9 @@
 til.
-ExecutorPipeline;%0Aimport org.ethereum.util.Functional
+*
 ;%0Aim
@@ -451,16 +451,99 @@
 eption;%0A
+import java.nio.file.Files;%0Aimport java.nio.file.Path;%0Aimport java.nio.file.Paths;%0A
 import j
@@ -2871,55 +2871,677 @@
     
-FileInputStream inputStream = null;%0A           
+System.out.println(%22Loading blocks: %22 + fileSrc);%0A%0A            String blocksFormat = config.getConfig().hasPath(%22blocks.format%22) ? config.getConfig().getString(%22blocks.format%22) : null;%0A            if (%22binary%22.equalsIgnoreCase(blocksFormat)) %7B%0A                Path path = Paths.get(fileSrc);%0A                // NOT OPTIMAL, but fine for tests%0A                byte%5B%5D data = Files.readAllBytes(path);%0A                RLPList list = RLP.decode2(data);%0A                for (RLPElement item : list) %7B%0A                    Block block = new Block(item.getRLPData());%0A                    exec1.push(block);%0A                %7D%0A            %7D else %7B%0A                FileInputStream
  inp
@@ -3593,16 +3593,20 @@
         
+  
+  
 scanner 
@@ -3651,67 +3651,8 @@
     
-        System.out.println(%22Loading blocks: %22 + fileSrc);%0A%0A
     
@@ -3688,24 +3688,28 @@
 tLine()) %7B%0A%0A
+    
             
@@ -3763,24 +3763,28 @@
 extLine());%0A
+    
             
@@ -3836,32 +3836,36 @@
                 
+    
 exec1.push(block
@@ -3859,32 +3859,50 @@
 c1.push(block);%0A
+                %7D%0A
             %7D%0A  
@@ -3958,32 +3958,60 @@
 ntStackTrace();%0A
+            System.exit(1);%0A
         %7D%0A%0A%0A    
@@ -4214,16 +4214,40 @@
 e * %22);%0A
+        System.exit(0);%0A
     %7D%0A%0A 

Allow loading of rlp encoded blocks