#include "stdafx.h"
#include "addon_manifest_loader.h"
#include "tinyxml.h"


Addon_manifest_loader::Addon_manifest_loader()
:
_lookup_cached_addons(true)
{
}


Addon_manifest_loader::~Addon_manifest_loader()
{
}


void Addon_manifest_loader::do_ensure_valid_state(std::string &err)
{
  Addon_proc_net::do_ensure_valid_state(err);

  if (!err.empty())
    return;
  else if (_guid.empty() && _url.empty())
    err= "GUID or URL must be set.";
}


void Addon_manifest_loader::do_execute()
{
  if (_url.empty())
    load(_guid, _min_ver, _max_ver, _lookup_cached_addons);
  else
    load(_url, _lookup_cached_addons);
}


void Addon_manifest_loader::parse_single_manifest(const std::string &filepath)
{
  Addon_proc::ensure_valid_state();
  parse_manifest(filepath, false, false);
}


void Addon_manifest_loader::load(const std::string &url, bool lookup_cached_addons)
{
  // set temporary manifest_filename
  std::string manifest_filename= "addon-manifest.tmp.xml";

  _fd->filepath(_temp_dir.path() + "/" + manifest_filename);
  _fd->log_filepath(_fd->filepath() + ".log");
  _fd->url(url + _url_suffix);

  download();

  std::string manifest_filepath= _fd->filepath();

  Addon *addon= parse_manifest(manifest_filepath, lookup_cached_addons, true);
  if (addon)
  {
    _guid= addon->guid;
    _min_ver= addon->version;
    _max_ver= addon->version;
  }
}


void Addon_manifest_loader::load(const std::string &guid, const std::string &min_ver, const std::string &max_ver, bool lookup_cached_addons)
{
  std::string manifest_filepath;

  Addon *addon= NULL;
  if (lookup_cached_addons)
    addon= _cached_addons->find(guid, min_ver, max_ver);

  if (addon)
  {
    manifest_filepath= _cached_addons->storage_dir().path() + "/" + addon->manifest_filename();
    if (!File(manifest_filepath).exists())
      manifest_filepath.clear();
  }

  if (manifest_filepath.empty())
  {
    // set temporary manifest_filename;
    std::string manifest_filename= guid + ".tmp.xml";
    _fd->filepath(_temp_dir.path() + "/" + manifest_filename);
    _fd->log_filepath(_fd->filepath() + ".log");
    _fd->url(_url_base + "?addon_part=manifest&addon=" + guid + "&addon_min_ver=" + min_ver + "&addon_max_ver=" + max_ver + _url_suffix);

    download();

    manifest_filepath= _fd->filepath();
  }
  
  parse_manifest(manifest_filepath, lookup_cached_addons, true);
}


Addon * Addon_manifest_loader::parse_manifest(const std::string &filepath, bool lookup_cached_addons, bool process_dependencies)
{
  // parse manifest file
  TiXmlDocument xml(filepath.c_str());
  if (!xml.LoadFile())
    _log->throw_err("Failed to load manifest file: " + File(filepath).quoted_path() + ".");

  const TiXmlElement *el_addon_manifest= xml.FirstChildElement("ADDON_MANIFEST");
  if (!el_addon_manifest)
    _log->throw_err("Erroneous structure of manifest file: " + File(filepath).quoted_path() + ".");

  const TiXmlElement *el_addon= el_addon_manifest->FirstChildElement("ADDON");
  if (!el_addon)
    _log->throw_err("Erroneous structure of manifest file: " + File(filepath).quoted_path() + ".");

  std::string guid;
  el_addon->QueryValueAttribute("GUID", &guid);

  std::string version;
  el_addon->QueryValueAttribute("Version", &version);

  std::string addon_name;
  el_addon->QueryValueAttribute("Name", &addon_name);

  Addon *addon= _cached_addons->find_or_create(guid, version);
  std::string manifest_filepath= _cached_addons->storage_dir().path() + "/" + addon->manifest_filename();

  if (!addon->valid() || (manifest_filepath != filepath))
  {
    addon->reset();

    el_addon->QueryValueAttribute("Description", &addon->description);
    el_addon->QueryValueAttribute("Date", &addon->date);
    el_addon->QueryValueAttribute("NextReleaseDate", &addon->next_release_date);
    el_addon->QueryValueAttribute("ReleaseNotes", &addon->release_notes);
    el_addon->QueryValueAttribute("Author", &addon->author);
    el_addon->QueryValueAttribute("Platform", &addon->platform);
    el_addon->QueryValueAttribute("Type", &addon->type);
    el_addon->QueryValueAttribute("Authorization", &addon->authorization);

    const TiXmlElement *el_dependencies= el_addon->FirstChildElement("DEPENDENCIES");
    if (el_dependencies)
    {
      const TiXmlElement *el_dependency= el_dependencies->FirstChildElement("DEPENDENCY");
      while (el_dependency)
      {
        Addon::Dependency dep;
        el_dependency->QueryValueAttribute("GUID", &dep.guid);
        el_dependency->QueryValueAttribute("MinVersion", &dep.min_ver);
        el_dependency->QueryValueAttribute("MaxVersion", &dep.max_ver);
        addon->dependencies.push_back(dep);
        el_dependency= el_dependency->NextSiblingElement();
      }
    }

    // rename the manifest file if its name doesn't comply with addon version (for example manifest was downloaded into temporary file)
    if (filepath != manifest_filepath)
      if (!File::rename(filepath, manifest_filepath))
        _log->throw_err("Failed to move file from " + File(filepath).quoted_path() + " to " + File(manifest_filepath).quoted_path() + ".");
  }

  // set addon name as last step of parsing single manifest file
  // taking into account that:
  // 1) addon validity is determined by non-empty guid, version & name
  // 2) there can be exception thrown during parsing of manifest file
  addon->name= addon_name;

  if (process_dependencies)
  {
    // recursively fetch manifests for all dependencies
    for (Addon::Dependencies::const_iterator i= addon->dependencies.begin(), i_end= addon->dependencies.end(); i != i_end; ++i)
      load(i->guid, i->min_ver, i->max_ver, lookup_cached_addons);
  }

  _log->push_info(addon->full_name());

  return addon;
}
