Implement a method in TorrentManger to write a Data packet of a torrent to the correct location on
disk. Also create a utility method to initialize the structure for fileState, fixing a bug of
opening a non-existent file with the fs::stream::in bit.

Change-Id: Ife08c2fef93d6a6cba906c3735ffac57506b7750
diff --git a/src/torrent-manager.cpp b/src/torrent-manager.cpp
index 305fa3b..f62491f 100644
--- a/src/torrent-manager.cpp
+++ b/src/torrent-manager.cpp
@@ -238,6 +238,25 @@
   return packets;
 }
 
+static std::pair<std::shared_ptr<fs::fstream>, std::vector<bool>>
+initializeFileState(const string& dataPath,
+                    const FileManifest& manifest)
+{
+  // construct the file name
+  const auto manifestName = manifest.name();
+  auto fileName = manifestName.getSubName(1, manifestName.size() - 2).toUri();
+  auto filePath = dataPath + fileName;
+  vector<bool> fileBitMap(manifest.catalog().size());
+  auto fbits = fs::fstream::out | fs::fstream::binary;
+  // if file exists, use in O/W use concatenate mode
+  fbits |= fs::exists(filePath) ? fs::fstream::in : fs::fstream::ate;
+  auto s = std::make_shared<fs::fstream>(filePath, fbits);
+  if (!*s) {
+    BOOST_THROW_EXCEPTION(io::Error("Cannot open: " + dataPath));
+  }
+  return std::make_pair(s, fileBitMap);
+}
+
 void TorrentManager::Initialize()
 {
   // .../<torrent_name>/torrent-file/<implicit_digest>
@@ -265,29 +284,28 @@
     }
     // construct the file name
     auto fileName = m.name().getSubName(1, m.name().size() - 2).toUri();
-    auto filePath = m_dataPath + fileName;
+    fs::path filePath = m_dataPath + fileName;
     // If there are any valid packets, add corresponding state to manager
-    auto packets = intializeDataPackets(filePath, m, *currTorrentFile_it);
+    if (!fs::exists(filePath)) {
+      boost::filesystem::create_directories(filePath.parent_path());
+      continue;
+    }
+    auto packets = intializeDataPackets(filePath.string(), m, *currTorrentFile_it);
     if (!packets.empty()) {
-      // build the bit map
-      auto catalog =  m.catalog();
-      vector<bool> fileBitMap(catalog.size());
+      m_fileStates[m.getFullName()] = initializeFileState(m_dataPath, m);
+      auto& fileBitMap = m_fileStates[m.getFullName()].second;
       auto read_it = packets.begin();
       size_t i = 0;
-      for (auto name : catalog) {
+      for (auto name : m.catalog()) {
         if (name == read_it->getFullName()) {
           ++read_it;
-          fileBitMap[i]= true;
+          fileBitMap[i] = true;
         }
         ++i;
       }
       for (const auto& d : packets) {
         seed(d);
       }
-      auto s = std::make_shared<fs::fstream>(filePath, fs::fstream::binary
-                                                     | fs::fstream::in
-                                                     | fs::fstream::out);
-      m_fileStates[m.getFullName()] = std::make_pair(s, fileBitMap);
     }
   }
   for (const auto& t : m_torrentSegments) {
@@ -296,7 +314,45 @@
   for (const auto& m : m_fileManifests) {
     seed(m);
   }
+}
 
+bool TorrentManager::writeData(const Data& packet)
+{
+  // find correct manifest
+  const auto& packetName = packet.getName();
+  auto manifest_it = std::find_if(m_fileManifests.begin(), m_fileManifests.end(),
+                                 [&packetName](const FileManifest& m) {
+                                   return m.getName().isPrefixOf(packetName);
+                                 });
+  if (m_fileManifests.end() == manifest_it) {
+    return false;
+  }
+  // get file state out
+  auto& fileState = m_fileStates[manifest_it->getFullName()];
+  // if there is no open stream to the file
+  if (nullptr == fileState.first) {
+    fileState = initializeFileState(m_dataPath, *manifest_it);
+  }
+  auto packetNum = packetName.get(packet.getName().size() - 1).toSequenceNumber();
+  // if we already have the packet, do not rewrite it.
+  if (fileState.second[packetNum]) {
+    return false;
+  }
+  auto packetOffset = packetNum * manifest_it->data_packet_size();
+  // write data to disk
+  fileState.first->seekg(packetOffset);
+  try {
+    auto content = packet.getContent();
+    std::vector<char> data(content.value_begin(), content.value_end());
+    fileState.first->write(&data[0], data.size());
+  }
+  catch (io::Error &e) {
+    std::cerr << e.what() << std::endl;
+    return false;
+  }
+  // update bitmap
+  fileState.second[packetNum] = true;
+  return true;
 }
 
 void TorrentManager::seed(const Data& data) const {