/* -*- indent-tabs-mode: nil; -*-
 *
 * This file is part of the Mango recipe management distribution.
 * Copyright (c) 2000-2003, Roland Jesse
 * All rights reserved.
 *
 * For license and copyright information see the file COPYRIGHT or 
 * <http://www.opensource.org/licenses/bsd-license.html>.
 *
 * $Id: AmbrosiaFingerprint.cpp,v 1.3 2003/05/11 09:56:41 rjesse Exp $
 */

#include "AmbrosiaFingerprint.h"
#include "Recipe.h"
#include <qdir.h>
#include <qsettings.h>
#include <stdlib.h>             // random()
#ifdef Q_OS_WIN32
# include <time.h>         // time()
#endif

/*!
  Constructs an empty fingerprint object. Please initialise it before
  use.
  \sa init()
*/
AmbrosiaFingerprint::
AmbrosiaFingerprint()
   : m_part1(0), m_part2(0)
{
}

/*!
  Constructs a fingerprint object based on the information stored
  in \a recipe.
*/
AmbrosiaFingerprint::
AmbrosiaFingerprint( const Recipe& recipe)
   : m_part1(0), m_part2(0)
{
   calculateFP( recipe);
}

/*!
  Constructs a fingerprint object based on the information stored
  in the fingerprint string \a str.
*/
AmbrosiaFingerprint::
AmbrosiaFingerprint(const QString& str)
   : m_part1(0), m_part2(0)
{
   *this = str;
}

AmbrosiaFingerprint
AmbrosiaFingerprint::operator=( const AmbrosiaFingerprint& right)
{
   m_part1 = right.part1();
   m_part2 = right.part2();
   return *this;
}

AmbrosiaFingerprint 
AmbrosiaFingerprint::operator=( const QString& str)
{
   extractFP( str);
   return *this;
}

Q_INT32 
AmbrosiaFingerprint::part1() const
{
   return m_part1;
}

Q_INT32
AmbrosiaFingerprint::part2() const
{
   return m_part2;
}

/*!
  Calculates all fingerprint data based on the information stored
  in \a recipe.

  For the first 32 bit part of the fingerprint, the recipe's creation
  date is used if possible. If that is not available, the current date
  is used. As a recipe has no associated creating time, the current
  time is used.

  For the second 32 bit part, a counter and user name are used. The
  name is taken from the recipe's original author. If that is not
  available, "Mango" is used. As case for the name does not matter,
  all letters are uppercased. Downcasing would be better because of
  umlauts et cetera, but at least Ambrosia and Kalorio seem to
  uppercase. All letters of the name are summed up whereby the
  character value at each position is multiplied by the position.
*/
AmbrosiaFingerprint& AmbrosiaFingerprint::
calculateFP( const Recipe& recipe)
{
   m_part1 = m_part2 = 0;

   const QDate created( recipe.created().date());
   const QTime curTime( QTime::currentTime());
   m_part1 = calculatePart1( created, curTime);
   m_part2 = calculatePart2( recipe.typist());

   return *this;
}

/*!
  Extracts the fingerprint information from \a str.
  Useful for import of files that come with an Ambrosia ID.

  The string consists of both 32 bit values, comma separated.
*/
void AmbrosiaFingerprint::
extractFP( const QString& str)
{
   int comIdx = str.find( ',');
   QString s1 = str.left( comIdx);
   m_part1 = s1.toLong();
   int com2Idx = str.find( ',', comIdx);
   QString s2 = str.mid( comIdx + 1, com2Idx - comIdx);
   m_part2 = s1.toLong();
}

/*!
  Updates the fingerprint.

  This only touches the date bits. Serial-ID and creator-ID are left as
  before. The default date (31.12.1899) and time (00:00) are used as
  specified by Ambrosia.

  \param d the new date.
  \param t the new time.
*/
void AmbrosiaFingerprint::
update( const QDate& d, const QTime& t)
{
   m_part1 = calculatePart1( d, t);
}

/*!
  Calculates the first 32 bit part of the id.
*/
Q_INT32 AmbrosiaFingerprint::
calculatePart1( const QDate& date, const QTime& time) const
{
   Q_INT32 ret;
   QDate d = date;
   if (! d.isValid())
      d = QDate::currentDate();
                
   ret = d.year() - 1980;
   ret = (ret << 4) | d.month();
   ret = (ret << 5) | d.day();
   ret = (ret << 5) | time.hour();
   ret = (ret << 6) | time.minute();

   return ret;
}

/*!
  Calculates the second 32 bit part of the id.
*/
Q_INT32 AmbrosiaFingerprint::
calculatePart2( const QString& name) const
{
   Q_INT32 ret;

   QString s( name);
   if (s.isEmpty()) s = "Mango";
   s = s.upper();
   for (uint i = 0; i < s.length(); i++)
      ret += s.at( i).latin1() * (i+1);
   ret &= 0xFFF;

   Q_INT32 counter = getNextCounter();
   ret = (ret << 20) | counter;
   
   return ret;
}

/*!
  The fingerprint uses a serial-ID (counter) which is incremented each
  time a new fingerprint is calculated. This function takes care of
  initialising this counter as well as incrementing it as needed.
  
  \return the next available counter.
*/
Q_INT32 AmbrosiaFingerprint::
getNextCounter() const
{
   Q_INT32 ret = 0;

   QSettings settings;
   QDir home = QDir::home();
   QString path = home.path();
   path.append( "/.mango");
#ifdef Q_OS_UNIX
   if (! home.exists( path))
	  home.mkdir( path);
#endif
   settings.insertSearchPath( QSettings::Unix, path);
   // No search path for Windows as default Registry path is fine.
   ret = settings.readNumEntry( "/Mango/IDCounter", 0);

   if (0 == ret) {              // initialise
#ifdef Q_OS_WIN32
      srand( (unsigned) time( 0));
      ret = rand() * 10000;
#else
      ret = random() * 10000;
#endif
   }
   ret = (ret + 1) & 0xFFFFF;   // increment
   settings.writeEntry( "/Mango/IDCounter", ret);
   return ret;
}

/*!
  \return string representation of the ID.
  The string consists of both 32 bit parts, comma separated, followed
  by the name of the creating application, i.e. "Mango".
*/
QString AmbrosiaFingerprint::
asString() const
{
   QString ret;
   ret.append( QString::number( m_part1));
   ret.append( ",");
   ret.append( QString::number( m_part2));
   ret.append( ", Mango");
   return ret;
}
