/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- registerColorNative
 - color_class_init
 - attachColorInterface
 - color_getrgb
 - color_gettransform
 - color_setrgb
 - color_settransform
 - color_ctor
 - parseColorTransProp
 - getTarget
 
// Color.cpp:  ActionScript class for colors, for Gnash.
// 
//   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
//   2011 Free Software Foundation, Inc
//
// 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 3 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//
#include "Color_as.h"
#include <sstream>
#include "as_object.h" // for inheritance
#include "log.h"
#include "fn_call.h"
#include "Global_as.h"
#include "smart_ptr.h" // for boost intrusive_ptr
#include "NativeFunction.h" 
#include "SWFCxForm.h" // for composition
#include "VM.h"
#include "MovieClip.h"
#include "namedStrings.h"
namespace gnash {
// Forward declarations.
namespace {
    as_value color_getrgb(const fn_call& fn);
    as_value color_gettransform(const fn_call& fn);
    as_value color_setrgb(const fn_call& fn);
    as_value color_settransform(const fn_call& fn);
    as_value color_ctor(const fn_call& fn);
    void attachColorInterface(as_object& o);
    inline void parseColorTransProp(as_object& obj, const ObjectURI& key,
            boost::int16_t& target, bool scale);
    inline MovieClip* getTarget(as_object* obj, const fn_call& fn);
}
void
registerColorNative(as_object& o)
{
        VM& vm = getVM(o);
        vm.registerNative(color_setrgb, 700, 0);
        vm.registerNative(color_settransform, 700, 1);
        vm.registerNative(color_getrgb, 700, 2);
        vm.registerNative(color_gettransform, 700, 3);
}
// extern (used by Global.cpp)
void
color_class_init(as_object& where, const ObjectURI& uri)
{
    as_object* cl = registerBuiltinClass(where, color_ctor,
            attachColorInterface, 0, uri);
    as_object* proto = toObject(
        getMember(*cl, NSV::PROP_PROTOTYPE), getVM(where));
    if (!proto) return;
    const int protect = as_object::DefaultFlags | PropFlags::readOnly;
    proto->set_member_flags(NSV::PROP_uuPROTOuu, protect); 
    proto->set_member_flags(NSV::PROP_CONSTRUCTOR, protect); 
}
namespace {
void
attachColorInterface(as_object& o)
{
        VM& vm = getVM(o);
    const int flags = PropFlags::dontEnum |
                      PropFlags::dontDelete |
                      PropFlags::readOnly;
        o.init_member("setRGB", vm.getNative(700, 0), flags);
        o.init_member("setTransform", vm.getNative(700, 1), flags);
        o.init_member("getRGB", vm.getNative(700, 2), flags);
        o.init_member("getTransform", vm.getNative(700, 3), flags);
}
as_value
color_getrgb(const fn_call& fn)
{
        as_object* obj = ensure<ValidThis>(fn);
    MovieClip* sp = getTarget(obj, fn);
    if (!sp) return as_value();
        const SWFCxForm& trans = getCxForm(*sp);
    const int r = trans.rb;
    const int g = trans.gb;
    const int b = trans.bb;
    const boost::int32_t rgb = (r<<16) | (g<<8) | b;
        return as_value(rgb);
}
as_value
color_gettransform(const fn_call& fn)
{
        as_object* obj = ensure<ValidThis>(fn);
    MovieClip* sp = getTarget(obj, fn);
    if (!sp) return as_value();
        const SWFCxForm& cx = getCxForm(*sp);
        // Convert to as_object
    Global_as& gl = getGlobal(fn);
        as_object* ret = createObject(gl);
        ret->init_member("ra", double(cx.ra / 2.56));
        ret->init_member("ga", double(cx.ga / 2.56));
        ret->init_member("ba", double(cx.ba / 2.56));
        ret->init_member("aa", double(cx.aa / 2.56));
        ret->init_member("rb", int(cx.rb));
        ret->init_member("gb", int(cx.gb));
        ret->init_member("bb", int(cx.bb));
        ret->init_member("ab", int(cx.ab));
        return ret;
}
as_value
color_setrgb(const fn_call& fn)
{
        as_object* obj = ensure<ValidThis>(fn);
        
    if (!fn.nargs) {
                IF_VERBOSE_ASCODING_ERRORS(
                log_aserror(_("Color.setRGB() : missing argument"));
                );
                return as_value();
        }
    MovieClip* sp = getTarget(obj, fn);
    if (!sp) return as_value();
        boost::int32_t color = toInt(fn.arg(0), getVM(fn));
        const int r = (color & 0xff0000) >> 16;
        const int g = (color & 0x00ff00) >> 8;
        const int b = (color & 0x0000ff);
        SWFCxForm newTrans = getCxForm(*sp);
        newTrans.rb = static_cast<boost::int16_t>(r);
        newTrans.gb = static_cast<boost::int16_t>(g);
        newTrans.bb = static_cast<boost::int16_t>(b);
        newTrans.ra = 0;
        newTrans.ga = 0;
        newTrans.ba = 0;
    sp->setCxForm(newTrans);
        return as_value();
}
as_value
color_settransform(const fn_call& fn)
{
        as_object* obj = ensure<ValidThis>(fn);
        if (!fn.nargs) {
                IF_VERBOSE_ASCODING_ERRORS(
                log_aserror(_("Color.setTransform() : missing argument"));
                );
                return as_value();
        }
        as_object* trans = toObject(fn.arg(0), getVM(fn));
    if (!trans) {
                IF_VERBOSE_ASCODING_ERRORS(
            std::ostringstream ss; fn.dump_args(ss);
            log_aserror(_("Color.setTransform(%s) : first argument doesn't "
                                "cast to an object"), ss.str());
                );
                return as_value();
        }
    MovieClip* sp = getTarget(obj, fn);
    if (!sp) return as_value();
        VM& vm = getVM(*obj);
        SWFCxForm newTrans = getCxForm(*sp);
    // TODO: use NSV 
        // multipliers
        parseColorTransProp(*trans, getURI(vm, "ra"), newTrans.ra, true);
        parseColorTransProp(*trans, getURI(vm, "ga"), newTrans.ga, true);
        parseColorTransProp(*trans, getURI(vm, "ba"), newTrans.ba, true);
        parseColorTransProp(*trans, getURI(vm, "aa"), newTrans.aa, true);
        // offsets
        parseColorTransProp(*trans, getURI(vm, "rb"), newTrans.rb, false);
        parseColorTransProp(*trans, getURI(vm, "gb"), newTrans.gb, false);
        parseColorTransProp(*trans, getURI(vm, "bb"), newTrans.bb, false);
        parseColorTransProp(*trans, getURI(vm, "ab"), newTrans.ab, false);
        sp->setCxForm(newTrans);
        return as_value();
}
/// The first argument is set as the target property.
//
/// The target property is used to change the MovieClip's color.
/// The pp calls ASSetPropFlags on all Color properties during construction,
/// adding the readOnly flag. Because Gnash adds the __constructor__ property
/// during construction, we have no control over its flags.
as_value
color_ctor(const fn_call& fn)
{
        
    as_object* obj = ensure<ValidThis>(fn);
    
    as_value target;
    if (fn.nargs) target = fn.arg(0);
    obj->set_member(NSV::PROP_TARGET, target);
    Global_as& gl = getGlobal(fn);
    as_object* null = 0;
    callMethod(&gl, NSV::PROP_AS_SET_PROP_FLAGS, obj, null, 7);
        return as_value(); 
}
inline void
parseColorTransProp(as_object& obj, const ObjectURI& key, boost::int16_t&
        target, bool scale)
{
        as_value tmp;
        if (!obj.get_member(key, &tmp)) return;
    
        const double d = toNumber(tmp, getVM(obj));
        if (scale) {   
        target = static_cast<boost::int16_t>(d * 2.56);
    }
        else {
        target = static_cast<boost::int16_t>(d);
    }
}
// First try to convert target to a MovieClip. If that fails, convert to 
// a string and look up the target.
inline MovieClip*
getTarget(as_object* obj, const fn_call& fn)
{
    const as_value& target = getMember(*obj, NSV::PROP_TARGET);
    MovieClip* sp = target.toMovieClip();
    if (sp) return sp;
    DisplayObject* o = findTarget(fn.env(), target.to_string());
    if (o) return o->to_movie();
    return 0;
}
} // anonymous namespace 
} // end of gnash namespace