while (true) {
      Expected<Record> exptRcd1 = cursor1->next();
      if (exptRcd1.status().code() == ErrorCodes::ERR_EXHAUST) {
        break;
      }
      INVARIANT(exptRcd1.ok());
      count1++;

      auto exptRcdv2 =
        kvstore2->getKV(exptRcd1.value().getRecordKey(), txn2.get());
      if (exptRcdv2.ok()) {
        EXPECT_EQ(exptRcd1.value().getRecordValue(), exptRcdv2.value());
      } else {
        notFoundNum++;
      }
    }

// Please refer to the license text that comes with this tendis open source
// project for additional information.

#include <stdlib.h>

#include <memory>
#include <utility>
#include <thread>  // NOLINT
#include <string>
#include <vector>
#include <algorithm>

#include "gtest/gtest.h"
#include "glog/logging.h"

#include "tendisplus/server/server_entry.h"
#include "tendisplus/server/index_manager.h"
#include "tendisplus/server/segment_manager.h"
#include "tendisplus/commands/command.h"
#include "tendisplus/network/network.h"
#include "tendisplus/utils/test_util.h"
#include "tendisplus/utils/scopeguard.h"
#include "tendisplus/utils/sync_point.h"
#include "tendisplus/utils/invariant.h"

namespace tendisplus {

const char* master1_dir = "restoretest_master1";
const char* master2_dir = "restoretest_master2";
const char* slave1_dir = "restoretest_slave1";
uint32_t master1_port = 12001;
uint32_t master2_port = 12002;
uint32_t slave1_port = 12003;

AllKeys initData(std::shared_ptr<ServerEntry> server,
                 uint32_t count,
                 const char* key_suffix) {
  auto ctx1 = std::make_shared<asio::io_context>();
  auto sess1 = makeSession(server, ctx1);
  WorkLoad work(server, sess1);
  work.init();

  AllKeys all_keys;

  auto kv_keys = work.writeWork(RecordType::RT_KV, count, 0, true, key_suffix);
  all_keys.emplace_back(kv_keys);

  auto list_keys =
    work.writeWork(RecordType::RT_LIST_META, count, 2, true, key_suffix);
  all_keys.emplace_back(list_keys);

  auto hash_keys =
    work.writeWork(RecordType::RT_HASH_META, count, 2, true, key_suffix);
  all_keys.emplace_back(hash_keys);

  auto set_keys =
    work.writeWork(RecordType::RT_SET_META, count, 2, true, key_suffix);
  all_keys.emplace_back(set_keys);

  auto zset_keys =
    work.writeWork(RecordType::RT_ZSET_META, count, 2, true, key_suffix);
  all_keys.emplace_back(zset_keys);

  return all_keys;
}

AllKeys initKvData(const std::shared_ptr<ServerEntry>& server,
                   uint32_t count,
                   const char* key_suffix) {
  auto ctx1 = std::make_shared<asio::io_context>();
  auto sess1 = makeSession(server, ctx1);
  WorkLoad work(server, sess1);
  work.init();

  AllKeys all_keys;

  auto kv_keys = work.writeWork(RecordType::RT_KV, count, 0, true, key_suffix);
  all_keys.emplace_back(kv_keys);

  return std::move(all_keys);
}

void addOneKeyEveryKvstore(const std::shared_ptr<ServerEntry>& server,
                           const char* key) {
  for (size_t i = 0; i < server->getKVStoreCount(); i++) {
    auto kvstore = server->getStores()[i];
    auto eTxn1 = kvstore->createTransaction(nullptr);
    EXPECT_EQ(eTxn1.ok(), true);
    std::unique_ptr<Transaction> txn1 = std::move(eTxn1.value());

    Status s =
      kvstore->setKV(Record(RecordKey(0, 0, RecordType::RT_KV, key, ""),
                            RecordValue("txn1", RecordType::RT_KV, -1)),
                     txn1.get());
    EXPECT_EQ(s.ok(), true);

    Expected<uint64_t> exptCommitId = txn1->commit();
    EXPECT_TRUE(exptCommitId.ok());
  }
}

void backup(const std::shared_ptr<ServerEntry>& server, const string& mode) {
  auto ctx = std::make_shared<asio::io_context>();
  auto sess = makeSession(server, ctx);

  // clear data
  const char* dir = "./back_test";
  if (filesystem::exists(dir)) {
    filesystem::remove_all(dir);
  }
  filesystem::create_directory(dir);

  std::vector<std::string> args;
  args.push_back("backup");
  args.push_back(dir);
  args.push_back(mode);
  sess->setArgs(args);
  auto expect = Command::runSessionCmd(sess.get());
  EXPECT_TRUE(expect.ok());
}

void restoreBackup(const std::shared_ptr<ServerEntry>& server) {
  auto ctx = std::make_shared<asio::io_context>();
  auto sess = makeSession(server, ctx);

  std::vector<std::string> args;
  args.push_back("restorebackup");
  args.push_back("all");
  args.push_back("./back_test");
  args.push_back("force");
  sess->setArgs(args);
  auto expect = Command::runSessionCmd(sess.get());
  EXPECT_TRUE(expect.ok());
}

void flushBinlog(const std::shared_ptr<ServerEntry>& server) {
  auto ctx = std::make_shared<asio::io_context>();
  auto sess = makeSession(server, ctx);

  for (size_t i = 0; i < server->getKVStoreCount(); i++) {
    std::vector<std::string> args;
    args.push_back("binlogflush");
    args.push_back(std::to_string(i));
    sess->setArgs(args);
    auto expect = Command::runSessionCmd(sess.get());
    EXPECT_TRUE(expect.ok());
  }
}

#ifdef _WIN32
#define popen _popen
#define pclose _pclose
#endif

bool runShell(const std::string& cmd) {
  FILE* fstream = NULL;
  char buff[1024];
  bool success = true;
  memset(buff, 0, sizeof(buff));

  if (NULL == (fstream = popen(cmd.c_str(), "r"))) {
    INVARIANT(0);
    return false;
  }

  memset(buff, 0x00, sizeof(buff));
  while (NULL != fgets(buff, sizeof(buff), fstream)) {
    if (buff[0] != '\0') {
      LOG(INFO) << std::string(buff);
      memset(buff, 0x00, sizeof(buff));
      success = false;
    }
  }
  pclose(fstream);
  return success;
}

void restoreBinlog(const string& src_binlog_dir,
                   const std::shared_ptr<ServerEntry>& server,
                   uint64_t end_ts = UINT64_MAX) {
  for (size_t i = 0; i < server->getKVStoreCount(); i++) {
    auto kvstore = server->getStores()[i];
    uint64_t binglogPos = kvstore->getHighestBinlogId();

    std::string subpath =
      "./" + src_binlog_dir + "/dump/" + std::to_string(i) + "/";
    std::vector<std::string> loglist;
    for (auto& p : filesystem::recursive_directory_iterator(subpath)) {
      const filesystem::path& path = p.path();
      if (!filesystem::is_regular_file(p)) {
        LOG(INFO) << "maxDumpFileSeq ignore:" << p.path();
        continue;
      }
      // assert path with dir prefix
#ifndef _WIN32
      INVARIANT(path.string().find(subpath) == 0);
#endif
      std::string relative = path.string().erase(0, subpath.size());
      if (relative.substr(0, 6) != "binlog") {
        LOG(INFO) << "maxDumpFileSeq ignore:" << relative;
        continue;
      }
      LOG(INFO) << "binlog file:" << p.path();
      loglist.push_back(p.path().u8string());
    }
    if (loglist.size() <= 0) {
      continue;
    }
    std::sort(loglist.begin(), loglist.end());
    for (size_t j = 0; j < loglist.size(); ++j) {
      std::string cmd = "./build/bin/binlog_tool";
      cmd += " --logfile=" + loglist[j];
      cmd += " --mode=base64";
      cmd += " --start-position=" + std::to_string(binglogPos + 1);
      cmd += " --end-datetime=" + std::to_string(end_ts);
      cmd += "| ./bin/redis-cli --csv -p " + std::to_string(master2_port);
      cmd += "| grep -v OK";
      LOG(INFO) << cmd;

      EXPECT_TRUE(runShell(cmd));
    }
  }
}

void waitBinlogDump(const std::shared_ptr<ServerEntry>& server) {
  INVARIANT(server->getParams()->maxBinlogKeepNum == 1);
  for (size_t i = 0; i < server->getKVStoreCount(); i++) {
    auto kvstore = server->getStores()[i];

    while (true) {
      auto ptxn = kvstore->createTransaction(nullptr);
      EXPECT_TRUE(ptxn.ok());
      std::unique_ptr<Transaction> txn = std::move(ptxn.value());
      uint64_t minBinlogId = 0;
      auto expBinlogidMin = RepllogCursorV2::getMinBinlogId(txn.get());
      if (expBinlogidMin.status().code() == ErrorCodes::ERR_EXHAUST) {
        minBinlogId = 0;
      } else {
        EXPECT_TRUE(expBinlogidMin.ok());
        minBinlogId = expBinlogidMin.value();
      }

      uint64_t maxBinlogId = 0;
      auto expBinlogidMax = RepllogCursorV2::getMaxBinlogId(txn.get());
      if (expBinlogidMax.status().code() == ErrorCodes::ERR_EXHAUST) {
        maxBinlogId = 0;
      } else {
        EXPECT_TRUE(expBinlogidMin.ok());
        maxBinlogId = expBinlogidMax.value();
      }

      // wait only one binlog left in rocksdb
      if (minBinlogId == maxBinlogId) {
        break;
      } else {
        std::this_thread::sleep_for(10ms);
      }
    }
  }
}

void compareAllowNotFound(const std::shared_ptr<ServerEntry>& master,
                          const std::shared_ptr<ServerEntry>& slave) {
  INVARIANT(master->getKVStoreCount() == slave->getKVStoreCount());

  for (size_t i = 0; i < master->getKVStoreCount(); i++) {
    uint64_t count1 = 0;
    uint64_t count2 = 0;
    uint64_t notFoundNum = 0;
    auto kvstore1 = master->getStores()[i];
    auto kvstore2 = slave->getStores()[i];

    auto ptxn2 = kvstore2->createTransaction(nullptr);
    EXPECT_TRUE(ptxn2.ok());
    std::unique_ptr<Transaction> txn2 = std::move(ptxn2.value());

    auto ptxn1 = kvstore1->createTransaction(nullptr);
    EXPECT_TRUE(ptxn1.ok());
    std::unique_ptr<Transaction> txn1 = std::move(ptxn1.value());
    auto cursor1 = txn1->createAllDataCursor();
    auto cursor1_binlog = txn1->createBinlogCursor();

    // check the binlog
    while (true) {
      Expected<Record> exptRcd1 = cursor1_binlog->next();
      if (exptRcd1.status().code() == ErrorCodes::ERR_EXHAUST) {
        break;
      }
      INVARIANT(exptRcd1.ok());
      count1++;

      auto exptRcdv2 =
        kvstore2->getKV(exptRcd1.value().getRecordKey(), txn2.get());
      if (exptRcdv2.ok()) {
        EXPECT_EQ(exptRcd1.value().getRecordValue(), exptRcdv2.value());
      } else {
        notFoundNum++;
      }
    }

    auto cursor2 = txn2->createAllDataCursor();
    auto cursor2_binlog = txn2->createBinlogCursor();
    while (true) {
      Expected<Record> exptRcd2 = cursor2->next();
      if (exptRcd2.status().code() == ErrorCodes::ERR_EXHAUST) {
        break;
      }
      INVARIANT(exptRcd2.ok());
      count2++;
    }
    while (true) {
      Expected<Record> exptRcd2 = cursor2_binlog->next();
      if (exptRcd2.status().code() == ErrorCodes::ERR_EXHAUST) {
        break;
      }
      INVARIANT(exptRcd2.ok());
      count2++;
    }

    if (count1 == 0) {
      EXPECT_EQ(count1, count2);
    } else {
      if (count1 != count2) {
        if (count1 == 2) {
          // master2 will dont has datakey and binlogkey.
          EXPECT_EQ(count2, 0);
        } else {
          // master2 datakey num will be one less, binlogkey num be
          // the same.
          EXPECT_EQ(count1, count2 + 1);
        }
        // the last datakey and the binlogkey will cant found.
        EXPECT_EQ(notFoundNum, 2);
      }
    }
    LOG(INFO) << "compare data: store " << i << " count1:" << count1
              << " count2:" << count2 << " notFoundNum:" << notFoundNum;
  }
}

std::vector<uint32_t> getKeyNum(const std::shared_ptr<ServerEntry>& master) {
  std::vector<uint32_t> keyNum;
  for (size_t i = 0; i < master->getKVStoreCount(); i++) {
    uint64_t count = 0;
    auto kvstore = master->getStores()[i];

    auto ptxn = kvstore->createTransaction(nullptr);
    EXPECT_TRUE(ptxn.ok());
    std::unique_ptr<Transaction> txn = std::move(ptxn.value());
    auto cursor = txn->createAllDataCursor();
    auto cursor_binlog = txn->createBinlogCursor();
    while (true) {
      Expected<Record> exptRcd = cursor->next();
      if (exptRcd.status().code() == ErrorCodes::ERR_EXHAUST) {
        break;
      }
      INVARIANT(exptRcd.ok());
      count++;
    }
    while (true) {
      Expected<Record> exptRcd = cursor_binlog->next();
      if (exptRcd.status().code() == ErrorCodes::ERR_EXHAUST) {
        break;
      }
      INVARIANT(exptRcd.ok());
      count++;
    }
    keyNum.push_back(count);
  }
  return keyNum;
}

void checkNumAllowDiff(std::vector<uint32_t> nums1,
                       std::vector<uint32_t> nums2) {
  EXPECT_EQ(nums1.size(), nums2.size());
  for (size_t i = 0; i < nums1.size(); ++i) {
    // if 2nd time not add, num will be equal
    if (nums1[i] != nums2[i]) {
      // master has one datakey less.
      // if only store one key, master2 has no datakey and binlogkey, so
      // be 2 num less.
      LOG(INFO) << "checkNumAllowDiff, i:" << i
        << "nums1:" << nums1[i] << " nums2:" << nums2[i];
      EXPECT_TRUE(nums1[i] == nums2[i] + 1 || nums1[i] == nums2[i] + 2);
    }
  }
}

std::pair<std::shared_ptr<ServerEntry>, std::shared_ptr<ServerEntry>>
makeRestoreEnv(uint32_t storeCnt) {
  EXPECT_TRUE(setupEnv(master1_dir));
  EXPECT_TRUE(setupEnv(master2_dir));

  auto cfg1 = makeServerParam(master1_port, storeCnt, master1_dir, false);
  auto cfg2 = makeServerParam(master2_port, storeCnt, master2_dir, false);
  cfg1->maxBinlogKeepNum = 1;
  cfg2->maxBinlogKeepNum = 1;
  cfg1->minBinlogKeepSec = 0;
  cfg2->minBinlogKeepSec = 0;
  cfg1->binlogDelRange = 1;
  cfg2->binlogDelRange = 1;

  auto master1 = std::make_shared<ServerEntry>(cfg1);
  auto s = master1->startup(cfg1);
  INVARIANT(s.ok());

  auto master2 = std::make_shared<ServerEntry>(cfg2);
  s = master2->startup(cfg2);
  return std::make_pair(master1, master2);
}

#ifdef _WIN32
size_t recordSize = 10;
#else
size_t recordSize = 100;
#endif

TEST(Restore, Common) {
#ifdef _WIN32
  size_t i = 1;
  {
#else
  for (size_t i = 0; i < 2; i++) {
#endif
    LOG(INFO) << ">>>>>> test store count:" << i;

    const auto guard = MakeGuard([] {
      destroyEnv(master1_dir);
      destroyEnv(master2_dir);
      std::this_thread::sleep_for(std::chrono::seconds(1));
    });

    auto hosts = makeRestoreEnv(i);
    auto& master1 = hosts.first;
    auto& master2 = hosts.second;

    // make sure no binlog been deleted
    runCommand(master1, {"config", "set", "maxBinlogKeepNum", "1000000000"});
    runCommand(master2, {"config", "set", "maxBinlogKeepNum", "1000000000"});

    auto allKeys1 = initData(master1, recordSize, "suffix1");
    LOG(INFO) << ">>>>>> master1 initData 1st end;";
    backup(master1, "copy");
    restoreBackup(master2);
    LOG(INFO) << ">>>>>> master2 restoreBackup end;";
    compareData(master1, master2);  // compare data + binlog
    LOG(INFO) << ">>>>>> compareData 1st end;";

    backup(master1, "ckpt");
    restoreBackup(master2);
    LOG(INFO) << ">>>>>> master2 restoreBackup end;";
    compareData(master1, master2);  // compare data + binlog
    LOG(INFO) << ">>>>>> compareData 1st end;";

    // make sure binlog should been deleted
    runCommand(master1, {"config", "set", "maxBinlogKeepNum", "1"});
    runCommand(master2, {"config", "set", "maxBinlogKeepNum", "1"});

    uint32_t part1_num = std::rand() % recordSize;
    part1_num = part1_num == 0 ? 1 : part1_num;
    uint32_t part2_num = recordSize - part1_num;
    // add kv only
    auto partKeys2 = initKvData(master1, part1_num, "suffix21");
    waitBinlogDump(master1);
    std::vector<uint32_t> m1_keynum1 = getKeyNum(master1);
    LOG(INFO) << ">>>>>> master1 initKvData 1st end;";
    uint64_t ts = msSinceEpoch();
    LOG(INFO) << "ms:" << ts;
    sleep(1);  // wait ts changed
    // add kv only
    auto partKeys3 = initKvData(master1, part2_num, "suffix22");
    waitBinlogDump(master1);
    std::vector<uint32_t> m1_keynum2 = getKeyNum(master1);
    LOG(INFO) << ">>>>>> master1 initKvData 2st end;";
    flushBinlog(master1);
    restoreBinlog(master1_dir, master2, ts);
    LOG(INFO) << ">>>>>> master2 restoreBinlog 1st end;";
    waitBinlogDump(master2);
    std::vector<uint32_t> m2_keynum1 = getKeyNum(master2);
    // if a kvstore write some key in the second time,
    // the keynum will equal, otherwise the keynum will be one less
    checkNumAllowDiff(m1_keynum1, m2_keynum1);  // check num only
    flushBinlog(master1);
    restoreBinlog(master1_dir, master2, UINT64_MAX);
    LOG(INFO) << ">>>>>> master2 restoreBinlog 2st end;";
    waitBinlogDump(master2);
    std::vector<uint32_t> m2_keynum2 = getKeyNum(master2);
    checkNumAllowDiff(m1_keynum2, m2_keynum2);  // check num only
    compareAllowNotFound(master1, master2);
    LOG(INFO) << ">>>>>> compareData 2st end;";

    testAll(master1);
    addOneKeyEveryKvstore(master1, "restore_test_key1");
    waitBinlogDump(master1);
    flushBinlog(master1);
    restoreBinlog(master1_dir, master2, UINT64_MAX);
    addOneKeyEveryKvstore(master2, "restore_test_key1");
    waitBinlogDump(master2);
    compareData(master1, master2, false);  // compare data only

    master1->stop();
    master2->stop();
    ASSERT_EQ(master1.use_count(), 1);
    ASSERT_EQ(master2.use_count(), 1);
    LOG(INFO) << ">>>>>> test store count:" << i << " end;";
  }
}

std::vector<std::shared_ptr<ServerEntry>> makeRestoreEnv2(uint32_t storeCnt) {
  EXPECT_TRUE(setupEnv(master1_dir));
  EXPECT_TRUE(setupEnv(master2_dir));
  EXPECT_TRUE(setupEnv(slave1_dir));

  auto cfg1 = makeServerParam(master1_port, storeCnt, master1_dir, false);
  auto cfg2 = makeServerParam(master2_port, storeCnt, master2_dir, false);
  auto cfg3 = makeServerParam(slave1_port, storeCnt, slave1_dir, false);
  cfg1->minBinlogKeepSec = 60;
  cfg1->binlogDelRange = 1;
  cfg2->maxBinlogKeepNum = 1;
  cfg2->minBinlogKeepSec = 0;
  cfg2->binlogDelRange = 1;
  cfg3->maxBinlogKeepNum = 1;
  cfg3->minBinlogKeepSec = 0;
  cfg3->binlogDelRange = 1;
  cfg3->slaveBinlogKeepNum = 1;

  auto master1 = std::make_shared<ServerEntry>(cfg1);
  auto s = master1->startup(cfg1);
  INVARIANT(s.ok());

  auto master2 = std::make_shared<ServerEntry>(cfg2);
  s = master2->startup(cfg2);

  auto slave1 = std::make_shared<ServerEntry>(cfg3);
  s = slave1->startup(cfg3);
  return std::vector<std::shared_ptr<ServerEntry>>({master1, master2, slave1});
}

TEST(Restore, Common2) {
#ifdef _WIN32
  size_t i = 1;
  {
#else
  for (size_t i = 0; i < 2; i++) {
#endif
    LOG(INFO) << ">>>>>> test store count:" << i;

    const auto guard = MakeGuard([] {
      destroyEnv(master1_dir);
      destroyEnv(master2_dir);
      destroyEnv(slave1_dir);
      std::this_thread::sleep_for(std::chrono::seconds(1));
    });

    auto hosts = makeRestoreEnv2(i);
    auto& master1 = hosts[0];
    auto& master2 = hosts[1];
    auto& slave1 = hosts[2];
    {
      auto ctx = std::make_shared<asio::io_context>();
      auto session = makeSession(slave1, ctx);
      WorkLoad work(slave1, session);
      work.init();
      work.slaveof("127.0.0.1", master1_port);
    }

    LOG(INFO) << ">>>>>> master1 add data begin.";
    auto thread = std::thread([this, master1]() {
      testAll(master1);  // need about 40 seconds
    });
    uint32_t sleep_time = genRand() % 20 + 10;  // 10-30 seconds
    sleep(sleep_time);
    LOG(INFO) << ">>>>>> master1 backup and master2 restoreBackup.";
    backup(slave1, "ckpt");
    restoreBackup(master2);
    thread.join();
    LOG(INFO) << ">>>>>> master1 add data end.";
    addOneKeyEveryKvstore(master1, "restore_test_key1");
    addOneKeyEveryKvstore(master1, "restore_test_key2");
    waitSlaveCatchup(master1, slave1);
    waitBinlogDump(slave1);
    flushBinlog(slave1);
    restoreBinlog(slave1_dir, master2, UINT64_MAX);
    addOneKeyEveryKvstore(master2, "restore_test_key1");
    addOneKeyEveryKvstore(master2, "restore_test_key2");
    waitBinlogDump(master2);
    compareData(master1, master2, false);  // compare data only

    master1->stop();
    master2->stop();
    slave1->stop();
    ASSERT_EQ(master1.use_count(), 1);
    ASSERT_EQ(master2.use_count(), 1);
    ASSERT_EQ(slave1.use_count(), 1);
    LOG(INFO) << ">>>>>> test store count:" << i << " end;";
  }
}
}  // namespace tendisplus


{
    delete _sinus;
    _sinus = new OscSinus(sampleRate);
}

/***************************************************************************
**                                                                        **
**  Polyphone, a soundfont editor                                         **
**                                                                        **
**                                                                        **
****************************************************************************
**           Author: Davy Triponney                                       **
**  Website/Contact: https://www.polyphone-soundfonts.com                 **
**             Date: 01.01.2013                                           **
***************************************************************************/

#include "calibrationsinus.h"
#include "qmath.h"

CalibrationSinus::CalibrationSinus() :
    _sinus(nullptr),
    _pitch(0),
    _currentPitch(-1),
    _level(0),
    _currentLevel(0),
    _buf(nullptr)
{
    initBuffer(1024);
}

CalibrationSinus::~CalibrationSinus()
{
    delete _sinus;
    delete [] _buf;
}

void CalibrationSinus::initBuffer(quint32 size)
{
    delete [] _buf;
    _bufSize = size;
    _buf = new float[_bufSize];
}

void CalibrationSinus::setSampleRate(quint32 sampleRate)

void CalibrationSinus::setPitch(int numNote)
{
    _mutex.lock();
    _pitch = numNote;
    _mutex.unlock();
}

void CalibrationSinus::on()
{
    _mutex.lock();
    _level = 0.7f;
    _mutex.unlock();
}

void CalibrationSinus::off()
{
    _mutex.lock();
    _level = 0;
    _mutex.unlock();
}

void CalibrationSinus::addData(float * dataR, float * dataL, quint32 len)
{
    if (!_sinus)
        return;

    if (len > _bufSize)
        initBuffer(len);

    if (!_mutex.tryLock(1)) // Impossible ici d'attendre
        return;
    double pitch = _pitch;
    float level = _level;
    _mutex.unlock();

    // Possibly stop here
    if (level <= 0.0004f && _currentLevel <= 0.0004f)
        return;

    // Current frequency
    if (_currentPitch < 0)
        _currentPitch = pitch;
    if (_currentPitch - pitch < -1 || _currentPitch - pitch > 1)
        _currentPitch += (pitch - _currentPitch) / 2; // fast transition
    else
        _currentPitch = pitch; // smooth transition

    // Generate data and copy
    _sinus->getSinus(_buf, len, 440.0f * static_cast<float>(qPow(2., (_currentPitch - 69.) / 12.)), 0);

    for (quint32 i = 0; i < len; i++)
    {
        // Smooth transition for the level
        if (_currentLevel > level)
            _currentLevel -= 0.0002f;
        else if (_currentLevel < level)
            _currentLevel += 0.0002f;

        dataR[i] += _currentLevel * _buf[i];
        dataL[i] += _currentLevel * _buf[i];
    }
}


/***************************************************************************
**                                                                        **
**  Polyphone, a soundfont editor                                         **
**                                                                        **
**                                                                        **
****************************************************************************
**           Author: Davy Triponney                                       **
**  Website/Contact: https://www.polyphone-soundfonts.com                 **
**             Date: 01.01.2013                                           **
***************************************************************************/

#include "calibrationsinus.h"
#include "qmath.h"

CalibrationSinus::CalibrationSinus() :
    _sinus(nullptr),
    _pitch(0),
    _currentPitch(-1),
    _level(0),
    _currentLevel(0),
    _buf(nullptr)
{
    initBuffer(1024);
}

CalibrationSinus::~CalibrationSinus()
{
    delete _sinus;
    delete [] _buf;
}

void CalibrationSinus::initBuffer(quint32 size)
{
    delete [] _buf;
    _bufSize = size;
    _buf = new float[_bufSize];
}

void CalibrationSinus::setSampleRate(quint32 sampleRate)
{
    delete _sinus;
    _sinus = new OscSinus(sampleRate);
}

void CalibrationSinus::setPitch(int numNote)
{
    _mutex.lock();
    _pitch = numNote;
    _mutex.unlock();
}

void CalibrationSinus::on()
{
    _mutex.lock();
    _level = 0.7f;
    _mutex.unlock();
}

void CalibrationSinus::off()
{
    _mutex.lock();
    _level = 0;
    _mutex.unlock();
}

void CalibrationSinus::addData(float * dataR, float * dataL, quint32 len)
{
    if (!_sinus)
        return;

    if (len > _bufSize)
        initBuffer(len);

    if (!_mutex.tryLock(1)) // Impossible ici d'attendre
        return;

    float level = _level;
    _mutex.unlock();

    // Possibly stop here
    if (level <= 0.0004f && _currentLevel <= 0.0004f)
        return;

    // Current frequency
    if (_currentPitch < 0)
        _currentPitch = pitch;
    if (_currentPitch - pitch < -1 || _currentPitch - pitch > 1)
        _currentPitch += (pitch - _currentPitch) / 2; // fast transition
    else
        _currentPitch = pitch; // smooth transition

    // Generate data and copy
    _sinus->getSinus(_buf, len, 440.0f * static_cast<float>(qPow(2., (_currentPitch - 69.) / 12.)), 0);

    for (quint32 i = 0; i < len; i++)
    {
        // Smooth transition for the level
        if (_currentLevel > level)
            _currentLevel -= 0.0002f;
        else if (_currentLevel < level)
            _currentLevel += 0.0002f;

        dataR[i] += _currentLevel * _buf[i];
        dataL[i] += _currentLevel * _buf[i];
    }
}


{
	// TODO(b/149110874) Use coshf/sinhf when we've implemented SpirV versions at the SpirV level
	return Float4(0.5f) * (emulated::Exp(x) + emulated::Exp(-x));
}

#include "EmulatedIntrinsics.hpp"

#include <algorithm>
#include <cmath>
#include <functional>
#include <mutex>
#include <utility>

namespace rr {
namespace {

template<typename T>
struct UnderlyingType
{
	using Type = typename decltype(rr::Extract(std::declval<RValue<T>>(), 0))::rvalue_underlying_type;
};

template<typename T>
using UnderlyingTypeT = typename UnderlyingType<T>::Type;

// Call single arg function on a vector type
template<typename Func, typename T>
RValue<T> call4(Func func, const RValue<T> &x)
{
	T result;
	result = Insert(result, Call(func, Extract(x, 0)), 0);
	result = Insert(result, Call(func, Extract(x, 1)), 1);
	result = Insert(result, Call(func, Extract(x, 2)), 2);
	result = Insert(result, Call(func, Extract(x, 3)), 3);
	return result;
}

// Call two arg function on a vector type
template<typename Func, typename T>
RValue<T> call4(Func func, const RValue<T> &x, const RValue<T> &y)
{
	T result;
	result = Insert(result, Call(func, Extract(x, 0), Extract(y, 0)), 0);
	result = Insert(result, Call(func, Extract(x, 1), Extract(y, 1)), 1);
	result = Insert(result, Call(func, Extract(x, 2), Extract(y, 2)), 2);
	result = Insert(result, Call(func, Extract(x, 3), Extract(y, 3)), 3);
	return result;
}

template<typename T, typename EL = UnderlyingTypeT<T>>
void gather(T &out, RValue<Pointer<EL>> base, RValue<Int4> offsets, RValue<Int4> mask, unsigned int alignment, bool zeroMaskedLanes)
{
	constexpr bool atomic = false;
	constexpr std::memory_order order = std::memory_order_relaxed;

	Pointer<Byte> baseBytePtr = base;

	out = T(0);
	for(int i = 0; i < 4; i++)
	{
		If(Extract(mask, i) != 0)
		{
			auto offset = Extract(offsets, i);
			auto el = Load(Pointer<EL>(&baseBytePtr[offset]), alignment, atomic, order);
			out = Insert(out, el, i);
		}
		Else If(zeroMaskedLanes)
		{
			out = Insert(out, EL(0), i);
		}
	}
}

template<typename T, typename EL = UnderlyingTypeT<T>>
void scatter(RValue<Pointer<EL>> base, RValue<T> val, RValue<Int4> offsets, RValue<Int4> mask, unsigned int alignment)
{
	constexpr bool atomic = false;
	constexpr std::memory_order order = std::memory_order_relaxed;

	Pointer<Byte> baseBytePtr = base;

	for(int i = 0; i < 4; i++)
	{
		If(Extract(mask, i) != 0)
		{
			auto offset = Extract(offsets, i);
			Store(Extract(val, i), Pointer<EL>(&baseBytePtr[offset]), alignment, atomic, order);
		}
	}
}

// TODO(b/148276653): Both atomicMin and atomicMax use a static (global) mutex that makes all min
// operations for a given T mutually exclusive, rather than only the ones on the value pointed to
// by ptr. Use a CAS loop, as is done for LLVMReactor's min/max atomic for Android.
// TODO(b/148207274): Or, move this down into Subzero as a CAS-based operation.
template<typename T>
static T atomicMin(T *ptr, T value)
{
	static std::mutex m;

	std::lock_guard<std::mutex> lock(m);
	T origValue = *ptr;
	*ptr = std::min(origValue, value);
	return origValue;
}
template<typename T>
static T atomicMax(T *ptr, T value)
{
	static std::mutex m;

	std::lock_guard<std::mutex> lock(m);
	T origValue = *ptr;
	*ptr = std::max(origValue, value);
	return origValue;
}

}  // anonymous namespace

namespace emulated {

RValue<Float4> Gather(RValue<Pointer<Float>> base, RValue<Int4> offsets, RValue<Int4> mask, unsigned int alignment, bool zeroMaskedLanes /* = false */)
{
	Float4 result{};
	gather(result, base, offsets, mask, alignment, zeroMaskedLanes);
	return result;
}

RValue<Int4> Gather(RValue<Pointer<Int>> base, RValue<Int4> offsets, RValue<Int4> mask, unsigned int alignment, bool zeroMaskedLanes /* = false */)
{
	Int4 result{};
	gather(result, base, offsets, mask, alignment, zeroMaskedLanes);
	return result;
}

void Scatter(RValue<Pointer<Float>> base, RValue<Float4> val, RValue<Int4> offsets, RValue<Int4> mask, unsigned int alignment)
{
	scatter(base, val, offsets, mask, alignment);
}

void Scatter(RValue<Pointer<Int>> base, RValue<Int4> val, RValue<Int4> offsets, RValue<Int4> mask, unsigned int alignment)
{
	scatter<Int4>(base, val, offsets, mask, alignment);
}

RValue<Float> Exp2(RValue<Float> x)
{
	return Call(exp2f, x);
}

RValue<Float> Log2(RValue<Float> x)
{
	return Call(log2f, x);
}

RValue<Float4> Sin(RValue<Float4> x)
{
	return call4(sinf, x);
}

RValue<Float4> Cos(RValue<Float4> x)
{
	return call4(cosf, x);
}

RValue<Float4> Tan(RValue<Float4> x)
{
	return call4(tanf, x);
}

RValue<Float4> Asin(RValue<Float4> x)
{
	return call4(asinf, x);
}

RValue<Float4> Acos(RValue<Float4> x)
{
	return call4(acosf, x);
}

RValue<Float4> Atan(RValue<Float4> x)
{
	return call4(atanf, x);
}

RValue<Float4> Sinh(RValue<Float4> x)
{
	// TODO(b/149110874) Use coshf/sinhf when we've implemented SpirV versions at the SpirV level
	return Float4(0.5f) * (emulated::Exp(x) - emulated::Exp(-x));
}

RValue<Float4> Cosh(RValue<Float4> x)

RValue<Float4> Tanh(RValue<Float4> x)
{
	return call4(tanhf, x);
}

RValue<Float4> Asinh(RValue<Float4> x)
{
	return call4(asinhf, x);
}

RValue<Float4> Acosh(RValue<Float4> x)
{
	return call4(acoshf, x);
}

RValue<Float4> Atanh(RValue<Float4> x)
{
	return call4(atanhf, x);
}

RValue<Float4> Atan2(RValue<Float4> x, RValue<Float4> y)
{
	return call4(atan2f, x, y);
}

RValue<Float4> Pow(RValue<Float4> x, RValue<Float4> y)
{
	return call4(powf, x, y);
}

RValue<Float4> Exp(RValue<Float4> x)
{
	return call4(expf, x);
}

RValue<Float4> Log(RValue<Float4> x)
{
	return call4(logf, x);
}

RValue<Float4> Exp2(RValue<Float4> x)
{
	return call4(exp2f, x);
}

RValue<Float4> Log2(RValue<Float4> x)
{
	return call4(log2f, x);
}

RValue<Int> MinAtomic(RValue<Pointer<Int>> x, RValue<Int> y, std::memory_order memoryOrder)
{
	return Call(atomicMin<int32_t>, x, y);
}

RValue<UInt> MinAtomic(RValue<Pointer<UInt>> x, RValue<UInt> y, std::memory_order memoryOrder)
{
	return Call(atomicMin<uint32_t>, x, y);
}

RValue<Int> MaxAtomic(RValue<Pointer<Int>> x, RValue<Int> y, std::memory_order memoryOrder)
{
	return Call(atomicMax<int32_t>, x, y);
}

RValue<UInt> MaxAtomic(RValue<Pointer<UInt>> x, RValue<UInt> y, std::memory_order memoryOrder)
{
	return Call(atomicMax<uint32_t>, x, y);
}

RValue<Float4> FRem(RValue<Float4> lhs, RValue<Float4> rhs)
{
	return call4(fmodf, lhs, rhs);
}

}  // namespace emulated
}  // namespace rr


result = Insert(result, Call(func, Extract(x, 3), Extract(y, 3)), 3);

#include "EmulatedIntrinsics.hpp"

#include <algorithm>
#include <cmath>
#include <functional>
#include <mutex>
#include <utility>

namespace rr {
namespace {

template<typename T>
struct UnderlyingType
{
	using Type = typename decltype(rr::Extract(std::declval<RValue<T>>(), 0))::rvalue_underlying_type;
};

template<typename T>
using UnderlyingTypeT = typename UnderlyingType<T>::Type;

// Call single arg function on a vector type
template<typename Func, typename T>
RValue<T> call4(Func func, const RValue<T> &x)
{
	T result;
	result = Insert(result, Call(func, Extract(x, 0)), 0);
	result = Insert(result, Call(func, Extract(x, 1)), 1);
	result = Insert(result, Call(func, Extract(x, 2)), 2);
	result = Insert(result, Call(func, Extract(x, 3)), 3);
	return result;
}

// Call two arg function on a vector type
template<typename Func, typename T>
RValue<T> call4(Func func, const RValue<T> &x, const RValue<T> &y)
{
	T result;
	result = Insert(result, Call(func, Extract(x, 0), Extract(y, 0)), 0);
	result = Insert(result, Call(func, Extract(x, 1), Extract(y, 1)), 1);
	result = Insert(result, Call(func, Extract(x, 2), Extract(y, 2)), 2);

	return result;
}

template<typename T, typename EL = UnderlyingTypeT<T>>
void gather(T &out, RValue<Pointer<EL>> base, RValue<Int4> offsets, RValue<Int4> mask, unsigned int alignment, bool zeroMaskedLanes)
{
	constexpr bool atomic = false;
	constexpr std::memory_order order = std::memory_order_relaxed;

	Pointer<Byte> baseBytePtr = base;

	out = T(0);
	for(int i = 0; i < 4; i++)
	{
		If(Extract(mask, i) != 0)
		{
			auto offset = Extract(offsets, i);
			auto el = Load(Pointer<EL>(&baseBytePtr[offset]), alignment, atomic, order);
			out = Insert(out, el, i);
		}
		Else If(zeroMaskedLanes)
		{
			out = Insert(out, EL(0), i);
		}
	}
}

template<typename T, typename EL = UnderlyingTypeT<T>>
void scatter(RValue<Pointer<EL>> base, RValue<T> val, RValue<Int4> offsets, RValue<Int4> mask, unsigned int alignment)
{
	constexpr bool atomic = false;
	constexpr std::memory_order order = std::memory_order_relaxed;

	Pointer<Byte> baseBytePtr = base;

	for(int i = 0; i < 4; i++)
	{
		If(Extract(mask, i) != 0)
		{
			auto offset = Extract(offsets, i);
			Store(Extract(val, i), Pointer<EL>(&baseBytePtr[offset]), alignment, atomic, order);
		}
	}
}

// TODO(b/148276653): Both atomicMin and atomicMax use a static (global) mutex that makes all min
// operations for a given T mutually exclusive, rather than only the ones on the value pointed to
// by ptr. Use a CAS loop, as is done for LLVMReactor's min/max atomic for Android.
// TODO(b/148207274): Or, move this down into Subzero as a CAS-based operation.
template<typename T>
static T atomicMin(T *ptr, T value)
{
	static std::mutex m;

	std::lock_guard<std::mutex> lock(m);
	T origValue = *ptr;
	*ptr = std::min(origValue, value);
	return origValue;
}
template<typename T>
static T atomicMax(T *ptr, T value)
{
	static std::mutex m;

	std::lock_guard<std::mutex> lock(m);
	T origValue = *ptr;
	*ptr = std::max(origValue, value);
	return origValue;
}

}  // anonymous namespace

namespace emulated {

RValue<Float4> Gather(RValue<Pointer<Float>> base, RValue<Int4> offsets, RValue<Int4> mask, unsigned int alignment, bool zeroMaskedLanes /* = false */)
{
	Float4 result{};
	gather(result, base, offsets, mask, alignment, zeroMaskedLanes);
	return result;
}

RValue<Int4> Gather(RValue<Pointer<Int>> base, RValue<Int4> offsets, RValue<Int4> mask, unsigned int alignment, bool zeroMaskedLanes /* = false */)
{
	Int4 result{};
	gather(result, base, offsets, mask, alignment, zeroMaskedLanes);
	return result;
}

void Scatter(RValue<Pointer<Float>> base, RValue<Float4> val, RValue<Int4> offsets, RValue<Int4> mask, unsigned int alignment)
{
	scatter(base, val, offsets, mask, alignment);
}

void Scatter(RValue<Pointer<Int>> base, RValue<Int4> val, RValue<Int4> offsets, RValue<Int4> mask, unsigned int alignment)
{
	scatter<Int4>(base, val, offsets, mask, alignment);
}

RValue<Float> Exp2(RValue<Float> x)
{
	return Call(exp2f, x);
}

RValue<Float> Log2(RValue<Float> x)
{
	return Call(log2f, x);
}

RValue<Float4> Sin(RValue<Float4> x)
{
	return call4(sinf, x);
}

RValue<Float4> Cos(RValue<Float4> x)
{
	return call4(cosf, x);
}

RValue<Float4> Tan(RValue<Float4> x)
{
	return call4(tanf, x);
}

RValue<Float4> Asin(RValue<Float4> x)
{
	return call4(asinf, x);
}

RValue<Float4> Acos(RValue<Float4> x)
{
	return call4(acosf, x);
}

RValue<Float4> Atan(RValue<Float4> x)
{
	return call4(atanf, x);
}

RValue<Float4> Sinh(RValue<Float4> x)
{
	// TODO(b/149110874) Use coshf/sinhf when we've implemented SpirV versions at the SpirV level
	return Float4(0.5f) * (emulated::Exp(x) - emulated::Exp(-x));
}

RValue<Float4> Cosh(RValue<Float4> x)
{
	// TODO(b/149110874) Use coshf/sinhf when we've implemented SpirV versions at the SpirV level
	return Float4(0.5f) * (emulated::Exp(x) + emulated::Exp(-x));
}

RValue<Float4> Tanh(RValue<Float4> x)
{
	return call4(tanhf, x);
}

RValue<Float4> Asinh(RValue<Float4> x)
{
	return call4(asinhf, x);
}

RValue<Float4> Acosh(RValue<Float4> x)
{
	return call4(acoshf, x);
}

RValue<Float4> Atanh(RValue<Float4> x)
{
	return call4(atanhf, x);
}

RValue<Float4> Atan2(RValue<Float4> x, RValue<Float4> y)
{
	return call4(atan2f, x, y);
}

RValue<Float4> Pow(RValue<Float4> x, RValue<Float4> y)
{
	return call4(powf, x, y);
}

RValue<Float4> Exp(RValue<Float4> x)
{
	return call4(expf, x);
}

RValue<Float4> Log(RValue<Float4> x)
{
	return call4(logf, x);
}

RValue<Float4> Exp2(RValue<Float4> x)
{
	return call4(exp2f, x);
}

RValue<Float4> Log2(RValue<Float4> x)
{
	return call4(log2f, x);
}

RValue<Int> MinAtomic(RValue<Pointer<Int>> x, RValue<Int> y, std::memory_order memoryOrder)
{
	return Call(atomicMin<int32_t>, x, y);
}

RValue<UInt> MinAtomic(RValue<Pointer<UInt>> x, RValue<UInt> y, std::memory_order memoryOrder)
{
	return Call(atomicMin<uint32_t>, x, y);
}

RValue<Int> MaxAtomic(RValue<Pointer<Int>> x, RValue<Int> y, std::memory_order memoryOrder)
{
	return Call(atomicMax<int32_t>, x, y);
}

RValue<UInt> MaxAtomic(RValue<Pointer<UInt>> x, RValue<UInt> y, std::memory_order memoryOrder)
{
	return Call(atomicMax<uint32_t>, x, y);
}

RValue<Float4> FRem(RValue<Float4> lhs, RValue<Float4> rhs)
{
	return call4(fmodf, lhs, rhs);
}

}  // namespace emulated
}  // namespace rr


{
  std::map<std::string, PackageFileLoc>::const_iterator it;
  it = index_.begin();
  for (int i = 0; (i < idx) && (it != index_.end()); ++i, ++it) ;

  if (it != index_.end()) {
    return it->first.c_str();
  }

  return "";
}

// Uses unpacker to get data from the glc file.

#include "./glc_unpacker.h"
#include <iostream>  // NOLINT(readability/streams)
#include <map>
#include <sstream>  // NOLINT(readability/streams)
#include <string>
#include <cstring>
#include <cassert>
#include "./file_package.h"
#include "./file_unpacker.h"
#include "./glc_reader.h"

const char* LAYER_INFO = "earth/layer_info.txt";
// Types for layers
const char* IMAGERY_LAYER_TYPE = "IMAGE";
const char* TERRAIN_LAYER_TYPE = "TERRAIN";
const char* VECTOR_LAYER_TYPE = "VECTOR";
const char* MAP_LAYER_TYPE = "MAP";

// GEE identifying bytes in all glxs.
const std::uint32_t GEE_ID = 0x47454547;
const std::uint64_t GEE_ID_OFFSET = 8;

/**
 * Constructor. Load the index to all of the files and
 * packages in the composite file. If this is a package file,
 * treat it as a composite with a single layer that is the
 * whole file. If files_only flag is true, only read
 * top level files (not used for serving tiles).
 */
GlcUnpacker::GlcUnpacker(const GlcReader& glc_reader,
                         bool is_composite,
                         bool files_only)
    : glc_reader_(glc_reader),
    is_composite_(is_composite),
    is_gee_(false),
    is_2d_(false),
    is_3d_(false) {
  length_ = glc_reader_.Size();

  // Check file is a GEE file first.
  std::uint32_t id;
  std::int64_t offset = length_ - GEE_ID_OFFSET;
  if (!glc_reader_.ReadData(&id, offset, sizeof(id))) {
    std::cerr << "Unable to read file id." << std::endl;
    return;
  }
  if (id != GEE_ID) {
    std::cerr << "Id does not match expected GEE id." << std::endl;
    return;
  } else {
    is_gee_ = true;
  }

  // Get offset to index.
  offset = length_ - Package::kIndexOffsetOffset;
  std::uint32_t num_files = 0;
  std::int64_t index_offset;
  if (!glc_reader_.ReadData(
      &index_offset, offset, Package::kIndexOffsetSize)) {
    std::cerr << "Unable to read file offset." << std::endl;
    return;
  }
  // If negative, use as offset from end of file.
  if (index_offset < 0) {
    reader_offset_ = length_ + index_offset;
  } else {
    reader_offset_ = index_offset;
  }

  // Read in the index header.
  if (!Read(&num_files, sizeof(num_files))) {
    std::cerr << "Unable to read number of files in file." << std::endl;
    return;
  }

  // Read in the index entries.
  std::string path;
  PackageFileLoc file_loc;
  for (std::uint32_t i = 0; i < num_files; ++i) {
    // Read in the relative path of the index entry.
    std::uint16_t path_len;
    if (!Read(&path_len, sizeof(path_len))) {
      std::cerr << "Unable to read file name length." << std::endl;
      return;
    }

    path.resize(path_len);
    if (!Read(&path[0], path_len)) {
      std::cerr << "Unable to read file name." << std::endl;
      return;
    }

    // Read in the size and offset of the index entry.
    if (!Read(&file_loc, sizeof(PackageFileLoc))) {
      std::cerr << "Unable to read file size and offset." << std::endl;
      return;
    }
    // If offsets in index are negative, add them to file size to get offset
    // relative to beginning of file.
    file_loc.NormalizeNegativeOffset(length_);
    index_[path] = file_loc;
  }

  // For a real composite, set up file_unpacker for each layer.
  if (is_composite) {
    SetUpLayerUnpackers(files_only);

  // If this is just a glb or glm, set up a single layer 0.
  } else {
    std::string suffix = glc_reader_.Suffix();
    if (suffix == "glb") {
      is_3d_ = true;
    } else if (suffix == "glm")  {
      is_2d_ = true;
    } else {
      std::cout << "Unknown suffix: " << suffix << std::endl;
      return;
    }

    if (files_only) {
      return;
    }

    unpacker_index_[0] = new FileUnpacker(glc_reader_, 0, length_);
  }

  // Read in earth/info.txt
  std::map<std::string, PackageFileLoc>::iterator it
      = index_.find("earth/info.txt");
  if (it != index_.end()) {
    file_loc = it->second;
    if (!glc_reader_.Read(&info_, file_loc.Offset(), file_loc.Size())) {
      std::cerr << "Unable to read earth/info.txt" << std::endl;
    }
  }
}

GlcUnpacker::~GlcUnpacker() {
  std::map<int, FileUnpacker*>::iterator it;
  for (it = unpacker_index_.begin(); it != unpacker_index_.end(); ++it) {
    delete it->second;
  }
}

/**
 * Helper method for the Constructor that creates a file_unpacker for each
 * layer listed in the layer_info.txt file.
 */
void GlcUnpacker::SetUpLayerUnpackers(bool files_only) {
  PackageFileLoc file_loc;
  if (!FindFile(LAYER_INFO, &file_loc)) {
    std::cerr << "Unable to load layer info." << std::endl;
    return;
  }

  std::string layer_info;
  if (!glc_reader_.Read(&layer_info, file_loc.Offset(), file_loc.Size())) {
    std::cerr << "Unable to read layer info." << std::endl;
    return;
  }

  std::stringstream stream(
      layer_info, std::stringstream::in);

  std::string layer_name;
  std::string layer_path;
  std::string layer_type;
  int layer_id;
  int parent_layer;

  // Read in layer info.
  while (true) {
    stream >> layer_name;
    stream >> layer_path;
    stream >> layer_type;
    stream >> layer_id;

    // Ensure that a full line of data was available.
    if (stream.eof()) {
      break;
    }

    stream >> parent_layer;

    // Open new unpacker for glb layer.
    if ((layer_type == IMAGERY_LAYER_TYPE) ||
        (layer_type == TERRAIN_LAYER_TYPE) ||
        (layer_type == VECTOR_LAYER_TYPE) ||
        (layer_type == MAP_LAYER_TYPE)) {
      if (layer_type == MAP_LAYER_TYPE) {
        is_2d_ = true;
      } else {
        is_3d_ = true;
      }
      if (!files_only) {
        std::map<std::string, PackageFileLoc>::iterator it =
            index_.find(layer_path);
        if (it != index_.end()) {
          unpacker_index_[layer_id] = new FileUnpacker(
              glc_reader_, it->second.Offset(), it->second.Size());
          parent_layer_[layer_id] = parent_layer;
          layer_type_[layer_id] = layer_type;
        } else {
          std::cerr << "Missing layer: " << layer_path << std::endl;
        }
      }
    } else if (layer_type == "DBROOT") {
      meta_dbroot_path_ = layer_path;
    }
  }
}

/**
 * Helper for reading in consecutive fields from the glc file.
 */
bool GlcUnpacker::Read(void* data, std::uint64_t size) {
  if (glc_reader_.ReadData(data, reader_offset_, size)) {
    reader_offset_ += size;
    return true;
  }
  return false;
}

/**
 * Find data packet and set offset and size for the packet. Data packets can
 * be imagery, terrain or vectors.
 */
bool GlcUnpacker::FindDataPacket(const char* qtpath,
                                 int packet_type,
                                 int channel,
                                 int layer,
                                 PackageFileLoc* data_loc) {
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return false;
  }

  FileUnpacker* unpacker;
  while (true) {
    unpacker = GetUnpacker(layer);
    if (unpacker) {
      if (unpacker->FindDataPacket(qtpath, packet_type, channel, data_loc)) {
        return true;
      } else {
        layer = parent_layer_[layer];
        if (layer == 0) {
          return false;
        }
      }
    } else {
      std::cerr << "No unpacker for " << layer << std::endl;
      return false;
    }
  }
}

/**
 * Find map data packet and set offset and size for the packet.
 * Map data packets can be imagery or vectors.
 */
bool GlcUnpacker::FindMapDataPacket(const char* qtpath,
                                    int packet_type,
                                    int channel,
                                    int layer,
                                    PackageFileLoc* data_loc) {
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return false;
  }

  FileUnpacker* unpacker;
  while (true) {
    unpacker = GetUnpacker(layer);
    if (unpacker) {
      if (unpacker->FindMapDataPacket(qtpath, packet_type, channel, data_loc)) {
        return true;
      } else {
        layer = parent_layer_[layer];
        if (layer == 0) {
          return false;
        }
      }
    } else {
      return false;
    }
  }
}

void GlcUnpacker::MapDataPacketWalker(const map_packet_walker& walker) const
{
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return;
  }

  for( auto& index_item : unpacker_index_) {
    if (index_item.second->MapDataPacketWalker(index_item.first, walker) == true) {
      break;
    }
  }
}

void GlcUnpacker::MapDataPacketWalker(int layer, const map_packet_walker& walker) const
{
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return;
  }
  auto iter = unpacker_index_.find(layer);
  if (iter == unpacker_index_.end()) {
    std::cerr << "Layer " << layer << " not found." << std::endl;
    return;
  }
  iter->second->MapDataPacketWalker(layer, walker);
}

/**
 * Find the names of all files in each layer of a globe.
 */
bool GlcUnpacker::MapFileWalker(const map_file_walker& walker)
{
    for( auto& index_item : unpacker_index_) {
        if (MapFileWalker(index_item.first, walker)) {
          return true;
        }
    }
    return false;
}

bool GlcUnpacker::MapFileWalker(int layer, const map_file_walker& walker)
{
  FileUnpacker* unpacker = GetUnpacker(layer);

  for (int i = 0; i < unpacker->IndexSize(); ++i) {
    const char *package_file = unpacker->IndexFile(i);

    if (walker(layer, package_file) != true) {
      return true;
    }
  }
  return false;
}

/**
 * Find qtp packet and set offset and size for the packet. Qtp packets can
 * be quadtree packets or a dbroot packet.
 */
bool GlcUnpacker::FindQtpPacket(const char* qtpath,
                                int packet_type,
                                int channel,
                                int layer,
                                PackageFileLoc* data_loc) {
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return false;
  }

  FileUnpacker* unpacker;
  while (true) {
    unpacker = GetUnpacker(layer);
    if (unpacker) {
      if (unpacker->FindQtpPacket(qtpath, packet_type, channel, data_loc)) {
        return true;
      } else {
        layer = parent_layer_[layer];
        if (layer == 0) {
          return false;
        }
      }
    } else {
      return false;
    }
  }
}

/**
 * Find max LOD where there is data of some kind at the given address.
 * Does not look at resolutions higher than the given address so
 * possible values are 0 to len(qtpath) - 1. As for all other qtpaths,
 * expects leading "0".
 */
int GlcUnpacker::MaxResolutionLod(const char* qtpath, bool is_2d) {
  std::string new_qtpath(qtpath);
  size_t len = new_qtpath.size();
  int lod = len - 1;
  for (size_t i = 1; i < len; ++i) {
    if (HasData(new_qtpath.c_str(), is_2d)) {
      return lod;
    }
    new_qtpath = new_qtpath.substr(0, lod);
    --lod;
  }

  return lod;
}

/**
 * Returns whether there are any data at the given address.
 */
bool GlcUnpacker::HasData(const char* qtpath, bool is_2d) {
  std::map<int, FileUnpacker*>::iterator it;
  for (it = unpacker_index_.begin(); it != unpacker_index_.end(); ++it) {
    int layer_id = it->first;

    // Skip 2d if we are looking for 3d data.
    if (layer_type_[layer_id] == MAP_LAYER_TYPE) {
      if (!is_2d) {
        continue;
      }
    // Skip 3d if we are looking for 2d data.
    } else {
      if (is_2d) {
        continue;
      }
    }

    FileUnpacker* unpacker = GetUnpacker(layer_id);
    if (unpacker->HasData(qtpath, is_2d)) {
      return true;
    }
  }

  return false;
}

/**
 * Find layer file and set the offset and size for retrieving the file from the
 * package.
 */
bool GlcUnpacker::FindLayerFile(const char* file_name,
                                int layer,
                                PackageFileLoc* file_loc) {
  FileUnpacker* unpacker = GetUnpacker(layer);
  if (unpacker) {
    return unpacker->FindFile(file_name, file_loc);
  } else {
    return false;
  }
}

/**
 * Find file and set the offset and size for retrieving the file from the
 * package.
 */
bool GlcUnpacker::FindFile(const char* file_name,
                           PackageFileLoc* file_loc) const {
  std::map<std::string, PackageFileLoc>::const_iterator it;
  it = index_.find(file_name);
  if (it != index_.end()) {
    file_loc->Set(it->second.Offset(), it->second.Size());
    return true;
  } else {
    std::cerr << "Unable to find: " << file_name << std::endl;
    file_loc->Set(0, 0);
    return false;
  }
}

/**
 * Find meta dbroot for the composite globe.
 */
bool GlcUnpacker::FindMetaDbRoot(PackageFileLoc* data_loc) {
  if (!meta_dbroot_path_.empty()) {
    return FindFile(meta_dbroot_path_.c_str(), data_loc);
  }

  data_loc->Set(0, 0);
  return false;
}

/**
 * Find dbroot for the composite globe.
 */
bool GlcUnpacker::FindDbRoot(PackageFileLoc* data_loc) {
  if (is_composite_) {
    return FindMetaDbRoot(data_loc);
  } else if (is_3d_) {
    assert(unpacker_index_[0]);
    return unpacker_index_[0]->FindDbRoot(data_loc);
  }

  data_loc->Set(0, 0);
  return false;
}

/**
 * Get idx-th file in index. Assumes iteration order is stable if
 * index is unchanged.
 */
const char* GlcUnpacker::IndexFile(int idx) const

/**
 * Get the unpacker associated with the given layer.  Returns null
 * if it is not found.
 */
FileUnpacker* GlcUnpacker::GetUnpacker(int layer) {
  std::map<int, FileUnpacker*>::iterator it = unpacker_index_.find(layer);
  if (it == unpacker_index_.end()) {
    std::cerr << "No layer " << layer << std::endl;
    return 0;
  }

  return it->second;
}

/**
 * Get number of layers in current globe or map.
 */
int GlcUnpacker::NumberOfLayers() {
  return unpacker_index_.size();
}

/**
 * Get nth layer's index.
 */
int GlcUnpacker::LayerIndex(int n) {
  if ((n < 0) || (n >= static_cast<int>(unpacker_index_.size()))) {
    return -1;
  }

  std::map<int, FileUnpacker*>::iterator it = unpacker_index_.begin();
  for (int j = 0; j < n; ++j, ++it) ;
  return it->first;
}

/**
 * Extracts the first timestamp from the info file, which should
 * be a GMT timestamp. Assumes date begins with 20 (fails in 2100).
 * The beginning of the info file is quite structured so this
 * should be reliable. It starts with 3 lines similar to the
 * following:
 *   Portable Globe
 *   2012-02-24 01:26:07 GMT
 */
std::string GlcUnpacker::ExtractDateFromInfo() {
  size_t idx = info_.find("\n20");
  return info_.substr(idx + 1, 4)
       + info_.substr(idx + 6, 2)
       + info_.substr(idx + 9, 2)
       + info_.substr(idx + 12, 2)
       + info_.substr(idx + 15, 2)
       + info_.substr(idx + 18, 2);
}

/**
 * Calculates a crc for the info data.
 */
 std::uint32_t GlcUnpacker::InfoCrc() {
  std::uint32_t crc = 0;
  unsigned char* ptr = reinterpret_cast<unsigned char*>(&info_[0]);
  for (std::uint32_t i = 0; i < info_.size(); ++i) {
    // Use the full bit space by shifting to one of the
    std::uint32_t value = *ptr++ << ((i & 3) * 8);
    crc += value;
  }
  return crc;
}

/**
 * Calculate the id associated with this file. Assumes that the bundle
 * contains a file called "earth/info.txt" which uniquely identifies
 * the file. If not, it returns "noinfo-<file_size>".
 */
const char* GlcUnpacker::Id() {
  std::stringstream sstream;
  std::string info;
  if (id_.empty()) {
    if (info_.empty()) {
      sstream << "noinfo";
    } else {
      sstream << ExtractDateFromInfo();
      sstream << "-" << std::hex << InfoCrc();
    }
    sstream << "-" << std::hex << length_;
    id_ = sstream.str();
  }

  return id_.c_str();
}

std::map<int, std::string> GlcUnpacker::getKmlData() {
  std::map<int, std::string> kmlData;

  for (auto &mapLayer : unpacker_index_) {
    FileUnpacker *layerFile = mapLayer.second;
    auto layerId = mapLayer.first;

    for (int i = 0; i < layerFile->IndexSize(); ++i) {
      auto package_file = layerFile->IndexFile(i);

      if (strcmp(package_file, "earth/polygon.kml")) {
        continue;
      }

      PackageFileLoc file_loc;
      if (layerFile->FindFile(package_file, &file_loc)) {
        auto& kmlDataStr = kmlData[layerId];
        if (!glc_reader_.Read(&kmlDataStr, file_loc.Offset(), file_loc.Size())) {
            std::cerr << "Unable to read kml file data." << std::endl;
        }
      } else {
          std::cerr << "Unable to to position to the earth/polygon.kml file data." << std::endl;
      }

      break;
    }
  }

  return kmlData;
}


// Uses unpacker to get data from the glc file.

#include "./glc_unpacker.h"
#include <iostream>  // NOLINT(readability/streams)
#include <map>
#include <sstream>  // NOLINT(readability/streams)
#include <string>
#include <cstring>
#include <cassert>
#include "./file_package.h"
#include "./file_unpacker.h"
#include "./glc_reader.h"

const char* LAYER_INFO = "earth/layer_info.txt";
// Types for layers
const char* IMAGERY_LAYER_TYPE = "IMAGE";
const char* TERRAIN_LAYER_TYPE = "TERRAIN";
const char* VECTOR_LAYER_TYPE = "VECTOR";
const char* MAP_LAYER_TYPE = "MAP";

// GEE identifying bytes in all glxs.
const std::uint32_t GEE_ID = 0x47454547;
const std::uint64_t GEE_ID_OFFSET = 8;

/**
 * Constructor. Load the index to all of the files and
 * packages in the composite file. If this is a package file,
 * treat it as a composite with a single layer that is the
 * whole file. If files_only flag is true, only read
 * top level files (not used for serving tiles).
 */
GlcUnpacker::GlcUnpacker(const GlcReader& glc_reader,
                         bool is_composite,
                         bool files_only)
    : glc_reader_(glc_reader),
    is_composite_(is_composite),
    is_gee_(false),
    is_2d_(false),
    is_3d_(false) {
  length_ = glc_reader_.Size();

  // Check file is a GEE file first.
  std::uint32_t id;
  std::int64_t offset = length_ - GEE_ID_OFFSET;
  if (!glc_reader_.ReadData(&id, offset, sizeof(id))) {
    std::cerr << "Unable to read file id." << std::endl;
    return;
  }
  if (id != GEE_ID) {
    std::cerr << "Id does not match expected GEE id." << std::endl;
    return;
  } else {
    is_gee_ = true;
  }

  // Get offset to index.
  offset = length_ - Package::kIndexOffsetOffset;
  std::uint32_t num_files = 0;
  std::int64_t index_offset;
  if (!glc_reader_.ReadData(
      &index_offset, offset, Package::kIndexOffsetSize)) {
    std::cerr << "Unable to read file offset." << std::endl;
    return;
  }
  // If negative, use as offset from end of file.
  if (index_offset < 0) {
    reader_offset_ = length_ + index_offset;
  } else {
    reader_offset_ = index_offset;
  }

  // Read in the index header.
  if (!Read(&num_files, sizeof(num_files))) {
    std::cerr << "Unable to read number of files in file." << std::endl;
    return;
  }

  // Read in the index entries.
  std::string path;
  PackageFileLoc file_loc;
  for (std::uint32_t i = 0; i < num_files; ++i) {
    // Read in the relative path of the index entry.
    std::uint16_t path_len;
    if (!Read(&path_len, sizeof(path_len))) {
      std::cerr << "Unable to read file name length." << std::endl;
      return;
    }

    path.resize(path_len);
    if (!Read(&path[0], path_len)) {
      std::cerr << "Unable to read file name." << std::endl;
      return;
    }

    // Read in the size and offset of the index entry.
    if (!Read(&file_loc, sizeof(PackageFileLoc))) {
      std::cerr << "Unable to read file size and offset." << std::endl;
      return;
    }
    // If offsets in index are negative, add them to file size to get offset
    // relative to beginning of file.
    file_loc.NormalizeNegativeOffset(length_);
    index_[path] = file_loc;
  }

  // For a real composite, set up file_unpacker for each layer.
  if (is_composite) {
    SetUpLayerUnpackers(files_only);

  // If this is just a glb or glm, set up a single layer 0.
  } else {
    std::string suffix = glc_reader_.Suffix();
    if (suffix == "glb") {
      is_3d_ = true;
    } else if (suffix == "glm")  {
      is_2d_ = true;
    } else {
      std::cout << "Unknown suffix: " << suffix << std::endl;
      return;
    }

    if (files_only) {
      return;
    }

    unpacker_index_[0] = new FileUnpacker(glc_reader_, 0, length_);
  }

  // Read in earth/info.txt
  std::map<std::string, PackageFileLoc>::iterator it
      = index_.find("earth/info.txt");
  if (it != index_.end()) {
    file_loc = it->second;
    if (!glc_reader_.Read(&info_, file_loc.Offset(), file_loc.Size())) {
      std::cerr << "Unable to read earth/info.txt" << std::endl;
    }
  }
}

GlcUnpacker::~GlcUnpacker() {
  std::map<int, FileUnpacker*>::iterator it;
  for (it = unpacker_index_.begin(); it != unpacker_index_.end(); ++it) {
    delete it->second;
  }
}

/**
 * Helper method for the Constructor that creates a file_unpacker for each
 * layer listed in the layer_info.txt file.
 */
void GlcUnpacker::SetUpLayerUnpackers(bool files_only) {
  PackageFileLoc file_loc;
  if (!FindFile(LAYER_INFO, &file_loc)) {
    std::cerr << "Unable to load layer info." << std::endl;
    return;
  }

  std::string layer_info;
  if (!glc_reader_.Read(&layer_info, file_loc.Offset(), file_loc.Size())) {
    std::cerr << "Unable to read layer info." << std::endl;
    return;
  }

  std::stringstream stream(
      layer_info, std::stringstream::in);

  std::string layer_name;
  std::string layer_path;
  std::string layer_type;
  int layer_id;
  int parent_layer;

  // Read in layer info.
  while (true) {
    stream >> layer_name;
    stream >> layer_path;
    stream >> layer_type;
    stream >> layer_id;

    // Ensure that a full line of data was available.
    if (stream.eof()) {
      break;
    }

    stream >> parent_layer;

    // Open new unpacker for glb layer.
    if ((layer_type == IMAGERY_LAYER_TYPE) ||
        (layer_type == TERRAIN_LAYER_TYPE) ||
        (layer_type == VECTOR_LAYER_TYPE) ||
        (layer_type == MAP_LAYER_TYPE)) {
      if (layer_type == MAP_LAYER_TYPE) {
        is_2d_ = true;
      } else {
        is_3d_ = true;
      }
      if (!files_only) {
        std::map<std::string, PackageFileLoc>::iterator it =
            index_.find(layer_path);
        if (it != index_.end()) {
          unpacker_index_[layer_id] = new FileUnpacker(
              glc_reader_, it->second.Offset(), it->second.Size());
          parent_layer_[layer_id] = parent_layer;
          layer_type_[layer_id] = layer_type;
        } else {
          std::cerr << "Missing layer: " << layer_path << std::endl;
        }
      }
    } else if (layer_type == "DBROOT") {
      meta_dbroot_path_ = layer_path;
    }
  }
}

/**
 * Helper for reading in consecutive fields from the glc file.
 */
bool GlcUnpacker::Read(void* data, std::uint64_t size) {
  if (glc_reader_.ReadData(data, reader_offset_, size)) {
    reader_offset_ += size;
    return true;
  }
  return false;
}

/**
 * Find data packet and set offset and size for the packet. Data packets can
 * be imagery, terrain or vectors.
 */
bool GlcUnpacker::FindDataPacket(const char* qtpath,
                                 int packet_type,
                                 int channel,
                                 int layer,
                                 PackageFileLoc* data_loc) {
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return false;
  }

  FileUnpacker* unpacker;
  while (true) {
    unpacker = GetUnpacker(layer);
    if (unpacker) {
      if (unpacker->FindDataPacket(qtpath, packet_type, channel, data_loc)) {
        return true;
      } else {
        layer = parent_layer_[layer];
        if (layer == 0) {
          return false;
        }
      }
    } else {
      std::cerr << "No unpacker for " << layer << std::endl;
      return false;
    }
  }
}

/**
 * Find map data packet and set offset and size for the packet.
 * Map data packets can be imagery or vectors.
 */
bool GlcUnpacker::FindMapDataPacket(const char* qtpath,
                                    int packet_type,
                                    int channel,
                                    int layer,
                                    PackageFileLoc* data_loc) {
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return false;
  }

  FileUnpacker* unpacker;
  while (true) {
    unpacker = GetUnpacker(layer);
    if (unpacker) {
      if (unpacker->FindMapDataPacket(qtpath, packet_type, channel, data_loc)) {
        return true;
      } else {
        layer = parent_layer_[layer];
        if (layer == 0) {
          return false;
        }
      }
    } else {
      return false;
    }
  }
}

void GlcUnpacker::MapDataPacketWalker(const map_packet_walker& walker) const
{
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return;
  }

  for( auto& index_item : unpacker_index_) {
    if (index_item.second->MapDataPacketWalker(index_item.first, walker) == true) {
      break;
    }
  }
}

void GlcUnpacker::MapDataPacketWalker(int layer, const map_packet_walker& walker) const
{
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return;
  }
  auto iter = unpacker_index_.find(layer);
  if (iter == unpacker_index_.end()) {
    std::cerr << "Layer " << layer << " not found." << std::endl;
    return;
  }
  iter->second->MapDataPacketWalker(layer, walker);
}

/**
 * Find the names of all files in each layer of a globe.
 */
bool GlcUnpacker::MapFileWalker(const map_file_walker& walker)
{
    for( auto& index_item : unpacker_index_) {
        if (MapFileWalker(index_item.first, walker)) {
          return true;
        }
    }
    return false;
}

bool GlcUnpacker::MapFileWalker(int layer, const map_file_walker& walker)
{
  FileUnpacker* unpacker = GetUnpacker(layer);

  for (int i = 0; i < unpacker->IndexSize(); ++i) {
    const char *package_file = unpacker->IndexFile(i);

    if (walker(layer, package_file) != true) {
      return true;
    }
  }
  return false;
}

/**
 * Find qtp packet and set offset and size for the packet. Qtp packets can
 * be quadtree packets or a dbroot packet.
 */
bool GlcUnpacker::FindQtpPacket(const char* qtpath,
                                int packet_type,
                                int channel,
                                int layer,
                                PackageFileLoc* data_loc) {
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return false;
  }

  FileUnpacker* unpacker;
  while (true) {
    unpacker = GetUnpacker(layer);
    if (unpacker) {
      if (unpacker->FindQtpPacket(qtpath, packet_type, channel, data_loc)) {
        return true;
      } else {
        layer = parent_layer_[layer];
        if (layer == 0) {
          return false;
        }
      }
    } else {
      return false;
    }
  }
}

/**
 * Find max LOD where there is data of some kind at the given address.
 * Does not look at resolutions higher than the given address so
 * possible values are 0 to len(qtpath) - 1. As for all other qtpaths,
 * expects leading "0".
 */
int GlcUnpacker::MaxResolutionLod(const char* qtpath, bool is_2d) {
  std::string new_qtpath(qtpath);
  size_t len = new_qtpath.size();
  int lod = len - 1;
  for (size_t i = 1; i < len; ++i) {
    if (HasData(new_qtpath.c_str(), is_2d)) {
      return lod;
    }
    new_qtpath = new_qtpath.substr(0, lod);
    --lod;
  }

  return lod;
}

/**
 * Returns whether there are any data at the given address.
 */
bool GlcUnpacker::HasData(const char* qtpath, bool is_2d) {
  std::map<int, FileUnpacker*>::iterator it;
  for (it = unpacker_index_.begin(); it != unpacker_index_.end(); ++it) {
    int layer_id = it->first;

    // Skip 2d if we are looking for 3d data.
    if (layer_type_[layer_id] == MAP_LAYER_TYPE) {
      if (!is_2d) {
        continue;
      }
    // Skip 3d if we are looking for 2d data.
    } else {
      if (is_2d) {
        continue;
      }
    }

    FileUnpacker* unpacker = GetUnpacker(layer_id);
    if (unpacker->HasData(qtpath, is_2d)) {
      return true;
    }
  }

  return false;
}

/**
 * Find layer file and set the offset and size for retrieving the file from the
 * package.
 */
bool GlcUnpacker::FindLayerFile(const char* file_name,
                                int layer,
                                PackageFileLoc* file_loc) {
  FileUnpacker* unpacker = GetUnpacker(layer);
  if (unpacker) {
    return unpacker->FindFile(file_name, file_loc);
  } else {
    return false;
  }
}

/**
 * Find file and set the offset and size for retrieving the file from the
 * package.
 */
bool GlcUnpacker::FindFile(const char* file_name,
                           PackageFileLoc* file_loc) const {
  std::map<std::string, PackageFileLoc>::const_iterator it;
  it = index_.find(file_name);
  if (it != index_.end()) {
    file_loc->Set(it->second.Offset(), it->second.Size());
    return true;
  } else {
    std::cerr << "Unable to find: " << file_name << std::endl;
    file_loc->Set(0, 0);
    return false;
  }
}

/**
 * Find meta dbroot for the composite globe.
 */
bool GlcUnpacker::FindMetaDbRoot(PackageFileLoc* data_loc) {
  if (!meta_dbroot_path_.empty()) {
    return FindFile(meta_dbroot_path_.c_str(), data_loc);
  }

  data_loc->Set(0, 0);
  return false;
}

/**
 * Find dbroot for the composite globe.
 */
bool GlcUnpacker::FindDbRoot(PackageFileLoc* data_loc) {
  if (is_composite_) {
    return FindMetaDbRoot(data_loc);
  } else if (is_3d_) {
    assert(unpacker_index_[0]);
    return unpacker_index_[0]->FindDbRoot(data_loc);
  }

  data_loc->Set(0, 0);
  return false;
}

/**
 * Get idx-th file in index. Assumes iteration order is stable if
 * index is unchanged.
 */
const char* GlcUnpacker::IndexFile(int idx) const {
  std::map<std::string, PackageFileLoc>::const_iterator it;
  it = index_.begin();
  for (int i = 0; (i < idx) && (it != index_.end()); ++i, ++it) ;

  if (it != index_.end()) {
    return it->first.c_str();
  }

  return "";
}

/**
 * Get the unpacker associated with the given layer.  Returns null
 * if it is not found.
 */
FileUnpacker* GlcUnpacker::GetUnpacker(int layer) {
  std::map<int, FileUnpacker*>::iterator it = unpacker_index_.find(layer);
  if (it == unpacker_index_.end()) {
    std::cerr << "No layer " << layer << std::endl;
    return 0;
  }

  return it->second;
}

/**
 * Get number of layers in current globe or map.
 */
int GlcUnpacker::NumberOfLayers() {
  return unpacker_index_.size();
}

/**
 * Get nth layer's index.
 */
int GlcUnpacker::LayerIndex(int n) {
  if ((n < 0) || (n >= static_cast<int>(unpacker_index_.size()))) {
    return -1;
  }

  std::map<int, FileUnpacker*>::iterator it = unpacker_index_.begin();
  for (int j = 0; j < n; ++j, ++it) ;
  r

}

/**
 * Extracts the first timestamp from the info file, which should
 * be a GMT timestamp. Assumes date begins with 20 (fails in 2100).
 * The beginning of the info file is quite structured so this
 * should be reliable. It starts with 3 lines similar to the
 * following:
 *   Portable Globe
 *   2012-02-24 01:26:07 GMT
 */
std::string GlcUnpacker::ExtractDateFromInfo() {
  size_t idx = info_.find("\n20");
  return info_.substr(idx + 1, 4)
       + info_.substr(idx + 6, 2)
       + info_.substr(idx + 9, 2)
       + info_.substr(idx + 12, 2)
       + info_.substr(idx + 15, 2)
       + info_.substr(idx + 18, 2);
}

/**
 * Calculates a crc for the info data.
 */
 std::uint32_t GlcUnpacker::InfoCrc() {
  std::uint32_t crc = 0;
  unsigned char* ptr = reinterpret_cast<unsigned char*>(&info_[0]);
  for (std::uint32_t i = 0; i < info_.size(); ++i) {
    // Use the full bit space by shifting to one of the
    std::uint32_t value = *ptr++ << ((i & 3) * 8);
    crc += value;
  }
  return crc;
}

/**
 * Calculate the id associated with this file. Assumes that the bundle
 * contains a file called "earth/info.txt" which uniquely identifies
 * the file. If not, it returns "noinfo-<file_size>".
 */
const char* GlcUnpacker::Id() {
  std::stringstream sstream;
  std::string info;
  if (id_.empty()) {
    if (info_.empty()) {
      sstream << "noinfo";
    } else {
      sstream << ExtractDateFromInfo();
      sstream << "-" << std::hex << InfoCrc();
    }
    sstream << "-" << std::hex << length_;
    id_ = sstream.str();
  }

  return id_.c_str();
}

std::map<int, std::string> GlcUnpacker::getKmlData() {
  std::map<int, std::string> kmlData;

  for (auto &mapLayer : unpacker_index_) {
    FileUnpacker *layerFile = mapLayer.second;
    auto layerId = mapLayer.first;

    for (int i = 0; i < layerFile->IndexSize(); ++i) {
      auto package_file = layerFile->IndexFile(i);

      if (strcmp(package_file, "earth/polygon.kml")) {
        continue;
      }

      PackageFileLoc file_loc;
      if (layerFile->FindFile(package_file, &file_loc)) {
        auto& kmlDataStr = kmlData[layerId];
        if (!glc_reader_.Read(&kmlDataStr, file_loc.Offset(), file_loc.Size())) {
            std::cerr << "Unable to read kml file data." << std::endl;
        }
      } else {
          std::cerr << "Unable to to position to the earth/polygon.kml file data." << std::endl;
      }

      break;
    }
  }

  return kmlData;
}


FileUnpacker* GlcUnpacker::GetUnpacker(int layer) {
  std::map<int, FileUnpacker*>::iterator it = unpacker_index_.find(layer);
  if (it == unpacker_index_.end()) {
    std::cerr << "No layer " << layer << std::endl;
    return 0;
  }

  return it->second;
}

// Uses unpacker to get data from the glc file.

#include "./glc_unpacker.h"
#include <iostream>  // NOLINT(readability/streams)
#include <map>
#include <sstream>  // NOLINT(readability/streams)
#include <string>
#include <cstring>
#include <cassert>
#include "./file_package.h"
#include "./file_unpacker.h"
#include "./glc_reader.h"

const char* LAYER_INFO = "earth/layer_info.txt";
// Types for layers
const char* IMAGERY_LAYER_TYPE = "IMAGE";
const char* TERRAIN_LAYER_TYPE = "TERRAIN";
const char* VECTOR_LAYER_TYPE = "VECTOR";
const char* MAP_LAYER_TYPE = "MAP";

// GEE identifying bytes in all glxs.
const std::uint32_t GEE_ID = 0x47454547;
const std::uint64_t GEE_ID_OFFSET = 8;

/**
 * Constructor. Load the index to all of the files and
 * packages in the composite file. If this is a package file,
 * treat it as a composite with a single layer that is the
 * whole file. If files_only flag is true, only read
 * top level files (not used for serving tiles).
 */
GlcUnpacker::GlcUnpacker(const GlcReader& glc_reader,
                         bool is_composite,
                         bool files_only)
    : glc_reader_(glc_reader),
    is_composite_(is_composite),
    is_gee_(false),
    is_2d_(false),
    is_3d_(false) {
  length_ = glc_reader_.Size();

  // Check file is a GEE file first.
  std::uint32_t id;
  std::int64_t offset = length_ - GEE_ID_OFFSET;
  if (!glc_reader_.ReadData(&id, offset, sizeof(id))) {
    std::cerr << "Unable to read file id." << std::endl;
    return;
  }
  if (id != GEE_ID) {
    std::cerr << "Id does not match expected GEE id." << std::endl;
    return;
  } else {
    is_gee_ = true;
  }

  // Get offset to index.
  offset = length_ - Package::kIndexOffsetOffset;
  std::uint32_t num_files = 0;
  std::int64_t index_offset;
  if (!glc_reader_.ReadData(
      &index_offset, offset, Package::kIndexOffsetSize)) {
    std::cerr << "Unable to read file offset." << std::endl;
    return;
  }
  // If negative, use as offset from end of file.
  if (index_offset < 0) {
    reader_offset_ = length_ + index_offset;
  } else {
    reader_offset_ = index_offset;
  }

  // Read in the index header.
  if (!Read(&num_files, sizeof(num_files))) {
    std::cerr << "Unable to read number of files in file." << std::endl;
    return;
  }

  // Read in the index entries.
  std::string path;
  PackageFileLoc file_loc;
  for (std::uint32_t i = 0; i < num_files; ++i) {
    // Read in the relative path of the index entry.
    std::uint16_t path_len;
    if (!Read(&path_len, sizeof(path_len))) {
      std::cerr << "Unable to read file name length." << std::endl;
      return;
    }

    path.resize(path_len);
    if (!Read(&path[0], path_len)) {
      std::cerr << "Unable to read file name." << std::endl;
      return;
    }

    // Read in the size and offset of the index entry.
    if (!Read(&file_loc, sizeof(PackageFileLoc))) {
      std::cerr << "Unable to read file size and offset." << std::endl;
      return;
    }
    // If offsets in index are negative, add them to file size to get offset
    // relative to beginning of file.
    file_loc.NormalizeNegativeOffset(length_);
    index_[path] = file_loc;
  }

  // For a real composite, set up file_unpacker for each layer.
  if (is_composite) {
    SetUpLayerUnpackers(files_only);

  // If this is just a glb or glm, set up a single layer 0.
  } else {
    std::string suffix = glc_reader_.Suffix();
    if (suffix == "glb") {
      is_3d_ = true;
    } else if (suffix == "glm")  {
      is_2d_ = true;
    } else {
      std::cout << "Unknown suffix: " << suffix << std::endl;
      return;
    }

    if (files_only) {
      return;
    }

    unpacker_index_[0] = new FileUnpacker(glc_reader_, 0, length_);
  }

  // Read in earth/info.txt
  std::map<std::string, PackageFileLoc>::iterator it
      = index_.find("earth/info.txt");
  if (it != index_.end()) {
    file_loc = it->second;
    if (!glc_reader_.Read(&info_, file_loc.Offset(), file_loc.Size())) {
      std::cerr << "Unable to read earth/info.txt" << std::endl;
    }
  }
}

GlcUnpacker::~GlcUnpacker() {
  std::map<int, FileUnpacker*>::iterator it;
  for (it = unpacker_index_.begin(); it != unpacker_index_.end(); ++it) {
    delete it->second;
  }
}

/**
 * Helper method for the Constructor that creates a file_unpacker for each
 * layer listed in the layer_info.txt file.
 */
void GlcUnpacker::SetUpLayerUnpackers(bool files_only) {
  PackageFileLoc file_loc;
  if (!FindFile(LAYER_INFO, &file_loc)) {
    std::cerr << "Unable to load layer info." << std::endl;
    return;
  }

  std::string layer_info;
  if (!glc_reader_.Read(&layer_info, file_loc.Offset(), file_loc.Size())) {
    std::cerr << "Unable to read layer info." << std::endl;
    return;
  }

  std::stringstream stream(
      layer_info, std::stringstream::in);

  std::string layer_name;
  std::string layer_path;
  std::string layer_type;
  int layer_id;
  int parent_layer;

  // Read in layer info.
  while (true) {
    stream >> layer_name;
    stream >> layer_path;
    stream >> layer_type;
    stream >> layer_id;

    // Ensure that a full line of data was available.
    if (stream.eof()) {
      break;
    }

    stream >> parent_layer;

    // Open new unpacker for glb layer.
    if ((layer_type == IMAGERY_LAYER_TYPE) ||
        (layer_type == TERRAIN_LAYER_TYPE) ||
        (layer_type == VECTOR_LAYER_TYPE) ||
        (layer_type == MAP_LAYER_TYPE)) {
      if (layer_type == MAP_LAYER_TYPE) {
        is_2d_ = true;
      } else {
        is_3d_ = true;
      }
      if (!files_only) {
        std::map<std::string, PackageFileLoc>::iterator it =
            index_.find(layer_path);
        if (it != index_.end()) {
          unpacker_index_[layer_id] = new FileUnpacker(
              glc_reader_, it->second.Offset(), it->second.Size());
          parent_layer_[layer_id] = parent_layer;
          layer_type_[layer_id] = layer_type;
        } else {
          std::cerr << "Missing layer: " << layer_path << std::endl;
        }
      }
    } else if (layer_type == "DBROOT") {
      meta_dbroot_path_ = layer_path;
    }
  }
}

/**
 * Helper for reading in consecutive fields from the glc file.
 */
bool GlcUnpacker::Read(void* data, std::uint64_t size) {
  if (glc_reader_.ReadData(data, reader_offset_, size)) {
    reader_offset_ += size;
    return true;
  }
  return false;
}

/**
 * Find data packet and set offset and size for the packet. Data packets can
 * be imagery, terrain or vectors.
 */
bool GlcUnpacker::FindDataPacket(const char* qtpath,
                                 int packet_type,
                                 int channel,
                                 int layer,
                                 PackageFileLoc* data_loc) {
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return false;
  }

  FileUnpacker* unpacker;
  while (true) {
    unpacker = GetUnpacker(layer);
    if (unpacker) {
      if (unpacker->FindDataPacket(qtpath, packet_type, channel, data_loc)) {
        return true;
      } else {
        layer = parent_layer_[layer];
        if (layer == 0) {
          return false;
        }
      }
    } else {
      std::cerr << "No unpacker for " << layer << std::endl;
      return false;
    }
  }
}

/**
 * Find map data packet and set offset and size for the packet.
 * Map data packets can be imagery or vectors.
 */
bool GlcUnpacker::FindMapDataPacket(const char* qtpath,
                                    int packet_type,
                                    int channel,
                                    int layer,
                                    PackageFileLoc* data_loc) {
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return false;
  }

  FileUnpacker* unpacker;
  while (true) {
    unpacker = GetUnpacker(layer);
    if (unpacker) {
      if (unpacker->FindMapDataPacket(qtpath, packet_type, channel, data_loc)) {
        return true;
      } else {
        layer = parent_layer_[layer];
        if (layer == 0) {
          return false;
        }
      }
    } else {
      return false;
    }
  }
}

void GlcUnpacker::MapDataPacketWalker(const map_packet_walker& walker) const
{
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return;
  }

  for( auto& index_item : unpacker_index_) {
    if (index_item.second->MapDataPacketWalker(index_item.first, walker) == true) {
      break;
    }
  }
}

void GlcUnpacker::MapDataPacketWalker(int layer, const map_packet_walker& walker) const
{
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return;
  }
  auto iter = unpacker_index_.find(layer);
  if (iter == unpacker_index_.end()) {
    std::cerr << "Layer " << layer << " not found." << std::endl;
    return;
  }
  iter->second->MapDataPacketWalker(layer, walker);
}

/**
 * Find the names of all files in each layer of a globe.
 */
bool GlcUnpacker::MapFileWalker(const map_file_walker& walker)
{
    for( auto& index_item : unpacker_index_) {
        if (MapFileWalker(index_item.first, walker)) {
          return true;
        }
    }
    return false;
}

bool GlcUnpacker::MapFileWalker(int layer, const map_file_walker& walker)
{
  FileUnpacker* unpacker = GetUnpacker(layer);

  for (int i = 0; i < unpacker->IndexSize(); ++i) {
    const char *package_file = unpacker->IndexFile(i);

    if (walker(layer, package_file) != true) {
      return true;
    }
  }
  return false;
}

/**
 * Find qtp packet and set offset and size for the packet. Qtp packets can
 * be quadtree packets or a dbroot packet.
 */
bool GlcUnpacker::FindQtpPacket(const char* qtpath,
                                int packet_type,
                                int channel,
                                int layer,
                                PackageFileLoc* data_loc) {
  if (!is_gee_) {
    std::cerr << "Not a GEE file." << std::endl;
    return false;
  }

  FileUnpacker* unpacker;
  while (true) {
    unpacker = GetUnpacker(layer);
    if (unpacker) {
      if (unpacker->FindQtpPacket(qtpath, packet_type, channel, data_loc)) {
        return true;
      } else {
        layer = parent_layer_[layer];
        if (layer == 0) {
          return false;
        }
      }
    } else {
      return false;
    }
  }
}

/**
 * Find max LOD where there is data of some kind at the given address.
 * Does not look at resolutions higher than the given address so
 * possible values are 0 to len(qtpath) - 1. As for all other qtpaths,
 * expects leading "0".
 */
int GlcUnpacker::MaxResolutionLod(const char* qtpath, bool is_2d) {
  std::string new_qtpath(qtpath);
  size_t len = new_qtpath.size();
  int lod = len - 1;
  for (size_t i = 1; i < len; ++i) {
    if (HasData(new_qtpath.c_str(), is_2d)) {
      return lod;
    }
    new_qtpath = new_qtpath.substr(0, lod);
    --lod;
  }

  return lod;
}

/**
 * Returns whether there are any data at the given address.
 */
bool GlcUnpacker::HasData(const char* qtpath, bool is_2d) {
  std::map<int, FileUnpacker*>::iterator it;
  for (it = unpacker_index_.begin(); it != unpacker_index_.end(); ++it) {
    int layer_id = it->first;

    // Skip 2d if we are looking for 3d data.
    if (layer_type_[layer_id] == MAP_LAYER_TYPE) {
      if (!is_2d) {
        continue;
      }
    // Skip 3d if we are looking for 2d data.
    } else {
      if (is_2d) {
        continue;
      }
    }

    FileUnpacker* unpacker = GetUnpacker(layer_id);
    if (unpacker->HasData(qtpath, is_2d)) {
      return true;
    }
  }

  return false;
}

/**
 * Find layer file and set the offset and size for retrieving the file from the
 * package.
 */
bool GlcUnpacker::FindLayerFile(const char* file_name,
                                int layer,
                                PackageFileLoc* file_loc) {
  FileUnpacker* unpacker = GetUnpacker(layer);
  if (unpacker) {
    return unpacker->FindFile(file_name, file_loc);
  } else {
    return false;
  }
}

/**
 * Find file and set the offset and size for retrieving the file from the
 * package.
 */
bool GlcUnpacker::FindFile(const char* file_name,
                           PackageFileLoc* file_loc) const {
  std::map<std::string, PackageFileLoc>::const_iterator it;
  it = index_.find(file_name);
  if (it != index_.end()) {
    file_loc->Set(it->second.Offset(), it->second.Size());
    return true;
  } else {
    std::cerr << "Unable to find: " << file_name << std::endl;
    file_loc->Set(0, 0);
    return false;
  }
}

/**
 * Find meta dbroot for the composite globe.
 */
bool GlcUnpacker::FindMetaDbRoot(PackageFileLoc* data_loc) {
  if (!meta_dbroot_path_.empty()) {
    return FindFile(meta_dbroot_path_.c_str(), data_loc);
  }

  data_loc->Set(0, 0);
  return false;
}

/**
 * Find dbroot for the composite globe.
 */
bool GlcUnpacker::FindDbRoot(PackageFileLoc* data_loc) {
  if (is_composite_) {
    return FindMetaDbRoot(data_loc);
  } else if (is_3d_) {
    assert(unpacker_index_[0]);
    return unpacker_index_[0]->FindDbRoot(data_loc);
  }

  data_loc->Set(0, 0);
  return false;
}

/**
 * Get idx-th file in index. Assumes iteration order is stable if
 * index is unchanged.
 */
const char* GlcUnpacker::IndexFile(int idx) const {
  std::map<std::string, PackageFileLoc>::const_iterator it;
  it = index_.begin();
  for (int i = 0; (i < idx) && (it != index_.end()); ++i, ++it) ;

  if (it != index_.end()) {
    return it->first.c_str();
  }

  return "";
}

/**
 * Get the unpacker associated with the given layer.  Returns null
 * if it is not found.
 */

/**
 * Get number of layers in current globe or map.
 */
int GlcUnpacker::NumberOfLayers() {
  return unpacker_index_.size();
}

/**
 * Get nth layer's index.
 */
int GlcUnpacker::LayerIndex(int n) {
  if ((n < 0) || (n >= static_cast<int>(unpacker_index_.size()))) {
    return -1;
  }

  std::map<int, FileUnpacker*>::iterator it = unpacker_index_.begin();
  for (int j = 0; j < n; ++j, ++it) ;
  return it->first;
}

/**
 * Extracts the first timestamp from the info file, which should
 * be a GMT timestamp. Assumes date begins with 20 (fails in 2100).
 * The beginning of the info file is quite structured so this
 * should be reliable. It starts with 3 lines similar to the
 * following:
 *   Portable Globe
 *   2012-02-24 01:26:07 GMT
 */
std::string GlcUnpacker::ExtractDateFromInfo() {
  size_t idx = info_.find("\n20");
  return info_.substr(idx + 1, 4)
       + info_.substr(idx + 6, 2)
       + info_.substr(idx + 9, 2)
       + info_.substr(idx + 12, 2)
       + info_.substr(idx + 15, 2)
       + info_.substr(idx + 18, 2);
}

/**
 * Calculates a crc for the info data.
 */
 std::uint32_t GlcUnpacker::InfoCrc() {
  std::uint32_t crc = 0;
  unsigned char* ptr = reinterpret_cast<unsigned char*>(&info_[0]);
  for (std::uint32_t i = 0; i < info_.size(); ++i) {
    // Use the full bit space by shifting to one of the
    std::uint32_t value = *ptr++ << ((i & 3) * 8);
    crc += value;
  }
  return crc;
}

/**
 * Calculate the id associated with this file. Assumes that the bundle
 * contains a file called "earth/info.txt" which uniquely identifies
 * the file. If not, it returns "noinfo-<file_size>".
 */
const char* GlcUnpacker::Id() {
  std::stringstream sstream;
  std::string info;
  if (id_.empty()) {
    if (info_.empty()) {
      sstream << "noinfo";
    } else {
      sstream << ExtractDateFromInfo();
      sstream << "-" << std::hex << InfoCrc();
    }
    sstream << "-" << std::hex << length_;
    id_ = sstream.str();
  }

  return id_.c_str();
}

std::map<int, std::string> GlcUnpacker::getKmlData() {
  std::map<int, std::string> kmlData;

  for (auto &mapLayer : unpacker_index_) {
    FileUnpacker *layerFile = mapLayer.second;
    auto layerId = mapLayer.first;

    for (int i = 0; i < layerFile->IndexSize(); ++i) {
      auto package_file = layerFile->IndexFile(i);

      if (strcmp(package_file, "earth/polygon.kml")) {
        continue;
      }

      PackageFileLoc file_loc;
      if (layerFile->FindFile(package_file, &file_loc)) {
        auto& kmlDataStr = kmlData[layerId];
        if (!glc_reader_.Read(&kmlDataStr, file_loc.Offset(), file_loc.Size())) {
            std::cerr << "Unable to read kml file data." << std::endl;
        }
      } else {
          std::cerr << "Unable to to position to the earth/polygon.kml file data." << std::endl;
      }

      break;
    }
  }

  return kmlData;
}