///////////////////////////////////////////////////////////////////////////////
// Returns true if the specified item can be added to the array.  This is 
// determined by checking to make sure that the item derived from the common
// base class that was specified earlier.
// 
bool ClipboardElementArray::CanAdd( const Reflect::ObjectPtr& item ) const
{
    if ( !item.ReferencesObject() )
    {
        return false;
    }

    return item->IsA( GetCommonBaseClass() );
}
/// @copydoc ResourceHandler::CacheResource()
bool MaterialResourceHandler::CacheResource(
    AssetPreprocessor* pAssetPreprocessor,
    Resource* pResource,
    const String& /*rSourceFilePath*/ )
{
    HELIUM_ASSERT( pAssetPreprocessor );
    HELIUM_ASSERT( pResource );

    Material* pMaterial = Reflect::AssertCast< Material >( pResource );
    Shader* pShader = pMaterial->GetShader();
    bool failedToWriteASubdata = false;
    
    StrongPtr< Material::PersistentResourceData > resource_data( new Material::PersistentResourceData() );

    // Compute the shader variant indices from the user options selected in the material, as the array of indices in
    // the material is not yet initialized.
    //uint32_t shaderVariantIndices[ RShader::TYPE_MAX ];
    if( pShader )
    {
        const Shader::Options& rShaderUserOptions = pShader->GetUserOptions();
        const DynamicArray< Shader::SelectPair >& rMaterialUserOptions = pMaterial->GetUserOptions();

        for( size_t shaderTypeIndex = 0; shaderTypeIndex < RShader::TYPE_MAX; ++shaderTypeIndex )
        {
            size_t optionSetIndex = rShaderUserOptions.GetOptionSetIndex(
                static_cast< RShader::EType >( shaderTypeIndex ),
                rMaterialUserOptions.GetData(),
                rMaterialUserOptions.GetSize() );
            resource_data->m_shaderVariantIndices[ shaderTypeIndex ] = static_cast< uint32_t >( optionSetIndex );
        }
    }
    else
    {
        MemoryZero( resource_data->m_shaderVariantIndices, sizeof( resource_data->m_shaderVariantIndices ) );
    }

    size_t float1ParameterCount = pMaterial->GetFloat1ParameterCount();
    size_t float2ParameterCount = pMaterial->GetFloat2ParameterCount();
    size_t float3ParameterCount = pMaterial->GetFloat3ParameterCount();
    size_t float4ParameterCount = pMaterial->GetFloat4ParameterCount();

    Name parameterConstantBufferName = Material::GetParameterConstantBufferName();
    
    for( size_t platformIndex = 0; platformIndex < static_cast< size_t >( Cache::PLATFORM_MAX ); ++platformIndex )
    {
        PlatformPreprocessor* pPreprocessor = pAssetPreprocessor->GetPlatformPreprocessor(
            static_cast< Cache::EPlatform >( platformIndex ) );

        if( !pPreprocessor )
        {
            continue;
        }

        Resource::PreprocessedData& rPreprocessedData = pResource->GetPreprocessedData(
            static_cast< Cache::EPlatform >( platformIndex ) );
        SaveObjectToPersistentDataBuffer(resource_data.Get(), rPreprocessedData.persistentDataBuffer);
        rPreprocessedData.bLoaded = true;

        // Write out the parameter constant buffer data as the resource sub-data.
        size_t shaderProfileCount = pPreprocessor->GetShaderProfileCount();

        DynamicArray< DynamicArray< uint8_t > >& rSubDataBuffers = rPreprocessedData.subDataBuffers;
        rSubDataBuffers.Clear();
        rSubDataBuffers.Reserve( shaderProfileCount * RShader::TYPE_MAX );
        rSubDataBuffers.Resize( shaderProfileCount * RShader::TYPE_MAX );

        if( pShader )
        {
    //        deserializer.SetByteSwapping( bSwapBytes );

            for( size_t shaderTypeIndex = 0; shaderTypeIndex < RShader::TYPE_MAX; ++shaderTypeIndex )
            {
                RShader::EType shaderType = static_cast< RShader::EType >( shaderTypeIndex );
                size_t variantLoadId = pShader->BeginLoadVariant(
                    shaderType,
                    resource_data->m_shaderVariantIndices[ shaderTypeIndex ] );
                if( IsInvalid( variantLoadId ) )
                {
                    continue;
                }

                ShaderVariantPtr spVariant;
                while( !pShader->TryFinishLoadVariant( variantLoadId, spVariant ) )
                {
                }

                ShaderVariant* pVariant = spVariant;
                if( !pVariant )
                {
                    continue;
                }

                const Resource::PreprocessedData& rVariantData = pVariant->GetPreprocessedData(
                    static_cast< Cache::EPlatform >( platformIndex ) );
                HELIUM_ASSERT( rVariantData.bLoaded );

                const DynamicArray< DynamicArray< uint8_t > >& rVariantSubDataBuffers = rVariantData.subDataBuffers;
                size_t variantSubDataCount = rVariantSubDataBuffers.GetSize();
                HELIUM_ASSERT( variantSubDataCount != 0 );
                HELIUM_ASSERT( variantSubDataCount % shaderProfileCount == 0 );
                size_t systemOptionSetCount = variantSubDataCount / shaderProfileCount;

                for( size_t profileIndex = 0; profileIndex < shaderProfileCount; ++profileIndex )
                {
                    // Get the first option set of each profile
                    const DynamicArray< uint8_t >& rVariantSubData =
                        rVariantSubDataBuffers[ profileIndex * systemOptionSetCount ];

                    Reflect::ObjectPtr variantSubDataObjectPtr = 
                        Cache::ReadCacheObjectFromBuffer(rVariantSubData);

                    if (!variantSubDataObjectPtr.ReferencesObject())
                    {
                        HELIUM_TRACE(
                            TraceLevels::Error,
                            "MaterialResourceHandler: A shader variant subdata could not be read. (Option Set: %d Profile: %d)",
                            0,
                            profileIndex );

                        failedToWriteASubdata = true;
                    }
                    else if (!variantSubDataObjectPtr->IsA(Reflect::GetMetaClass<CompiledShaderData>()))
                    {
                        HELIUM_TRACE(
                            TraceLevels::Error,
                            "MaterialResourceHandler: A shader variant subdata was of an unexpected type. (Option Set: %d Profile: %d)\n",
                            0,
                            profileIndex );

                        failedToWriteASubdata = true;

                    }
                    else
                    {
                        CompiledShaderData &csd = *Reflect::AssertCast<CompiledShaderData>(variantSubDataObjectPtr.Get());
                        

                        size_t bufferCount = csd.constantBuffers.GetSize();
                        for( size_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex )
                        {
                            const ShaderConstantBufferInfo& rBufferInfo = csd.constantBuffers[ bufferIndex ];
                            if( rBufferInfo.name != parameterConstantBufferName )
                            {
                                continue;
                            }

                            size_t bufferSize = rBufferInfo.size;

                            DynamicArray< uint8_t >& rMaterialSubData =
                                rSubDataBuffers[ profileIndex * RShader::TYPE_MAX + shaderTypeIndex ];
                            rMaterialSubData.Clear();
                            rMaterialSubData.Reserve( bufferSize );
                            rMaterialSubData.Add( 0, bufferSize );

                            DynamicMemoryStream memoryStream( &rMaterialSubData );
                            ByteSwappingStream byteSwapStream( &memoryStream );
                            Stream& rOutputStream = memoryStream;
                            //Stream& rOutputStream =
                            //    ( bSwapBytes
                            //    ? static_cast< Stream& >( byteSwapStream )
                            //    : static_cast< Stream& >( memoryStream ) );

                            const DynamicArray< ShaderConstantInfo >& rConstants = rBufferInfo.constants;
                            size_t constantCount = rConstants.GetSize();
                            for( size_t constantIndex = 0; constantIndex < constantCount; ++constantIndex )
                            {
                                const ShaderConstantInfo& rConstantInfo = rConstants[ constantIndex ];
                                Name constantName = rConstantInfo.name;

                                size_t parameterIndex;
                                for( parameterIndex = 0; parameterIndex < float1ParameterCount; ++parameterIndex )
                                {
                                    const Material::Float1Parameter& rParameter = pMaterial->GetFloat1Parameter(
                                        parameterIndex );
                                    if( rParameter.name == constantName )
                                    {
                                        rOutputStream.Seek( rConstantInfo.offset, SeekOrigins::Begin );
                                        rOutputStream.Write(
                                            &rParameter.value,
                                            sizeof( float32_t ),
                                            Min< size_t >( 1, rConstantInfo.size / sizeof( float32_t ) ) );

                                        break;
                                    }
                                }

                                if( parameterIndex >= float1ParameterCount )
                                {
                                    for( parameterIndex = 0; parameterIndex < float2ParameterCount; ++parameterIndex )
                                    {
                                        const Material::Float2Parameter& rParameter = pMaterial->GetFloat2Parameter(
                                            parameterIndex );
                                        if( rParameter.name == constantName )
                                        {
                                            rOutputStream.Seek( rConstantInfo.offset, SeekOrigins::Begin );
                                            rOutputStream.Write(
                                                &rParameter.value,
                                                sizeof( float32_t ),
                                                Min< size_t >( 2, rConstantInfo.size / sizeof( float32_t ) ) );

                                            break;
                                        }
                                    }

                                    if( parameterIndex >= float2ParameterCount )
                                    {
                                        for( parameterIndex = 0;
                                            parameterIndex < float3ParameterCount;
                                            ++parameterIndex )
                                        {
                                            const Material::Float3Parameter& rParameter = pMaterial->GetFloat3Parameter(
                                                parameterIndex );
                                            if( rParameter.name == constantName )
                                            {
                                                rOutputStream.Seek( rConstantInfo.offset, SeekOrigins::Begin );
                                                rOutputStream.Write(
                                                    &rParameter.value,
                                                    sizeof( float32_t ),
                                                    Min< size_t >( 3, rConstantInfo.size / sizeof( float32_t ) ) );

                                                break;
                                            }
                                        }

                                        if( parameterIndex >= float3ParameterCount )
                                        {
                                            for( parameterIndex = 0;
                                                parameterIndex < float4ParameterCount;
                                                ++parameterIndex )
                                            {
                                                const Material::Float4Parameter& rParameter =
                                                    pMaterial->GetFloat4Parameter( parameterIndex );
                                                if( rParameter.name == constantName )
                                                {
                                                    rOutputStream.Seek(
                                                        rConstantInfo.offset,
                                                        SeekOrigins::Begin );
                                                    rOutputStream.Write(
                                                        &rParameter.value,
                                                        sizeof( float32_t ),
                                                        Min< size_t >( 4, rConstantInfo.size / sizeof( float32_t ) ) );

                                                    break;
                                                }
                                            }
                                        }
                                    }
                                }
                            }

                            break;
                        }
                    }
                }
            }
        }
    }


    return !failedToWriteASubdata;
}