/* This file is part of the KDE project
   Copyright (C) 2002, Dirk Schönberger <dirk.schoenberger@sz-online.de>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include "aielement.h"
#include <tqglobal.h>

AIElement::Private::Private()
{
    typ = AIElement::Invalid;
}

AIElement::Private::Private( Private* d )
{
  switch( d->typ )
	{
		case AIElement::Invalid:
	    break;
		case AIElement::String:
		case AIElement::Reference:
		case AIElement::Operator:
	    value.ptr = new TQString( *((TQString*)d->value.ptr) );
	    break;
		case AIElement::CString:
	    // TQCString is explicit shared
	    value.ptr = new TQCString( *((TQCString*)d->value.ptr) );
	    break;
/*		case AIElement::List:
	    value.ptr = new TQValueList<AIElement>( *((TQValueList<AIElement>*)d->value.ptr) );
	    break; */
		case AIElement::ElementArray:
	    value.ptr = new TQValueVector<AIElement>( *((TQValueVector<AIElement>*)d->value.ptr) );
	    break;
		case AIElement::Block:
	    value.ptr = new TQValueVector<AIElement>( *((TQValueVector<AIElement>*)d->value.ptr) );
	    break;
		case AIElement::ByteArray:
	    value.ptr = new TQByteArray( *((TQByteArray*)d->value.ptr) );
	    break;
		case AIElement::Int:
	    value.i = d->value.i;
	    break;
		case AIElement::UInt:
	    value.u = d->value.u;
	    break;
		case AIElement::Double:
	    value.d = d->value.d;
	    break;
		case AIElement::Byte:
	    value.b = d->value.b;
	    break;
		default:
	    Q_ASSERT( 0 );
	}

  typ = d->typ;
}

AIElement::Private::~Private()
{
    clear();
}

void AIElement::Private::clear()
{
  switch( typ )
	{
		case AIElement::String:
		case AIElement::Operator:
		case AIElement::Reference:
	    delete (TQString*)value.ptr;
	    break;
		case AIElement::CString:
	    delete (TQCString*)value.ptr;
	    break;
/*		case AIElement::List:
	    delete (TQValueList<AIElement>*)value.ptr;
	    break; */
		case AIElement::ElementArray:
	    delete (TQValueVector<AIElement>*)value.ptr;
	    break;
		case AIElement::Block:
	    delete (TQValueVector<AIElement>*)value.ptr;
	    break;
		case AIElement::ByteArray:
	    delete (TQByteArray*)value.ptr;
	    break;
		case AIElement::Invalid:
		case AIElement::Int:
		case AIElement::UInt:
		case AIElement::Double:
		case AIElement::Byte:
	    break;
	}

  typ = AIElement::Invalid;
}

/*!
  Constructs an invalid aielement.
*/
AIElement::AIElement()
{
    d = new Private;
}

/*!
  Destroys the AIElement and the contained object.

  Note that subclasses that reimplement clear() should reimplement
  the destructor to call clear().  This destructor calls clear(), but
  because it is the destructor, AIElement::clear() is called rather than
  a subclass's clear().
*/
AIElement::~AIElement()
{
  if ( d->deref() )
	  delete d;
}

/*!
  Constructs a copy of the aielement, \a p, passed as the argument to this
  constructor. Usually this is a deep copy, but a shallow copy is made
  if the stored data type is explicitly shared, as e.g. TQImage is.
*/
AIElement::AIElement( const AIElement& p )
{
  d = new Private;
  *this = p;
}

/*!
  Constructs a new aielement with a string value, \a val.
*/
AIElement::AIElement( const TQString& val, Type type )
{
  d = new Private;
  d->typ = type;
  d->value.ptr = new TQString( val );
}

/*!
  Constructs a new aielement with a C-string value, \a val.

  If you want to modify the TQCString after you've passed it to this
  constructor, we recommend passing a deep copy (see
  TQCString::copy()).
*/
AIElement::AIElement( const TQCString& val )
{
  d = new Private;
  d->typ = CString;
  d->value.ptr = new TQCString( val );
}

/*!
  Constructs a new aielement with a C-string value of \a val if \a val
  is non-null.  The aielement creates a deep copy of \a val.

  If \a val is null, the resulting aielement has type Invalid.
*/
AIElement::AIElement( const char* val )
{
  d = new Private;
  if ( val == 0 )
	  return;
  d->typ = CString;
  d->value.ptr = new TQCString( val );
}

/*!
  Constructs a new aielement with an integer value, \a val.
*/
AIElement::AIElement( int val )
{
  d = new Private;
  d->typ = Int;
  d->value.i = val;
}

/*!
  Constructs a new aielement with an unsigned integer value, \a val.
*/
AIElement::AIElement( uint val )
{
  d = new Private;
  d->typ = UInt;
  d->value.u = val;
}

/*!
  Constructs a new aielement with an byte value, \a val.
*/
AIElement::AIElement( uchar val )
{
  d = new Private;
  d->typ = Byte;
  d->value.b = val;
}


/*!
  Constructs a new aielement with a floating point value, \a val.
*/
AIElement::AIElement( double val )
{
  d = new Private;
  d->typ = Double;
  d->value.d = val;
}

/*!
  Constructs a new aielement with a list value, \a val.
*/
/* AIElement::AIElement( const TQValueList<AIElement>& val )
{
  d = new Private;
  d->typ = List;
  d->value.ptr = new TQValueList<AIElement>( val );
}  */

AIElement::AIElement( const TQValueVector<AIElement>& val, Type type )
{
  d = new Private;
  d->typ = type;
  d->value.ptr = new TQValueVector<AIElement>( val );
}

AIElement::AIElement( const TQByteArray& val )
{
  d = new Private;
  d->typ = ByteArray;
  d->value.ptr = new TQByteArray( val );
}

/*!
  Assigns the value of the aielement \a aielement to this aielement.

  This is a deep copy of the aielement, but note that if the aielement
  holds an explicitly shared type such as TQImage, a shallow copy
  is performed.
*/
AIElement& AIElement::operator= ( const AIElement& aielement )
{
  AIElement& other = (AIElement&)aielement;

  other.d->ref();
  if ( d->deref() )
	  delete d;

  d = other.d;

  return *this;
}

/*!
  \internal
*/
void AIElement::detach()
{
  if ( d->count == 1 )
	return;

  d->deref();
  d = new Private( d );
}

/*!
  Returns the name of the type stored in the aielement.
  The returned strings describe the C++ datatype used to store the
  data: for example, "TQFont", "TQString", or "TQValueList<AIElement>".
  An Invalid aielement returns 0.
*/
const char* AIElement::typeName() const
{
  return typeToName( d->typ );
}

/*! Convert this aielement to type Invalid and free up any resources
  used.
*/
void AIElement::clear()
{
  if ( d->count > 1 )
  {
	  d->deref();
	  d = new Private;
	  return;
  }

  d->clear();
}

static const int ntypes = 11;
static const char* const type_map[ntypes] =
{
  0,
//  "TQValueList<AIElement>",
  "TQString",
  "int",
  "uint",
  "double",
  "TQCString",
  "Operator",
  "Reference",
  "TQValueVector<AIElement>",
  "TQByteArray",
  "uchar",
};

/*!
  Converts the enum representation of the storage type, \a typ, to its
  string representation.
*/
const char* AIElement::typeToName( Type typ )
{
  if ( typ >= ntypes )
	  return 0;
  return type_map[typ];
}

/*!
  Converts the string representation of the storage type gven in \a
  name, to its enum representation.

  If the string representation cannot be converted to any enum
  representation, the aielement is set to \c Invalid.
*/
AIElement::Type AIElement::nameToType( const char* name )
{
  for ( int i = 0; i < ntypes; i++ ) {
	  if ( !qstrcmp( type_map[i], name ) )
	    return (Type) i;
  }
  return Invalid;
}

/*!
  Returns the aielement as a TQString if the aielement has type()
  String, CString, ByteArray, Int, Uint, Double,
  or TQString() otherwise.

  \sa asString()
*/
const TQString AIElement::toString() const
{
  if ( d->typ == CString )
	  return TQString::fromLatin1( toCString() );
  if ( d->typ == Int )
	  return TQString::number( toInt() );
  if ( d->typ == UInt )
	  return TQString::number( toUInt() );
  if ( d->typ == Double )
	  return TQString::number( toDouble() );
  if ( d->typ == Byte )
	  return TQString::number( toByte() );
  if ( d->typ != String )
	  return TQString();
  return *((TQString*)d->value.ptr);
}

const TQString AIElement::toReference() const
{
  if ( d->typ != Reference )
	  return TQString();
  return *((TQString*)d->value.ptr);
}

const TQString AIElement::toOperator() const
{
  if ( d->typ != Operator )
	  return TQString();
  return *((TQString*)d->value.ptr);
}

/*!
  Returns the aielement as a TQCString if the aielement has type()
  CString or String, or a 0 otherwise.

  \sa asCString()
*/
const TQCString AIElement::toCString() const
{
  if ( d->typ == CString )
	  return *((TQCString*)d->value.ptr);
  if ( d->typ == String )
	  return ((TQString*)d->value.ptr)->latin1();
  if ( d->typ == Operator )
	  return ((TQString*)d->value.ptr)->latin1();
  if ( d->typ == Reference )
	  return ((TQString*)d->value.ptr)->latin1();

  return 0;
}


/*!
  Returns the aielement as an int if the aielement has type()
  String, CString, Int, UInt, Double, Byte, or 0 otherwise.

  If \a ok is non-null, \a *ok is set to TRUE if the value could be
  converted to an int and FALSE otherwise.

  \sa asInt() canCast()
*/
int AIElement::toInt( bool * ok ) const
{
  if( d->typ == String )
	  return ((TQString*)d->value.ptr)->toInt( ok );
  if ( d->typ == CString )
	  return ((TQCString*)d->value.ptr)->toInt( ok );
  if ( ok )
	  *ok = canCast( UInt );
  if( d->typ == Int )
	  return d->value.i;
  if( d->typ == UInt )
	  return (int)d->value.u;
  if( d->typ == Byte )
	  return (int)d->value.b;
  if ( d->typ == Double )
	  return (int)d->value.d;
  return 0;
}

uchar AIElement::toByte( bool * ok ) const
{
  if( d->typ == String )
	  return ((TQString*)d->value.ptr)->toShort( ok );
  if ( d->typ == CString )
	  return ((TQCString*)d->value.ptr)->toShort( ok );
  if ( ok )
	  *ok = canCast( UInt );
  if( d->typ == Byte )
	  return d->value.b;
  if( d->typ == Int )
	  return (uchar)d->value.i;
  if( d->typ == UInt )
	  return (uchar)d->value.u;
  if ( d->typ == Double )
	  return (uchar)d->value.d;
  return 0;
}


/*!
  Returns the aielement as an unsigned int if the aielement has type()
  String, CString, UInt, Int, Double, Byte, or 0 otherwise.

  If \a ok is non-null, \a *ok is set to TRUE if the value could be
  converted to a uint and FALSE otherwise.

  \sa asUInt()
*/
uint AIElement::toUInt( bool * ok ) const
{
  if( d->typ == String )
	  return ((TQString*)d->value.ptr)->toUInt( ok );
  if ( d->typ == CString )
	  return ((TQCString*)d->value.ptr)->toUInt( ok );
  if ( ok )
	  *ok = canCast( UInt );
  if( d->typ == Int )
	  return d->value.i;
  if( d->typ == UInt )
	  return (int)d->value.u;
  if( d->typ == Byte )
	  return (int)d->value.b;
  if ( d->typ == Double )
	  return (int)d->value.d;

  return 0;
}

/*!
  Returns the aielement as a double if the aielement has type()
  String, CString, Double, Int, UInt, Byte, or 0.0 otherwise.

  If \a ok is non-null, \a *ok is set to TRUE if the value could be
  converted to a double and FALSE otherwise.

  \sa asDouble()
*/
double AIElement::toDouble( bool * ok ) const
{
  if( d->typ == String )
	  return ((TQString*)d->value.ptr)->toDouble( ok );
  if ( d->typ == CString )
	  return ((TQCString*)d->value.ptr)->toDouble( ok );
  if ( ok )
	  *ok = canCast( Double );
  if ( d->typ == Double )
	  return d->value.d;
  if ( d->typ == Int )
	  return (double)d->value.i;
  if ( d->typ == UInt )
	  return (double)d->value.u;
  if ( d->typ == Byte )
	  return (double)d->value.b;
  return 0.0;
}

/*!
  Returns the aielement as a TQValueList<AIElement> if the aielement has type()
  List or StringList, or an empty list otherwise.

  Note that if you want to iterate over the list, you should
  iterate over a copy, e.g.
    \code
    TQValueList<AIElement> list = myAIElement.toList();
    TQValueList<AIElement>::Iterator it = list.begin();
    while( it != list.end() ) {
	myProcessing( *it );
	++it;
    }
    \endcode

  \sa asList()
*/
/* const TQValueList<AIElement> AIElement::toList() const
{
  if ( d->typ == List )
	  return *((TQValueList<AIElement>*)d->value.ptr);
  return TQValueList<AIElement>();
} */

const TQValueVector<AIElement> AIElement::toElementArray() const
{
  if ( d->typ == ElementArray )
	  return *((TQValueVector<AIElement>*)d->value.ptr);
  return TQValueVector<AIElement>();
}

const TQValueVector<AIElement> AIElement::toBlock() const
{
  if ( d->typ == Block )
	  return *((TQValueVector<AIElement>*)d->value.ptr);
  return TQValueVector<AIElement>();
}


const TQByteArray AIElement::toByteArray() const
{
  if ( d->typ == ByteArray )
	  return *((TQByteArray*)d->value.ptr);
  return TQByteArray();
}

#define TQ_VARIANT_AS( f ) TQ##f& AIElement::as##f() { \
   if ( d->typ != f ) *this = AIElement( to##f() ); else detach(); return *((TQ##f*)d->value.ptr);}

TQ_VARIANT_AS(String)
TQ_VARIANT_AS(CString)

/*!
  Returns the aielement's value as int reference.
*/
int& AIElement::asInt()
{
  detach();
  if ( d->typ != Int ) {
	  int i = toInt();
	  d->clear();
 	  d->value.i = i;
	  d->typ = Int;
  }
  return d->value.i;
}

/*!
  Returns the aielement's value as unsigned int reference.
*/
uint& AIElement::asUInt()
{
  detach();
  if ( d->typ != UInt ) {
	  uint u = toUInt();
	  d->clear();
	  d->value.u = u;
	  d->typ = UInt;
  }
  return d->value.u;
}

/*!
  Returns the aielement's value as double reference.
*/
double& AIElement::asDouble()
{
  if ( d->typ != Double ) {
	  double dbl = toDouble();
	  d->clear();
	  d->value.d = dbl;
	  d->typ = Double;
  }
  return d->value.d;
}

/*!
  Returns the aielement's value as byte reference.
*/
uchar& AIElement::asByte()
{
  detach();
  if ( d->typ != Byte ) {
	  uchar b = toByte();
	  d->clear();
 	  d->value.b = b;
	  d->typ = Byte;
  }
  return d->value.b;
}


/*!
  Returns the aielement's value as aielement list reference.

  Note that if you want to iterate over the list, you should
  iterate over a copy, e.g.
    \code
    TQValueList<AIElement> list = myAIElement.asList();
    TQValueList<AIElement>::Iterator it = list.begin();
    while( it != list.end() ) {
	myProcessing( *it );
	++it;
    }
    \endcode
*/
/* TQValueList<AIElement>& AIElement::asList()
{
  if ( d->typ != List )
	  *this = AIElement( toList() );
  return *((TQValueList<AIElement>*)d->value.ptr);
}  */

TQValueVector<AIElement>& AIElement::asElementArray()
{
  if ( d->typ != ElementArray )
	  *this = AIElement( toElementArray() );
  return *((TQValueVector<AIElement>*)d->value.ptr);
}

TQValueVector<AIElement>& AIElement::asBlock()
{
  if ( d->typ != Block )
	  *this = AIElement( toBlock() );
  return *((TQValueVector<AIElement>*)d->value.ptr);
}


TQByteArray& AIElement::asByteArray()
{
  if ( d->typ != ByteArray )
	  *this = AIElement( toByteArray() );
  return *((TQByteArray*)d->value.ptr);
}

/*!
  Returns true if the aielement's type can be cast to the requested
  type, \p t. Such casting is done automatically when calling the
  toInt(), ... or asInt(), ... methods.

  The following casts are done automatically:
  <ul>
  <li> CString => String
  <li> Double => String, Int, UInt
  <li> Int => String, Double, UInt
  <li> String => CString, Int, Uint, Double
  <li> UInt => String, Double, Int
  </ul>
*/
bool AIElement::canCast( Type t ) const
{
  if ( d->typ == t )
	  return true;
  if ( t == Int && ( d->typ == String || d->typ == Double || d->typ == UInt || d->typ == Byte) )
	  return true;
  if ( t == UInt && ( d->typ == String || d->typ == Double || d->typ == Int || d->typ == Byte) )
	  return true;
  if ( t == Double && ( d->typ == String || d->typ == Int || d->typ == UInt || d->typ == Byte) )
	  return true;
  if ( t == CString && d->typ == String )
	  return true;
  if ( t == String && ( d->typ == CString || d->typ == Int || d->typ == UInt || d->typ == Double || d->typ == Byte) )
	  return true;

  return false;
}

/*!
  \brief Casts the aielement to the requested type.
  
  If the cast cannot be
  done, the aielement is set to the default value of the requested type
  (e.g. an empty string if the requested type \p t is
  AIElement::String, an empty point array if the requested type \p t is
  AIElement::PointArray, etc).

  \returns true if the current type of the
  aielement was successfully casted; otherwise returns false.

  \see canCast()
*/

bool AIElement::cast( Type t )
{
  switch ( t ) {
/*    case AIElement::List:
	    asList();
	    break; */
    case AIElement::ElementArray:
	    asElementArray();
	    break;
    case AIElement::Block:
	    asBlock();
	    break;
    case AIElement::String:
	    asString();
	    break;
    case AIElement::Int:
	    asInt();
	    break;
    case AIElement::UInt:
	    asUInt();
	    break;
    case AIElement::Double:
	    asDouble();
	    break;
    case AIElement::CString:
	    asCString();
	    break;
    case AIElement::Byte:
	    asByte();
	    break;
    case AIElement::ByteArray:
	    asByteArray();
	    break;
    default:
	    case AIElement::Invalid:
	    (*this) = AIElement();
  }
  return canCast( t );
}

/*!  Compares this AIElement with \a v and returns true if they are
  equal; otherwise returns false.
*/

bool AIElement::operator==( const AIElement &v ) const
{
  if ( !v.canCast( type() ) )
	  return false;
  switch( d->typ ) {
/*    case List:
	     return v.toList() == toList(); */
    case ElementArray:
	     return v.toElementArray() == toElementArray();
    case Block:
	     return v.toBlock() == toBlock();
    case ByteArray:
	     return v.toByteArray() == toByteArray();

    case String:
	     return v.toString() == toString();
    case Operator:
	     return v.toOperator() == toOperator();
    case Reference:
	     return v.toReference() == toReference();
    case CString:
	     return v.toCString() == toCString();
    case Int:
	     return v.toInt() == toInt();
    case UInt:
	     return v.toUInt() == toUInt();
    case Byte:
	     return v.toByte() == toByte();
    case Invalid:
	     break;
    }
    return false;
}

/*!  Compares this AIElement with \a v and returns true if they are
  not equal; otherwise returns false.
*/

bool AIElement::operator!=( const AIElement &v ) const
{
    return !( v == *this );
}
