public interface Manager {

    String sendTransaction(String to, String from, long amount);

    String sendMessage(byte[] body);
    String sendMessage(String from, String to, String message);

    List<Message> getNewMessages();

    BlockchainBlock getBlock(long id) throws IOException;

    BlockchainPeer[] getPeers() throws IOException;

    BlockchainChainInfo getChain() throws IOException;

    void authorize(String user, String password);
} @Value
public class Message {

    String id;

    String body;

    boolean isCommitted;
} @Getter
public class FabricBlock implements BlockchainBlock {
    List<FabricTransaction> transactions;
    String stateHash;
    String previousBlockHash;
    String consensusMetadata;
    FabricNonHashData nonHashData;

    @Override
    public String getHash() {
        return stateHash;
    }

    @Override
    public String getPreviousBlockHash() {
        return previousBlockHash;
    }

    @Override
    public FabricTransaction[] getTransactions() {
        return transactions == null ? new FabricTransaction[0] : transactions.toArray(new FabricTransaction[transactions.size()]);
    }

    @Override
    public long getTime() {
        return nonHashData.getLocalLedgerCommitTimestamp().getSeconds();
    }
} @Getter
public class FabricChain implements BlockchainChainInfo {
    long height;
    String currentBlockHash;
    String previousBlockHash;

    @Override
    public long getLastBlockNumber() {
        return height - 1;
    }
} @Getter
public class FabricPeer implements BlockchainPeer {
    FabricPeerID ID;
    String address;
    String type;

    @Override
    public String getId() {
        return ID.getName();
    }

    @Override
    public String getAddress() {
        return address;
    }
} @Log4j2
public class PrintOutputToConsole extends Thread {
    private InputStream is = null;

    private PrintOutputToConsole(InputStream is, String type) {
        this.is = is;
    }

    public void run() {
        String s;
        try {
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(is));
            while ((s = br.readLine()) != null) {
                System.out.println(s);
            }
        }
        catch (IOException ioe) {
            log.error("Cannot start PrintOutput class", ioe);
        }
    }


    public static PrintOutputToConsole getStreamWrapper(InputStream is, String type) {
        return new PrintOutputToConsole(is, type);
    }
} @Log4j2
public class PropertiesHelper {

    private static ObjectMapper mapper = new ObjectMapper();

    public static Properties loadPropertiesFromPath(String nxtPropertiesPath) {
        try {
            return loadPropertiesFromPath(nxtPropertiesPath, new File(nxtPropertiesPath).toURI().toURL());
        } catch (Exception e) {
            return new Properties();
        }
    }

    public static Properties loadProperties(String moduleName) {
        URL propertiesURL = getConfOrResourceFile(String.format("%s.properties", moduleName));
        return loadPropertiesFromPath(moduleName, propertiesURL);
    }

    private static Properties loadPropertiesFromPath(String moduleName, URL propertiesURL) {
        Properties properties = new Properties();
        if (propertiesURL != null) {
            try (InputStream resourceStream = propertiesURL.openStream()) {
                properties.load(resourceStream);
                log.info("Loading {} properties from file: {}", moduleName.toUpperCase(), propertiesURL);
            } catch (Exception e) {
                log.error(String.format("Couldn't load %s properties from file: %s", moduleName.toUpperCase(), propertiesURL), e);
            }
        } else {
            log.info("Couldn't find {} properties file", moduleName.toUpperCase());
        }
        return properties;
    }

    private static URL getResource(String fileName) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        return loader.getResource(fileName);
    }

    public static String getResourceString(String name) {
        return getResourceString(name, "utf-8");
    }

    public static String getResourceString(String name, String encoding) {
        if (name == null || name.isEmpty())
            return "";
        try {
            final File resourceFile = getConfFile(name);
            if (resourceFile.exists()) {
                try {
                    byte[] encoded = Files.readAllBytes(Paths.get(resourceFile.getPath()));
                    log.debug("getResourceString. Resource ({}) found: {}", name, resourceFile.getAbsolutePath());
                    return new String(encoded, Charset.forName(encoding));
                } catch (IOException e) {
                    log.warn("getResourceString. Couldn't read resource from file: {}. error={}", resourceFile.getAbsolutePath(), e.getMessage());
                }
            }

            log.info("getResourceString. Loading resource from jar: {}", name);
            final URL resource = getResource(name);
            if (resource == null) {
                log.warn("getResourceString. Couldn't load resource from jar file: {}.", name);
                return "";
            }
            return IOUtils.toString(resource.openStream(), Charset.forName(encoding));
        } catch (Exception e) {
            log.error("getResourceString. Couldn't read a resource file.", e);
        }
        return "";
    }

    private static File getConfFile(String fileName) {
        String parentPath = getParentFile(PropertiesHelper.class).getAbsolutePath();
        return new File(new File(parentPath), fileName);
    }

    public static File getParentFile(Class cls) {
        return new File(cls.getProtectionDomain().getCodeSource().getLocation().getPath()).getParentFile();
    }

    public static URL getConfOrResourceFile(String fileName) {
        File confFile = getConfFile(fileName);
        log.debug("Configuration path: {}", confFile.getAbsolutePath());
        if (confFile.exists()) {
            try {
                return confFile.toURI().toURL();
            } catch (MalformedURLException e) {
                log.error("Couldn't get URL from file: {}. error={}", confFile, e.getMessage());
            }
        }
        return getResource(fileName);
    }

    public static <T> T loadResource(Properties properties, String subdirectory, String propertyName, Class<T> clazz) throws InternalLogicException {
        String path = properties.getProperty(propertyName);
        if (subdirectory != null)
            path = String.format(path, subdirectory);
        String resourceJson = PropertiesHelper.getResourceString(path);
        if (resourceJson.isEmpty())
            throw new InternalLogicException(String.format("Couldn't find file for %s property with value '%s'.", propertyName, path));
        try {
            return mapper.readValue(resourceJson, clazz);
        } catch (Exception ex) {
            throw new InternalLogicException(String.format("Couldn't parse '%s' file for type %s due to %s", path, clazz, ex.getMessage()));
        }
    }

    public static <T> T loadResource(String path, Class<T> clazz) throws InternalLogicException {
        String resourceJson = PropertiesHelper.getResourceString(path);
        if (resourceJson.isEmpty())
            throw new InternalLogicException(String.format("Couldn't find file in the path '%s'.", path));
        try {
            return mapper.readValue(resourceJson, clazz);
        } catch (Exception ex) {
            throw new InternalLogicException(String.format("Couldn't parse '%s' file for type %s due to %s", path, clazz, ex.getMessage()));
        }
    }
} public enum RequestType {
    POST,
    GET
} public static HttpHelper httpHelper = new HttpHelper(120000, 120000); public static AtomicInteger id = new AtomicInteger();
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import org.apache.http.client.fluent.Request;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hyperledger.fabric.sdk.*;
import org.hyperledger.fabric.sdk.exception.ChainCodeException;
import org.hyperledger.fabric.sdk.exception.EnrollmentException;
import org.hyperledger.fabric.sdk.exception.RegistrationException;
import org.json.JSONObject;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import uk.dsxt.bb.blockchain.Manager;
import uk.dsxt.bb.blockchain.Message;
import uk.dsxt.bb.datamodel.fabric.FabricBlock;
import uk.dsxt.bb.datamodel.fabric.FabricChain;
import uk.dsxt.bb.datamodel.fabric.FabricPeer;
import uk.dsxt.bb.utils.PrintOutputToConsole;
import uk.dsxt.bb.utils.PropertiesHelper;
import uk.dsxt.bb.utils.RequestType;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.security.cert.CertificateException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static uk.dsxt.bb.utils.JSONRPCHelper.httpHelper;
import static uk.dsxt.bb.utils.JSONRPCHelper.id;
//        this.memberServiceUrl = properties.getProperty("memberServiceUrl");
//        initChain(chainName, memberServiceUrl, keyValueStore, peer, admin, passphrase);
//    }
    public FabricManager(String peer) {
        this.peer = peer;
    }

    @SuppressWarnings("unchecked")
    private static void setEnv(String key, String value) {
        try {
            Map<String, String> env = System.getenv();
            Class<?> cl = env.getClass();
            Field field = cl.getDeclaredField("m");
            field.setAccessible(true);
            Map<String, String> writableEnv = (Map<String, String>) field.get(env);
            writableEnv.put(key, value);
        } catch (Exception e) {
            throw new IllegalStateException("Failed to set environment variable", e);
        }
    }

    private static void sleep(long seconds) {
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            log.error("Exception in sleep method", e);
        }
    }

    private void initChain(String chainName, String memberServiceUrl, String keyValueStore, String peer, String admin,
                           String passphrase) {
        try {
            chain = new Chain(chainName);

            chain.setMemberServicesUrl(memberServiceUrl, null);

            chain.setKeyValStore(new FileKeyValStore(keyValueStore));
            chain.addPeer(peer, null);

            Member registrar = chain.getMember(admin);

            if (!registrar.isEnrolled()) {
                registrar = chain.enroll(admin, passphrase);
            }

            chain.setRegistrar(registrar);

            deployResponse = initChaincode();
        } catch (CertificateException | EnrollmentException e) {
            log.error("Failed to init FabricManager instance", e);
        }
    }

    private void start() {
        try {
            Runtime rt = Runtime.getRuntime();
            if (!isInit) {
                memberService = rt.exec(FabricConstants.DOCKER_START_MEMBERSERVICE);
                fabricProcess = rt.exec(FabricConstants.START_FIRST_PEER);
            } else {
                int peerID = validatingPeerID + 3;
                TimeUnit.SECONDS.sleep(validatingPeerID);
                String stPeer = FabricConstants.START_PEER.replaceAll("CORE_PEER_ADDRESS=172.17.0.3:7051",
                        String.format("CORE_PEER_ADDRESS=172.17.0.%d:7051", peerID))
                        .replaceFirst("vp0", String.format("vp%d", validatingPeerID));
                String startAnotherPeer = String.join(" ", stPeer,
                        FabricConstants.DOCKER_PEER_DISCOVERY_ROOTNODE.concat(peerToConnect), FabricConstants.DOCKER_PEER_NODE_START);
                fabricProcess = rt.exec(startAnotherPeer);
            }

        } catch (Exception e) {
            log.error("Failed to start FabricManager instance", e);
        }

        PrintOutputToConsole errorReported = PrintOutputToConsole.getStreamWrapper(fabricProcess.getErrorStream(),
                "ERROR");
        PrintOutputToConsole outputMessage = PrintOutputToConsole.getStreamWrapper(fabricProcess.getInputStream(),
                "OUTPUT");

        errorReported.start();
        outputMessage.start();
    }

    private void stop() {
        try {
            if (fabricProcess.isAlive())
                fabricProcess.destroyForcibly();
            if (memberService.isAlive())
                memberService.destroyForcibly();
        } catch (Exception e) {
            log.error("stop method failed", e);
        }
    }

    private ChainCodeResponse initChaincode() {
        DeployRequest request = new DeployRequest();

        request.setChaincodePath(chainCodePath);
        request.setArgs(new ArrayList<>(Collections.singletonList(ChaincodeFunction.INIT.name().toLowerCase())));

        Member member = getMember(admin, affiliation);
        request.setChaincodeName(chainCodeName);

        return member.deploy(request);
    }

    private String initChaincodeWithHttp(String url, String path) {
        try {
            String params = String.format("{\"jsonrpc\":\"2.0\", \n" +
                    "\"method\":\"deploy\", \n" +
                    "\"params\": {\n" +
                        "\"type\":1, \n" +
                        "\"chaincodeID\":{" +
                        "\"path\":\"%s\",\n" +
                        "\"name\":\"mycc\"" +
                    "\n }," +
                    "\"CtorMsg\": {\n" +
                        "\"args\":[\"init\"]\n" +
                        " }\n" +
                        " },\n" +
                    "\"id\":1\n" +