const Web3 = require("web3");
const Web3Shim = require("truffle-interface-adapter").Web3Shim;
const TruffleError = require("truffle-error");
const expect = require("truffle-expect");
const Resolver = require("truffle-resolver");
const Artifactor = require("truffle-artifactor");
const Ganache = require("ganache-core");

const Environment = {
  // It's important config is a Config object and not a vanilla object
  detect: async function(config) {
    expect.options(config, ["networks"]);

    if (!config.resolver) {
      config.resolver = new Resolver(config);
    }

    if (!config.artifactor) {
      config.artifactor = new Artifactor(config.contracts_build_directory);
    }

    if (!config.network) {
      if (config.networks["development"]) {
        config.network = "development";
      } else {
        config.network = "ganache";
        config.networks[config.network] = {
          host: "127.0.0.1",
          port: 7545,
          network_id: 5777
        };
      }
    }

    helpers.validateNetworkConfig(config);

    const web3 = new Web3Shim({
      provider: config.provider,
      networkType: config.networks[config.network].type
    });

    try {
      await helpers.detectAndSetNetworkId(config, web3);
      await helpers.setFromOnConfig(config, web3);
    } catch (error) {
      throw new Error(error);
    }
  },

  // Ensure you call Environment.detect() first.
  fork: async function(config, callback) {
    expect.options(config, ["from", "provider", "networks", "network"]);

    var web3 = new Web3Shim({
      provider: config.provider,
      networkType: config.networks[config.network].type
    });

    try {
      var accounts = await web3.eth.getAccounts();
      var block = await web3.eth.getBlock("latest");

      var upstreamNetwork = config.network;
      var upstreamConfig = config.networks[upstreamNetwork];
      var forkedNetwork = config.network + "-fork";

      config.networks[forkedNetwork] = {
        network_id: config.network_id,
        provider: Ganache.provider({
          fork: config.provider,
          unlocked_accounts: accounts,
          gasLimit: block.gasLimit
        }),
        from: config.from,
        gas: upstreamConfig.gas,
        gasPrice: upstreamConfig.gasPrice
      };
      config.network = forkedNetwork;

      callback();
    } catch (err) {
      callback(err);
    }
  },

  develop: function(config, ganacheOptions, callback) {
    expect.options(config, ["networks"]);

    var network = config.network || "develop";
    var url = `http://${ganacheOptions.host}:${ganacheOptions.port}/`;

    config.networks[network] = {
      network_id: ganacheOptions.network_id,
      provider: function() {
        return new Web3.providers.HttpProvider(url, { keepAlive: false });
      }
    };

    config.network = network;

    Environment.detect(config)
      .then(() => {
        callback();
      })
      .catch(error => {
        callback(error);
      });
  }
};

const helpers = {
  setFromOnConfig: async (config, web3) => {
    if (config.from) return;

    const accounts = await web3.eth.getAccounts();
    config.networks[config.network].from = accounts[0];
  },

  detectAndSetNetworkId: async (config, web3) => {
    const configNetworkId = config.networks[config.network].network_id;
    const providerNetworkId = await web3.eth.net.getId();
    if (configNetworkId !== "*") {
      // Ensure the network id matches the one in the config for safety
      if (providerNetworkId.toString() !== configNetworkId.toString()) {
        const error =
          `The network id specified in the truffle config ` +
          `(${configNetworkId}) does not match the one returned by the network ` +
          `(${providerNetworkId}).  Ensure that both the network and the ` +
          `provider are properly configured.`;
        throw new Error(error);
      }
    } else {
      // We have a "*" network. Get the current network and replace it with the real one.
      // TODO: Should we replace this with the blockchain uri?
      config.networks[config.network].network_id = providerNetworkId;
    }
  },

  validateNetworkConfig: config => {
    const networkConfig = config.networks[config.network];

    if (!networkConfig) {
      throw new TruffleError(
        `Unknown network "${config.network}` +
          `". See your Truffle configuration file for available networks.`
      );
    }

    const configNetworkId = config.networks[config.network].network_id;

    if (configNetworkId == null) {
      throw new Error(
        `You must specify a network_id in your '` +
          `${config.network}' configuration in order to use this network.`
      );
    }
  }
};

module.exports = Environment;

@@ -493,507 +493,36 @@
     
-if (!config.resolver) %7B%0A      config.resolver = new Resolver(config);%0A    %7D%0A%0A    if (!config.artifactor) %7B%0A      config.artifactor = new Artifactor(config.contracts_build_directory);%0A    %7D%0A%0A    if (!config.network) %7B%0A      if (config.networks%5B%22development%22%5D) %7B%0A        config.network = %22development%22;%0A      %7D else %7B%0A        config.network = %22ganache%22;%0A        config.networks%5Bconfig.network%5D = %7B%0A          host: %22127.0.0.1%22,%0A          port: 7545,%0A          network_id: 5777%0A        %7D;%0A      %7D%0A    %7D%0A
+helpers.setUpConfig(config);
 %0A   
@@ -4202,16 +4202,552 @@
 ;%0A    %7D%0A
+  %7D,%0A%0A  setUpConfig: config =%3E %7B%0A    if (!config.resolver) %7B%0A      config.resolver = new Resolver(config);%0A    %7D%0A%0A    if (!config.artifactor) %7B%0A      config.artifactor = new Artifactor(config.contracts_build_directory);%0A    %7D%0A%0A    if (!config.network) %7B%0A      if (config.networks%5B%22development%22%5D) %7B%0A        config.network = %22development%22;%0A      %7D else %7B%0A        config.network = %22ganache%22;%0A        config.networks%5Bconfig.network%5D = %7B%0A          host: %22127.0.0.1%22,%0A          port: 7545,%0A          network_id: 5777%0A        %7D;%0A      %7D%0A    %7D%0A
   %7D%0A%7D;%0A%0A

Reorganize environment.js