#include "gpuCacheMaterial.h"
#include <boost/foreach.hpp>
namespace GPUCache {
struct MaterialProperty::PropertyData : boost::noncopyable
{
    PropertyData()          {}
    virtual ~PropertyData() {}
};
struct MaterialProperty::BoolPropertyData : MaterialProperty::PropertyData 
{
    bool value;
    BoolPropertyData()       : value(false) {}
    BoolPropertyData(bool v) : value(v)     {}
};
struct MaterialProperty::Int32PropertyData : MaterialProperty::PropertyData
{
    int value;
    Int32PropertyData()      : value(0) {}
    Int32PropertyData(int v) : value(v) {}
};
struct MaterialProperty::FloatPropertyData : MaterialProperty::PropertyData
{
    float value;
    FloatPropertyData()        : value(0) {}
    FloatPropertyData(float v) : value(v) {}
};
struct MaterialProperty::Float2PropertyData : MaterialProperty::PropertyData
{
    float x, y;
    Float2PropertyData()                 : x(0), y(0) {}
    Float2PropertyData(float a, float b) : x(a), y(b) {}
};
struct MaterialProperty::Float3PropertyData : MaterialProperty::PropertyData
{
    float x, y, z;
    Float3PropertyData()                          : x(0), y(0), z(0) {}
    Float3PropertyData(float a, float b, float c) : x(a), y(b), z(c) {}
};
struct MaterialProperty::ColorPropertyData : MaterialProperty::PropertyData
{
    ColorPropertyData()                           {}
    ColorPropertyData(
const MColor& v) : value(v) {}
};
struct MaterialProperty::StringPropertyData : MaterialProperty::PropertyData
{
    StringPropertyData()                            {}
    StringPropertyData(
const MString& v) : value(v) {}
};
MaterialProperty::MPtr MaterialProperty::create(
    MaterialProperty::Type type
)
{
    return boost::make_shared<MaterialProperty>(boost::ref(name), type);
}
MaterialProperty::MaterialProperty(
const MString& name, Type type)
    : fName(name), fType(type)
{
    fDefaultValue = createData(type);
}
MaterialProperty::~MaterialProperty()
{}
bool MaterialProperty::asBool(double seconds) const
{
    assert(fType == kBool);
    return findValue<BoolPropertyData>(seconds)->value;
}
void MaterialProperty::setBool(double seconds, bool value)
{
    assert(fType == kBool);
    setValue(seconds, boost::make_shared<BoolPropertyData>(value));
}
int MaterialProperty::asInt32(double seconds) const
{
    assert(fType == kInt32);
    return findValue<Int32PropertyData>(seconds)->value;
}
void MaterialProperty::setInt32(double seconds, int value)
{
    assert(fType == kInt32);
    setValue(seconds, boost::make_shared<Int32PropertyData>(value));
}
float MaterialProperty::asFloat(double seconds) const
{
    assert(fType == kFloat);
    return findValue<FloatPropertyData>(seconds)->value;
}
void MaterialProperty::setFloat(double seconds, float value)
{
    assert(fType == kFloat);
    setValue(seconds, boost::make_shared<FloatPropertyData>(value));
}
void MaterialProperty::asFloat2(double seconds, float& x, float& y) const
{
    assert(fType == kFloat2);
    const Float2PropertyData* data = findValue<Float2PropertyData>(seconds);
    x = data->x;
    y = data->y;
}
void MaterialProperty::setFloat2(double seconds, float x, float y)
{
    assert(fType == kFloat2);
    setValue(seconds, boost::make_shared<Float2PropertyData>(x, y));
}
void MaterialProperty::asFloat3(double seconds, float& x, float& y, float& z) const
{
    assert(fType == kFloat3);
    const Float3PropertyData* data = findValue<Float3PropertyData>(seconds);
    x = data->x;
    y = data->y;
    z = data->z;
}
void MaterialProperty::setFloat3(double seconds, float x, float y, float z)
{
    assert(fType == kFloat3);
    setValue(seconds, boost::make_shared<Float3PropertyData>(x, y, z));
}
const MColor& MaterialProperty::asColor(
double seconds)
 const 
{
    assert(fType == kRGB);
    return findValue<ColorPropertyData>(seconds)->value;
}
void MaterialProperty::setColor(
double seconds, 
const MColor& value)
 
{
    assert(fType == kRGB);
    setValue(seconds, boost::make_shared<ColorPropertyData>(boost::ref(value)));
}
const MString& MaterialProperty::asString(
double seconds)
 const 
{
    assert(fType == kString);
    return findValue<StringPropertyData>(seconds)->value;
}
void MaterialProperty::setString(
double seconds, 
const MString& value)
 
{
    assert(fType == kString);
    setValue(seconds, boost::make_shared<StringPropertyData>(boost::ref(value)));
}
void MaterialProperty::setDefault(bool value)
{
    fDefaultValue = boost::make_shared<BoolPropertyData>(value);
}
void MaterialProperty::setDefault(int value)
{
    fDefaultValue = boost::make_shared<Int32PropertyData>(value);
}
void MaterialProperty::setDefault(float value)
{
    fDefaultValue = boost::make_shared<FloatPropertyData>(value);
}
void MaterialProperty::setDefault(float x, float y)
{
    fDefaultValue = boost::make_shared<Float2PropertyData>(x, y);
}
void MaterialProperty::setDefault(float x, float y, float z)
{
    fDefaultValue = boost::make_shared<Float3PropertyData>(x, y, z);
}
void MaterialProperty::setDefault(
const MColor& value)
 
{
    fDefaultValue = boost::make_shared<ColorPropertyData>(boost::ref(value));
}
void MaterialProperty::setDefault(
const MString& value)
 
{
    fDefaultValue = boost::make_shared<StringPropertyData>(boost::ref(value));
}
bool MaterialProperty::getDefaultAsBool() const
{
    const BoolPropertyData* data = static_cast<const BoolPropertyData*>(fDefaultValue.get());
    return data->value;
}
int MaterialProperty::getDefaultAsInt32() const
{
    const Int32PropertyData* data = static_cast<const Int32PropertyData*>(fDefaultValue.get());
    return data->value;
}
float MaterialProperty::getDefaultAsFloat() const
{
    const FloatPropertyData* data = static_cast<const FloatPropertyData*>(fDefaultValue.get());
    return data->value;
}
void MaterialProperty::getDefaultAsFloat2(float& x, float& y) const
{
    const Float2PropertyData* data = static_cast<const Float2PropertyData*>(fDefaultValue.get());
    x = data->x;
    y = data->y;
}
void MaterialProperty::getDefaultAsFloat3(float& x, float& y, float& z) const
{
    const Float3PropertyData* data = static_cast<const Float3PropertyData*>(fDefaultValue.get());
    x = data->x;
    y = data->y;
    z = data->z;
}
const MColor&  MaterialProperty::getDefaultAsColor()
 const 
{
    const ColorPropertyData* data = static_cast<const ColorPropertyData*>(fDefaultValue.get());
    return data->value;
}
const MString& MaterialProperty::getDefaultAsString()
 const 
{
    const StringPropertyData* data = static_cast<const StringPropertyData*>(fDefaultValue.get());
    return data->value;
}
MaterialProperty::PropertyDataPtr MaterialProperty::createData(Type type)
{
    switch (type) {
    case kBool:
        return boost::make_shared<BoolPropertyData>();
    case kInt32:
        return boost::make_shared<Int32PropertyData>();
    case kFloat:
        return boost::make_shared<FloatPropertyData>();
    case kFloat2:
        return boost::make_shared<Float2PropertyData>();
    case kFloat3:
        return boost::make_shared<Float3PropertyData>();
    case kRGB:
        return boost::make_shared<ColorPropertyData>();
    case kString:
        return boost::make_shared<StringPropertyData>();
    default:
        assert(0);  
        return boost::make_shared<FloatPropertyData>();
    }
}
MaterialProperty::MPtr MaterialNode::createProperty(
const MString& name, MaterialProperty::Type type)
{
    
    MaterialProperty::MPtr prop = MaterialProperty::create(name, type);
    assert(fProperties.find(name) == fProperties.end());
    fProperties.insert(std::make_pair(name, prop));
    return prop;
}
void MaterialNode::createProperty(
const MString& name, MaterialProperty::Type type, MaterialPropertyRef& ref)
 
{
    
    MaterialProperty::MPtr prop = createProperty(name, type);
    ref.initialize(prop);
}
MaterialProperty::MPtr MaterialNode::findProperty(
const MString& name)
{
    
    PropertyMap::iterator it = fProperties.find(name);
    if (it != fProperties.end()) {
        return boost::const_pointer_cast<MaterialProperty>((*it).second);
    }
    return MaterialProperty::MPtr();
}
MaterialProperty::Ptr MaterialNode::findProperty(
const MString& name)
 const{
    
    PropertyMap::const_iterator it = fProperties.find(name);
    if (it != fProperties.end()) {
        return (*it).second;
    }
    return MaterialProperty::Ptr();
}
bool MaterialGraph::isAnimated() const
{
    BOOST_FOREACH (const NamedMap::value_type& val, fMaterialNodeMap) {
        const MaterialNode::Ptr& node = val.second;
        if (!node) continue;
        BOOST_FOREACH (const MaterialNode::PropertyMap::value_type& val2, node->properties()) {
            const MaterialProperty::Ptr& prop = val2.second;
            if (!prop) continue;
            
            if (prop->isAnimated()) {
                return true;
            }
        }
    }
    return false;
}
void MaterialGraphMap::addMaterialGraph(const MaterialGraph::Ptr& graph)
{
    assert(fMaterialGraphMap.find(graph->name()) == fMaterialGraphMap.end());
    fMaterialGraphMap.insert(std::make_pair(graph->name(), graph));
}
const MaterialGraph::Ptr MaterialGraphMap::find(
const MString name)
 const 
{
    NamedMap::const_iterator it = fMaterialGraphMap.find(name);
    if (it != fMaterialGraphMap.end()) {
        return (*it).second;
    }
    return MaterialGraph::Ptr();
}
}