/***************************************************************************
 * rubyvariant.h
 * This file is part of the KDE project
 * copyright (C)2005 by Cyrille Berger (cberger@cberger.net)
 * copyright (C)2006 by Sebastian Sauer (mail@dipe.org)
 *
 * This program 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 program 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 program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 ***************************************************************************/

#ifndef KROSS_RUBYVARIANT_H
#define KROSS_RUBYVARIANT_H

#define HAVE_STRLCAT_PROTO 1
#define HAVE_STRLCPY_PROTO 1
#include "config.h"

#include <ruby.h>

#ifndef HAVE_RUBY_1_9
#include <st.h>
#else // HAVE_RUBY_1_9
#include <ruby/st.h>
#define STR2CSTR(x) StringValuePtr(x)
#endif // HAVE_RUBY_1_9
//#include <typeinfo>

#include "rubyconfig.h"

#include <tqstring.h>
#include <tqstringlist.h>
#include <tqvariant.h>
#include <tqsize.h>
#include <tqpoint.h>
#include <tqrect.h>
#include <tqurl.h>
#include <tqdatetime.h>

namespace Kross {

    /**
     * The RubyType helper classes used to cast between TQVariant
     * and VALUE values.
     *
     * Following TQVariant::Type's are implemented;
     *   \li TQVariant::Invalid
     *   \li TQVariant::Int
     *   \li TQVariant::UInt
     *   \li TQVariant::Double
     *   \li TQVariant::Bool
     *   \li TQVariant::LongLong
     *   \li TQVariant::ULongLong
     *   \li TQVariant::ByteArray
     *   \li TQVariant::String
     *   \li TQVariant::StringList
     *   \li TQVariant::Size
     *   \li TQVariant::SizeF
     *   \li TQVariant::Point
     *   \li TQVariant::PointF
     *   \li TQVariant::Rect
     *   \li TQVariant::RectF
     *   \li TQVariant::Url
     *   \li TQVariant::List
     *   \li TQVariant::Map
     *
     * Following TQVariant::Type's are unimplemented yet (do we need them anyways?);
     *   \li TQVariant::BitArray
     *   \li TQVariant::Date
     *   \li TQVariant::Time
     *   \li TQVariant::DateTime
     *   \li TQVariant::Bitmap
     *   \li TQVariant::Brush
     *   \li TQVariant::Char
     *   \li TQVariant::Color
     *   \li TQVariant::Cursor
     *   \li TQVariant::Font
     *   \li TQVariant::Icon
     *   \li TQVariant::Image
     *   \li TQVariant::KeySequence
     *   \li TQVariant::Line
     *   \li TQVariant::LineF
     *   \li TQVariant::Locale
     *   \li TQVariant::Palette
     *   \li TQVariant::Pen
     *   \li TQVariant::Pixmap
     *   \li TQVariant::PointArray
     *   \li TQVariant::Polygon
     *   \li TQVariant::RegExp
     *   \li TQVariant::Region
     *   \li TQVariant::SizePolicy
     *   \li TQVariant::TextFormat
     *   \li TQVariant::TextLength
     */
    template<typename VARIANTTYPE, typename RBTYPE = VALUE>
    struct RubyType
    {
        // template-specialisations need to implement following both static
        // functions to translate between TQVariant and Ruby's VALUE values.

        //inline static RBTYPE toVALUE(const VARIANTTYPE&) { return Py::None(); }
        //inline static TQVARIANTTYPE toVariant(const VARIANTTYPE&) { return TQVariant(); }
    };

    /// \internal
    template<>
    struct RubyType<TQVariant>
    {
        static VALUE toVALUE(const TQVariant& v);
        static TQVariant toVariant(VALUE value);
    };

    /// \internal
    template<>
    struct RubyType<int>
    {
        inline static VALUE toVALUE(int i) {
            return INT2FIX(i);
        }
        inline static int toVariant(VALUE value) {
            switch( TYPE(value) ) {
                case T_FIXNUM:
                    return FIX2INT(value);
                case T_BIGNUM:
                    return rb_big2int(value);
                case T_FLOAT:
#ifdef HAVE_RUBY_1_9
                    return (int)(RFLOAT_VALUE(value));
#else
                    return (int)(RFLOAT(value)->value);
#endif
                default:
                    break;
            }
            rb_raise(rb_eTypeError, "Integer must be a fixed number");
            return 0;
        }
    };

    /// \internal
    template<>
    struct RubyType<uint>
    {
        inline static VALUE toVALUE(uint i) {
            return UINT2NUM(i);
        }
        inline static uint toVariant(VALUE value) {
            switch( TYPE(value) ) {
                case T_FIXNUM:
                    return FIX2UINT(value);
                case T_BIGNUM:
                    return rb_big2uint(value);
                case T_FLOAT:
#ifdef HAVE_RUBY_1_9
                    return (uint)(RFLOAT_VALUE(value));
#else
                    return (uint)(RFLOAT(value)->value);
#endif
                default:
                    break;
            }
            rb_raise(rb_eTypeError, "Unsigned integer must be a fixed number");
            return 0;
        }
    };

    /// \internal
    template<>
    struct RubyType<double>
    {
        inline static VALUE toVALUE(double d) {
            return rb_float_new(d);
        }
        inline static double toVariant(VALUE value) {
            return NUM2DBL(value);
        }
    };

    /// \internal
    template<>
    struct RubyType<bool>
    {
        inline static VALUE toVALUE(bool b) {
            return b ? TRUE : FALSE;
        }
        inline static bool toVariant(VALUE value) {
            switch( TYPE(value) ) {
                case T_TRUE:
                    return true;
                case T_FALSE:
                    return false;
                default: {
                    rb_raise(rb_eTypeError, "Boolean value expected");
                    return false;
                } break;
            }
        }
    };

    /// \internal
    template<>
    struct RubyType<TQ_LLONG>
    {
        inline static VALUE toVALUE(TQ_LLONG l) {
            return /*INT2NUM*/ LONG2NUM((long)l);
        }
        inline static TQ_LLONG toVariant(VALUE value) {
            return NUM2LONG(value);
        }
    };

    /// \internal
    template<>
    struct RubyType<TQ_ULLONG>
    {
        inline static VALUE toVALUE(TQ_ULLONG l) {
            return UINT2NUM((unsigned long)l);
        }
        inline static TQ_ULLONG toVariant(VALUE value) {
            return NUM2UINT(value);
        }
    };

    /// \internal
    template<>
    struct RubyType<TQByteArray>
    {
        inline static VALUE toVALUE(const TQByteArray& ba) {
            return rb_str_new(ba.data(), ba.size());
        }
        inline static TQByteArray toVariant(VALUE value) {
            if( TYPE(value) != T_STRING ) {
                rb_raise(rb_eTypeError, "TQByteArray must be a string");
                //return STR2CSTR( rb_inspect(value) );
                return TQCString("");
            }
#ifdef HAVE_RUBY_1_9
            long length = LONG2NUM( RSTRING_LEN(value) );
#else
            long length = LONG2NUM( RSTRING(value)->len );
#endif
            if( length < 0 )
                return TQCString("");
#ifdef HAVE_RUBY_1_9
            return TQCString(RSTRING_PTR(value), RSTRING_LEN(value));
#else // HAVE_RUBY_1_9
            char* ca = rb_str2cstr(value, &length);
            return TQCString(ca, length);
#endif // HAVE_RUBY_1_9
        }
    };

    /// \internal
    template<>
    struct RubyType<TQString>
    {
        inline static VALUE toVALUE(const TQString& s) {
            return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1());
        }
        inline static TQString toVariant(VALUE value) {
            if( TYPE(value) != T_STRING ) {
                rb_raise(rb_eTypeError, "TQString must be a string");
                return TQString();
            }
            return STR2CSTR(value);
        }
    };

    /// \internal
    template<>
    struct RubyType<TQSize>
    {
        inline static VALUE toVALUE(const TQSize& s) {
            VALUE l = rb_ary_new();
            rb_ary_push(l, RubyType<int>::toVALUE(s.width()));
            rb_ary_push(l, RubyType<int>::toVALUE(s.height()));
            return l;
        }
        inline static TQSize toVariant(VALUE value) {
#ifdef HAVE_RUBY_1_9
            if( TYPE(value) != T_ARRAY || RARRAY_LEN(value) != 2 ) {
#else
            if( TYPE(value) != T_ARRAY || RARRAY(value)->len != 2 ) {
#endif
                rb_raise(rb_eTypeError, "TQSize must be an array with 2 elements");
                return TQSize();
            }
            return TQSize( RubyType<int>::toVariant( rb_ary_entry(value,0) ), RubyType<int>::toVariant( rb_ary_entry(value,1) ) );
        }
    };

#if 0
    /// \internal
    template<>
    struct RubyType<TQSizeF>
    {
        inline static VALUE toVALUE(const TQSizeF& s) {
            VALUE l = rb_ary_new();
            rb_ary_push(l, RubyType<double>::toVALUE(s.width()));
            rb_ary_push(l, RubyType<double>::toVALUE(s.height()));
            return l;
        }
        inline static TQSizeF toVariant(VALUE value) {
#ifdef HAVE_RUBY_1_9
            if( TYPE(value) != T_ARRAY || RARRAY_LEN(value) != 2 ) {
#else
            if( TYPE(value) != T_ARRAY || RARRAY(value)->len != 2 ) {
#endif
                rb_raise(rb_eTypeError, "TQSizeF must be an array with 2 elements");
                return TQSizeF();
            }
            return TQSizeF( RubyType<double>::toVariant( rb_ary_entry(value,0) ), RubyType<double>::toVariant( rb_ary_entry(value,1) ) );

        }
    };
#endif

    /// \internal
    template<>
    struct RubyType<TQPoint>
    {
        inline static VALUE toVALUE(const TQPoint& s) {
            VALUE l = rb_ary_new();
            rb_ary_push(l, RubyType<int>::toVALUE(s.x()));
            rb_ary_push(l, RubyType<int>::toVALUE(s.y()));
            return l;
        }
        inline static TQPoint toVariant(VALUE value) {
#ifdef HAVE_RUBY_1_9
            if( TYPE(value) != T_ARRAY || RARRAY_LEN(value) != 2 ) {
#else
            if( TYPE(value) != T_ARRAY || RARRAY(value)->len != 2 ) {
#endif
                rb_raise(rb_eTypeError, "TQPoint must be an array with 2 elements");
                return TQPoint();
            }
            return TQPoint( RubyType<int>::toVariant( rb_ary_entry(value,0) ), RubyType<int>::toVariant( rb_ary_entry(value,1) ) );
        }
    };

#if 0
    /// \internal
    template<>
    struct RubyType<TQPointF>
    {
        inline static VALUE toVALUE(const TQPointF& s) {
            VALUE l = rb_ary_new();
            rb_ary_push(l, RubyType<double>::toVALUE(s.x()));
            rb_ary_push(l, RubyType<double>::toVALUE(s.y()));
            return l;
        }
        inline static TQPointF toVariant(VALUE value) {
#ifdef HAVE_RUBY_1_9
            if( TYPE(value) != T_ARRAY || RARRAY_LEN(value) != 2 ) {
#else
            if( TYPE(value) != T_ARRAY || RARRAY(value)->len != 2 ) {
#endif
                rb_raise(rb_eTypeError, "TQPointF must be an array with 2 elements");
                return TQPointF();
            }
            return TQPointF( RubyType<double>::toVariant( rb_ary_entry(value,0) ), RubyType<double>::toVariant( rb_ary_entry(value,1) ) );
        }
    };
#endif

    /// \internal
    template<>
    struct RubyType<TQRect>
    {
        inline static VALUE toVALUE(const TQRect& s) {
            VALUE l = rb_ary_new();
            rb_ary_push(l, RubyType<int>::toVALUE(s.x()));
            rb_ary_push(l, RubyType<int>::toVALUE(s.y()));
            rb_ary_push(l, RubyType<int>::toVALUE(s.width()));
            rb_ary_push(l, RubyType<int>::toVALUE(s.height()));
            return l;
        }
        inline static TQRect toVariant(VALUE value) {
#ifdef HAVE_RUBY_1_9
            if( TYPE(value) != T_ARRAY || RARRAY_LEN(value) != 4 ) {
#else
            if( TYPE(value) != T_ARRAY || RARRAY(value)->len != 4 ) {
#endif
                rb_raise(rb_eTypeError, "TQRect must be an array with 4 elements");
                return TQRect();
            }
            return TQRect( RubyType<int>::toVariant( rb_ary_entry(value,0) ), RubyType<int>::toVariant( rb_ary_entry(value,1) ),
                           RubyType<int>::toVariant( rb_ary_entry(value,2) ), RubyType<int>::toVariant( rb_ary_entry(value,3) ) );
        }
    };

#if 0
    /// \internal
    template<>
    struct RubyType<TQRectF>
    {
        inline static VALUE toVALUE(const TQRectF& s) {
            VALUE l = rb_ary_new();
            rb_ary_push(l, RubyType<double>::toVALUE(s.x()));
            rb_ary_push(l, RubyType<double>::toVALUE(s.y()));
            rb_ary_push(l, RubyType<double>::toVALUE(s.width()));
            rb_ary_push(l, RubyType<double>::toVALUE(s.height()));
            return l;
        }
        inline static TQRectF toVariant(VALUE value) {
#ifdef HAVE_RUBY_1_9
            if( TYPE(value) != T_ARRAY || RARRAY_LEN(value) != 4 ) {
#else
            if( TYPE(value) != T_ARRAY || RARRAY(value)->len != 4 ) {
#endif
                rb_raise(rb_eTypeError, "TQRectF must be an array with 4 elements");
                return TQRectF();
            }
            return TQRectF( RubyType<double>::toVariant( rb_ary_entry(value,0) ), RubyType<double>::toVariant( rb_ary_entry(value,1) ),
                           RubyType<double>::toVariant( rb_ary_entry(value,2) ), RubyType<double>::toVariant( rb_ary_entry(value,3) ) );
        }
    };
#endif

    /// \internal
    template<>
    struct RubyType<TQUrl>
    {
        inline static VALUE toVALUE(const TQUrl& url) {
            return RubyType<TQString>::toVALUE( url.toString() );
        }
        inline static TQUrl toVariant(VALUE value) {
            return TQUrl( RubyType<TQString>::toVariant(value) );
        }
    };

    /// \internal
    template<>
    struct RubyType<TQStringList>
    {
        inline static VALUE toVALUE(const TQStringList& list) {
            VALUE l = rb_ary_new();
            for (TQStringList::const_iterator it = list.begin(); it != list.end(); ++it) {
                rb_ary_push(l, RubyType<TQString>::toVALUE(*it));
            }
            return l;
        }
        inline static TQStringList toVariant(VALUE value) {
            if( TYPE(value) != T_ARRAY ) {
                rb_raise(rb_eTypeError, "TQStringList must be an array");
                return TQStringList();
            }
            TQStringList l;
#ifdef HAVE_RUBY_1_9
            for(int i = 0; i < RARRAY_LEN(value); i++)
#else
            for(int i = 0; i < RARRAY(value)->len; i++)
#endif
                l.append( RubyType<TQString>::toVariant( rb_ary_entry(value, i) ) );
            return l;
        }
    };

#if 0
    /// \internal
    template<>
    struct RubyType<TQVariantList>
    {
        inline static VALUE toVALUE(const TQVariantList& list) {
            VALUE l = rb_ary_new();
            foreach(TQVariant v, list)
                rb_ary_push(l, RubyType<TQVariant>::toVALUE(v));
            return l;
        }
        inline static TQVariantList toVariant(VALUE value) {
            if( TYPE(value) != T_ARRAY ) {
                rb_raise(rb_eTypeError, "TQVariantList must be an array");
                return TQVariantList();
            }
            TQVariantList l;
#ifdef HAVE_RUBY_1_9
            for(int i = 0; i < RARRAY_LEN(value); i++)
#else
            for(int i = 0; i < RARRAY(value)->len; i++)
#endif
                l.append( RubyType<TQVariant>::toVariant( rb_ary_entry(value, i) ) );
            return l;
        }
    };
#endif

#if 0
    /// \internal
    template<>
    struct RubyType<TQStringVariantMap>
    {
        inline static VALUE toVALUE(const TQStringVariantMap& map) {
            VALUE h = rb_hash_new();
            TQStringVariantMap::ConstIterator it(map.constBegin()), end(map.end());
            for(; it != end; ++it)
                rb_hash_aset(h, RubyType<TQString>::toVALUE(it.key()), RubyType<TQVariant>::toVALUE(it.value()) );
            return h;
        }
        inline static int convertHash(VALUE key, VALUE value, VALUE  vmap) {
            TQStringVariantMap* map;
            Data_Get_Struct(vmap, TQStringVariantMap, map);
            if (key != TQundef)
                map->insert(STR2CSTR(key), RubyType<TQVariant>::toVariant(value));
            return ST_CONTINUE;
        }
        inline static TQStringVariantMap toVariant(VALUE value) {
            if( TYPE(value) != T_HASH ) {
                rb_raise(rb_eTypeError, "TQStringVariantMap must be a hash");
                return TQStringVariantMap();
            }
            TQStringVariantMap map;
            VALUE vmap = Data_Wrap_Struct(rb_cObject, 0,0, &map);
            rb_hash_foreach(value, (int (*)(...))convertHash, vmap);
            return map;
        }
    };
#endif

#if 0
    /**
     * The RubyMetaTypeFactory helper class us used as factory within
     * \a RubyExtension to translate an argument into a \a MetaType
     * needed for TQGenericArgument's data pointer.
     */
    class RubyMetaTypeFactory
    {
        public:
            static MetaType* create(int typeId, int metaTypeId, VALUE valueect = TQnil);
    };

    /// \internal
    template<typename VARIANTTYPE>
    class RubyMetaTypeVariant : public MetaTypeVariant<VARIANTTYPE>
    {
        public:
            RubyMetaTypeVariant(VALUE value)
                : MetaTypeVariant<VARIANTTYPE>(
                    (TYPE(value) == T_NIL)
                        ? TQVariant().value<VARIANTTYPE>()
                        : RubyType<VARIANTTYPE>::toVariant(value)
                ) {}

            virtual ~RubyMetaTypeVariant() {}
    };

#endif

}

#endif

