// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/sys_info.h"

#include "base/basictypes.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/string_number_conversions.h"
#include "base/string_tokenizer.h"
#include "base/threading/thread_restrictions.h"

#include <execinfo.h>

namespace base {

static const char* kLinuxStandardBaseVersionKeys[] = {
  "CHROMEOS_RELEASE_VERSION",
  "GOOGLE_RELEASE",
  "DISTRIB_RELEASE",
  NULL
};

const char kLinuxStandardBaseReleaseFile[] = "/etc/lsb-release";

struct ChromeOSVersionNumbers {
  ChromeOSVersionNumbers()
      : major_version(0),
        minor_version(0),
        bugfix_version(0),
        parsed(false) {
  }

  int32 major_version;
  int32 minor_version;
  int32 bugfix_version;
  bool parsed;
};

static base::LazyInstance<ChromeOSVersionNumbers>
    g_chrome_os_version_numbers(base::LINKER_INITIALIZED);

// static
void SysInfo::OperatingSystemVersionNumbers(int32* major_version,
                                            int32* minor_version,
                                            int32* bugfix_version) {
  if (!g_chrome_os_version_numbers.Get().parsed) {
    // The other implementations of SysInfo don't block on the disk.
    // See http://code.google.com/p/chromium/issues/detail?id=60394
    // Perhaps the caller ought to cache this?
    // Temporary allowing while we work the bug out.
    base::ThreadRestrictions::ScopedAllowIO allow_io;

    FilePath path(kLinuxStandardBaseReleaseFile);
    std::string contents;
    if (file_util::ReadFileToString(path, &contents)) {
      g_chrome_os_version_numbers.Get().parsed = true;
      ParseLsbRelease(contents,
          &(g_chrome_os_version_numbers.Get().major_version),
          &(g_chrome_os_version_numbers.Get().minor_version),
          &(g_chrome_os_version_numbers.Get().bugfix_version));
    }
  }
  *major_version = g_chrome_os_version_numbers.Get().major_version;
  *minor_version = g_chrome_os_version_numbers.Get().minor_version;
  *bugfix_version = g_chrome_os_version_numbers.Get().bugfix_version;
}

// static
std::string SysInfo::GetLinuxStandardBaseVersionKey() {
  return std::string(kLinuxStandardBaseVersionKeys[0]);
}

// static
void SysInfo::ParseLsbRelease(const std::string& lsb_release,
                              int32* major_version,
                              int32* minor_version,
                              int32* bugfix_version) {
  size_t version_key_index = std::string::npos;
  for (int i = 0; kLinuxStandardBaseVersionKeys[i] != NULL; ++i) {
    version_key_index = lsb_release.find(kLinuxStandardBaseVersionKeys[i]);
    if (std::string::npos != version_key_index) {
      break;
    }
  }
  if (std::string::npos == version_key_index) {
    return;
  }

  size_t start_index = lsb_release.find_first_of('=', version_key_index);
  start_index++;  // Move past '='.
  size_t length = lsb_release.find_first_of('\n', start_index) - start_index;
  std::string version = lsb_release.substr(start_index, length);
  StringTokenizer tokenizer(version, ".");
  // TODO(rkc): Ignore the 0. here; fix this once we move Chrome OS version
  // numbers from the 0.xx.yyy.zz format to the xx.yyy.zz format.
  // Refer to http://code.google.com/p/chromium-os/issues/detail?id=15789
  for (int i = 0; i < 4 && tokenizer.GetNext(); i++) {
    if (1 == i) {
      StringToInt(tokenizer.token_begin(),
                  tokenizer.token_end(),
                  major_version);
      *minor_version = *bugfix_version = 0;
    } else if (2 == i) {
      StringToInt(tokenizer.token_begin(),
                  tokenizer.token_end(),
                  minor_version);
    } else {  // 3 == i
      StringToInt(tokenizer.token_begin(),
                  tokenizer.token_end(),
                  bugfix_version);
    }
  }
}

}  // namespace base