#include "db.hh"
#include "hash.hh"
#include "util.hh"
#include "local-store.hh"
#include "globals.hh"
#include "pathlocks.hh"
#include "config.h"

#include <iostream>


namespace nix {


Hash parseHashField(const Path & path, const string & s);


/* Upgrade from schema 4 (Nix 0.11) to schema 5 (Nix >= 0.12).  The
   old schema uses Berkeley DB, the new one stores store path
   meta-information in files. */
void LocalStore::upgradeStore12()
{
#if OLD_DB_COMPAT
    
#ifdef __CYGWIN__
    /* Cygwin can't upgrade a read lock to a write lock... */
    lockFile(globalLock, ltNone, true);
#endif

    if (!lockFile(globalLock, ltWrite, false)) {
        printMsg(lvlError, "waiting for exclusive access to the Nix store...");
        lockFile(globalLock, ltWrite, true);
    }

    printMsg(lvlError, "upgrading Nix store to new schema (this may take a while)...");

    if (getSchema() >= nixSchemaVersion) return; /* somebody else beat us to it */

    /* Open the old Nix database and tables. */
    Database nixDB;
    nixDB.open(nixDBPath);
    
    /* dbValidPaths :: Path -> ()

       The existence of a key $p$ indicates that path $p$ is valid
       (that is, produced by a succesful build). */
    TableId dbValidPaths = nixDB.openTable("validpaths");

    /* dbReferences :: Path -> [Path]

       This table lists the outgoing file system references for each
       output path that has been built by a Nix derivation.  These are
       found by scanning the path for the hash components of input
       paths. */
    TableId dbReferences = nixDB.openTable("references");

    /* dbReferrers :: Path -> Path

       This table is just the reverse mapping of dbReferences.  This
       table can have duplicate keys, each corresponding value
       denoting a single referrer. */
    // Not needed for conversion: it's just the inverse of
    // references.
    // TableId dbReferrers = nixDB.openTable("referrers");

    /* dbDerivers :: Path -> [Path]

       This table lists the derivation used to build a path.  There
       can only be multiple such paths for fixed-output derivations
       (i.e., derivations specifying an expected hash). */
    TableId dbDerivers = nixDB.openTable("derivers");

    Paths paths;
    nixDB.enumTable(noTxn, dbValidPaths, paths);
    
    for (Paths::iterator i = paths.begin(); i != paths.end(); ++i) {
        ValidPathInfo info;
        info.path = *i;
        
        Paths references;
        nixDB.queryStrings(noTxn, dbReferences, *i, references);
        info.references.insert(references.begin(), references.end());
        
        string s;
        nixDB.queryString(noTxn, dbValidPaths, *i, s);
        info.hash = parseHashField(*i, s);
        
        nixDB.queryString(noTxn, dbDerivers, *i, info.deriver);
        
        registerValidPath(info, true);
        std::cerr << ".";
    }

    std::cerr << std::endl;

    writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());

    lockFile(globalLock, ltRead, true);

#else
    throw Error(
        "Your Nix store has a database in Berkeley DB format. To convert\n"
        "to the new format, please compile Nix with Berkeley DB support.");
#endif
}


}