/* 
 *
 * $Id: k3bversion.cpp 619556 2007-01-03 17:38:12Z trueg $
 * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
 *
 * This file is part of the K3b project.
 * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * See the file "COPYING" for the exact licensing terms.
 */

#include "k3bversion.h"

#include <tqregexp.h>
#include <kdebug.h>


K3bVersion::K3bVersion()
  : m_majorVersion( -1 ),
    m_minorVersion( -1 ),
    m_patchLevel( -1 )
{
}

K3bVersion::K3bVersion( const K3bVersion& v )
  : m_versionString( v.versionString() ),
    m_majorVersion( v.majorVersion() ),
    m_minorVersion( v.minorVersion() ),
    m_patchLevel( v.patchLevel() ),
    m_suffix( v.suffix() )
{
}

K3bVersion::K3bVersion( const TQString& version )
{
  setVersion( version );
}

K3bVersion::K3bVersion( int majorVersion, 
			int minorVersion, 
			int patchlevel, 
			const TQString& suffix )
{
  setVersion( majorVersion, minorVersion, patchlevel, suffix );
}

void K3bVersion::setVersion( const TQString& v )
{
  TQString suffix;
  splitVersionString( v.stripWhiteSpace(), m_majorVersion, suffix );
  if( m_majorVersion >= 0 ) {
    if( suffix.startsWith(".") ) {
      suffix = suffix.mid( 1 );
      splitVersionString( suffix, m_minorVersion, suffix );
      if( m_minorVersion < 0 ) {
	kdDebug() << "(K3bVersion) suffix must not start with a dot!" << endl;
	m_majorVersion = -1;
	m_minorVersion = -1;
	m_patchLevel = -1;
	m_suffix = "";
      }
      else {
	if( suffix.startsWith(".") ) {
	  suffix = suffix.mid( 1 );
	  splitVersionString( suffix, m_patchLevel, suffix );
	  if( m_patchLevel < 0 ) {
	    kdDebug() << "(K3bVersion) suffix must not start with a dot!" << endl;
	    m_majorVersion = -1;
	    m_minorVersion = -1;
	    m_patchLevel = -1;
	    m_suffix = "";
	  }
	  else {
	    m_suffix = suffix;
	  }
	}
	else {
	  m_patchLevel = -1;
	  m_suffix = suffix;
	}
      }
    }
    else {
      m_minorVersion = -1;
      m_patchLevel = -1;
      m_suffix = suffix;
    }
  }

  m_versionString = createVersionString( m_majorVersion, m_minorVersion, m_patchLevel, m_suffix );
}


// splits the leading number from s and puts it in num
// the dot is removed and the rest put in suffix
// if s does not start with a digit or the first non-digit char is not a dot
// suffix = s and num = -1 is returned
void K3bVersion::splitVersionString( const TQString& s, int& num, TQString& suffix )
{
  int pos = s.find( TQRegExp("\\D") );
  if( pos < 0 ) {
    num = s.toInt();
    suffix = "";
  }
  else if( pos == 0 ) {
    num = -1;
    suffix = s;
  }
  else {
    num = s.left( pos ).toInt();
    suffix = s.mid( pos );
  }
}


bool K3bVersion::isValid() const
{
  return (m_majorVersion >= 0);
}


void K3bVersion::setVersion( int majorVersion, 
			     int minorVersion, 
			     int patchlevel, 
			     const TQString& suffix )
{
  m_majorVersion = majorVersion;
  m_minorVersion = minorVersion;
  m_patchLevel = patchlevel;
  m_suffix = suffix;
  m_versionString = createVersionString( majorVersion, minorVersion, patchlevel, suffix );
}

K3bVersion& K3bVersion::operator=( const TQString& v )
{
  setVersion( v );
  return *this;
}

K3bVersion K3bVersion::simplify() const
{
  K3bVersion v( *this );
  v.m_suffix.truncate(0);
  return v;
}

TQString K3bVersion::createVersionString( int majorVersion, 
					 int minorVersion, 
					 int patchlevel, 
					 const TQString& suffix )
{
  if( majorVersion >= 0 ) {
    TQString s = TQString::number(majorVersion);
    
    if( minorVersion > -1 ) {
      s.append( TQString(".%1").arg(minorVersion) );
      if( patchlevel > -1 )
	s.append( TQString(".%1").arg(patchlevel) );
    }
    
    if( !suffix.isNull() )
      s.append( suffix );

    return s;
  }
  else
    return "";
}


int K3bVersion::compareSuffix( const TQString& suffix1, const TQString& suffix2 )
{
  static TQRegExp rcRx( "rc(\\d+)" );
  static TQRegExp preRx( "pre(\\d+)" );
  static TQRegExp betaRx( "beta(\\d+)" );
  static TQRegExp alphaRx( "a(?:lpha)?(\\d+)" );

  // first we check if one of the suffixes (or both are empty) becasue that case if simple
  if( suffix1.isEmpty() ) {
    if( suffix2.isEmpty() )
      return 0;
    else
      return 1; // empty greater than the non-empty (should we treat something like 1.0a as greater than 1.0?)
  }
  else if( suffix2.isEmpty() )
    return -1;

  // now search for our special suffixes
  if( rcRx.exactMatch( suffix1 ) ) {
    int v1 = rcRx.cap(1).toInt();

    if( rcRx.exactMatch( suffix2 ) ) {
      int v2 = rcRx.cap(1).toInt();
      return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
    }
    else if( preRx.exactMatch( suffix2 ) ||
	     betaRx.exactMatch( suffix2 ) ||
	     alphaRx.exactMatch( suffix2 ) )
      return 1; // rc > than all the others
    else
      return TQString::compare( suffix1, suffix2 );
  }

  else if( preRx.exactMatch( suffix1 ) ) {
    int v1 = preRx.cap(1).toInt();

    if( rcRx.exactMatch( suffix2 ) ) {
      return -1; // pre is less than rc
    }
    else if( preRx.exactMatch( suffix2 ) ) {
      int v2 = preRx.cap(1).toInt();
      return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
    }
    else if( betaRx.exactMatch( suffix2 ) ||
	     alphaRx.exactMatch( suffix2 ) )
      return 1; // pre is greater than beta or alpha
    else
      return TQString::compare( suffix1, suffix2 );
  }

  else if( betaRx.exactMatch( suffix1 ) ) {
    int v1 = betaRx.cap(1).toInt();

    if( rcRx.exactMatch( suffix2 ) ||
	preRx.exactMatch( suffix2 ) )
      return -1; // beta is less than rc or pre
    else if( betaRx.exactMatch( suffix2 ) ) {
      int v2 = betaRx.cap(1).toInt();
      return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
    }
    else if( alphaRx.exactMatch( suffix2 ) )
      return 1; // beta is greater then alpha
    else
      return TQString::compare( suffix1, suffix2 );
  }

  else if( alphaRx.exactMatch( suffix1 ) ) {
    int v1 = alphaRx.cap(1).toInt();

    if( rcRx.exactMatch( suffix2 ) ||
	preRx.exactMatch( suffix2 ) ||
	betaRx.exactMatch( suffix2 ) )
      return -1; // alpha is less than all the others
    else if( alphaRx.exactMatch( suffix2 ) ) {
      int v2 = alphaRx.cap(1).toInt();
      return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
    }
    else
      return TQString::compare( suffix1, suffix2 );
  }

  else
    return TQString::compare( suffix1, suffix2 );
}


bool operator<( const K3bVersion& v1, const K3bVersion& v2 )
{
  // both version objects need to be valid

  if( v1.majorVersion() == v2.majorVersion() ) {

    // 1 == 1.0
    if( ( v1.minorVersion() == v2.minorVersion() )
	||
	( v1.minorVersion() == -1 && v2.minorVersion() == 0 )
	||
	( v2.minorVersion() == -1 && v1.minorVersion() == 0 ) 
	)
      {
	// 1.0 == 1.0.0
	if( ( v1.patchLevel() == v2.patchLevel() )
	    ||
	    ( v1.patchLevel() == -1 && v2.patchLevel() == 0 )
	    ||
	    ( v2.patchLevel() == -1 && v1.patchLevel() == 0 )
	    )
	  {
	    return K3bVersion::compareSuffix( v1.suffix(), v2.suffix() ) < 0;
	  }
	else
	  return ( v1.patchLevel() < v2.patchLevel() );
      }
    else
      return ( v1.minorVersion() < v2.minorVersion() );
  }
  else 
    return ( v1.majorVersion() < v2.majorVersion() );
}

bool operator>( const K3bVersion& v1, const K3bVersion& v2 )
{
  return operator<( v2, v1 );
}


bool operator==( const K3bVersion& v1, const K3bVersion& v2 )
{
  return ( v1.majorVersion() == v2.majorVersion() &&
	   v1.minorVersion() == v2.minorVersion() &&
	   v1.patchLevel() == v2.patchLevel() &&
	   K3bVersion::compareSuffix( v1.suffix(), v2.suffix() ) == 0 );
}


bool operator<=( const K3bVersion& v1, const K3bVersion& v2 )
{
  return ( operator<( v1, v2 ) || operator==( v1, v2 ) );
}

bool operator>=( const K3bVersion& v1, const K3bVersion& v2 )
{
  return ( operator>( v1, v2 ) || operator==( v1, v2 ) );
}
