var extract = require('../helpers/extract.js');
var each = require('../helpers/each.js');
var parallel = require('../helpers/parallel.js');

module.exports = function (platform) {
  var urlParse = extract(platform, "urlParse");
  
  return {
    lsRemote: function (url, callback) {
      var url = urlParse(url);
      return runMachine(url, function (write, emit) {
        return lsRemoteMachine(url, write, emit);
      }, callback);
    },
    fetch: function (url, repo, callback) {
      var url = urlParse(url);
      url.agent = extract(platform, "agent");
      return runMachine(url, function (write, emit) {
        return fetchMachine(url, repo, write, emit);
      }, callback);
    },
    push: function (url, repo, callback) {
      var url = urlParse(url);
      url.agent = extract(platform, "agent");
      return runMachine(url, function (write, emit) {
        return pushMachine(url, repo, write, emit);
      }, callback);
    }
  };

  function runMachine(url, machine, callback) {
    if (!callback) return runMachine.bind(this, url, machine);
    url.port = url.port ? parseInt(url.port, 10) : 9418;
    if (url.protocol === "git:") {
      var tcp = extract(platform, "tcp");
      return tcp.connect(url.port, url.hostname, function (err, socket) {
        if (err) return callback(err);
        require('../transports/tcp.js')(socket, machine, callback);
      });
    }
    throw new TypeError(url.protocol + " urls not supported");
  }
};

function lsRemoteMachine(opts, write, emit) {
  write("git-upload-pack " + opts.pathname + "\0host=" + opts.hostname + "\0");
  return refsMachine(write, function (refs) {
    write(null);
    emit(refs);
  });
}

function refsMachine(write, emit) {
  var refs = {};
  var caps = null;
  return onLine;

  function onLine(line) {
    if (line === null) {
      return emit(refs, caps);
    }
    line = line.trim();
    if (!caps) line = pullCaps(line);
    var index = line.indexOf(" ");
    refs[line.substr(index + 1)] = line.substr(0, index);
    return onLine;
  }

  function pullCaps(line) {
    var index = line.indexOf("\0");
    caps = {};
    line.substr(index + 1).split(" ").map(function (cap) {
      var pair = cap.split("=");
      caps[pair[0]] = pair[1] || true;
    });
    return line.substr(0, index);
  }
}

function fetchMachine(opts, repo, write, emit) {
  var refs;
  var tasks;
  write("git-upload-pack " + opts.pathname + "\0host=" + opts.hostname + "\0");
  return refsMachine(write, function (result) {
    refs = result;
    tasks = [];
    var wants = [];
    each(refs, function (name, hash) {
      if (name === "HEAD" || name.indexOf('^') > 0) return;
      tasks.push(repo.writeRaw(name, hash));
      wants.push("want " + hash);
    });
    wants[0] += " multi_ack_detailed side-band-64k thin-pack ofs-delta agent=" + opts.agent;
    wants.forEach(function (want) {
      write(want + "\n");
    });
    write(null);
    write("done\n");
    return pack;

  });

  function pack(line) {
    // console.log("line", line);
    return pack;
  }

    // parallel(tasks)(function (err) {
    //   if (err) return callback(err);
    // });
}

function pushMachine(opts, repo, write, emit) {
  throw new Error("TODO: Implement pushMachine");
}


@@ -2368,16 +2368,29 @@
  tasks;%0A
+  var state;%0A
   write(
@@ -2966,20 +2966,157 @@
 urn 
+done;%0A%0A  %7D);%0A%0A  function done(line) %7B%0A    if (line.trim() !== %22NAK%22) throw new Error(%22Expected NAK%22);%0A    state = packMachine();%0A    return 
 pack;
-%0A
 %0A  %7D
-);
 %0A%0A  
@@ -3129,28 +3129,28 @@
 on pack(
-line
+item
 ) %7B%0A    
 // conso
@@ -3145,37 +3145,296 @@
     
-// console.log(%22line%22, line);
+if (!item) %7B%0A      // throw new Error(%22TODO: Implement me%22);%0A    %7D%0A    else if (item.progress) %7B%0A      if (opts.onProgress) opts.onProgress(item.progress);%0A    %7D%0A    else if (item.error) %7B%0A      if (opts.onError) opts.onError(item.error);%0A    %7D%0A    else %7B%0A      state = state(item);%0A    %7D
 %0A   
@@ -3643,12 +3643,103 @@
 chine%22);%0A%7D%0A%0A
+function packMachine() %7B%0A  return parse;%0A  function parse(chunk) %7B%0A    return parse;%0A  %7D%0A%7D%0A

Save progress