/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */


#include "SkMemberInfo.h"
#include "SkAnimateMaker.h"
#include "SkAnimatorScript.h"
#include "SkBase64.h"
#include "SkCamera.h"
#include "SkDisplayable.h"
#include "SkDisplayTypes.h"
#include "SkDraw3D.h"
#include "SkDrawColor.h"
#include "SkParse.h"
#include "SkScript.h"
#include "SkTSearch.h"
#include "SkTypedArray.h"

size_t SkMemberInfo::GetSize(SkDisplayTypes type) { // size of simple types only
    size_t byteSize;
    switch (type) {
        case SkType_ARGB:
            byteSize = sizeof(SkColor);
            break;
        case SkType_AddMode:
        case SkType_Align:
        case SkType_ApplyMode:
        case SkType_ApplyTransition:
        case SkType_BitmapEncoding:
        case SkType_Boolean:
        case SkType_Cap:
        case SkType_EventCode:
        case SkType_EventKind:
        case SkType_EventMode:
        case SkType_FilterType:
        case SkType_FontStyle:
        case SkType_FromPathMode:
        case SkType_Join:
        case SkType_MaskFilterBlurStyle:
        case SkType_PathDirection:
        case SkType_Style:
        case SkType_TileMode:
        case SkType_Xfermode:
            byteSize = sizeof(int);
            break;
        case SkType_Base64: // assume base64 data is always const, copied by ref
        case SkType_Displayable:
        case SkType_Drawable:
        case SkType_Matrix:
            byteSize = sizeof(void*);
            break;
        case SkType_MSec:
            byteSize = sizeof(SkMSec);
            break;
        case SkType_Point:
            byteSize = sizeof(SkPoint);
            break;
        case SkType_3D_Point:
            byteSize = sizeof(Sk3D_Point);
            break;
        case SkType_Int:
            byteSize = sizeof(int32_t);
            break;
        case SkType_Float:
            byteSize = sizeof(SkScalar);
            break;
        case SkType_DynamicString:
        case SkType_String:
            byteSize = sizeof(SkString);    // assume we'll copy by reference, not value
            break;
        default:
//          SkASSERT(0);
            byteSize = 0;
    }
    return byteSize;
}

bool SkMemberInfo::getArrayValue(const SkDisplayable* displayable, int index, SkOperand* value) const {
    SkASSERT(fType != SkType_String && fType != SkType_MemberProperty);
    char* valuePtr = (char*) *(SkOperand**) memberData(displayable);
    SkDisplayTypes type = (SkDisplayTypes) 0;
    if (displayable->getType() == SkType_Array) {
        SkDisplayArray* dispArray = (SkDisplayArray*) displayable;
        if (dispArray->values.count() <= index)
            return false;
        type = dispArray->values.getType();
    } else {
        SkASSERT(0); // incomplete
    }
    size_t byteSize = GetSize(type);
    memcpy(value, valuePtr + index * byteSize, byteSize);
    return true;
}

size_t SkMemberInfo::getSize(const SkDisplayable* displayable) const {
    size_t byteSize;
    switch (fType) {
        case SkType_MemberProperty:
            byteSize = GetSize(propertyType());
            break;
        case SkType_Array: {
            SkDisplayTypes type;
            if (displayable == NULL)
                return sizeof(int);
            if (displayable->getType() == SkType_Array) {
                SkDisplayArray* dispArray = (SkDisplayArray*) displayable;
                type = dispArray->values.getType();
            } else
                type = propertyType();
            SkTDOperandArray* array = (SkTDOperandArray*) memberData(displayable);
            byteSize = GetSize(type) * array->count();
            } break;
        default:
            byteSize = GetSize((SkDisplayTypes) fType);
    }
    return byteSize;
}

void SkMemberInfo::getString(const SkDisplayable* displayable, SkString** string) const {
    if (fType == SkType_MemberProperty) {
        SkScriptValue value;
        displayable->getProperty(propertyIndex(), &value);
        SkASSERT(value.fType == SkType_String);
        *string = value.fOperand.fString;
        return;
    }
    SkASSERT(fCount == sizeof(SkString) / sizeof(SkScalar));
    SkASSERT(fType == SkType_String || fType == SkType_DynamicString);
    void* valuePtr = memberData(displayable);
    *string = (SkString*) valuePtr;
}

void SkMemberInfo::getValue(const SkDisplayable* displayable, SkOperand value[], int count) const {
    SkASSERT(fType != SkType_String && fType != SkType_MemberProperty);
    SkASSERT(count == fCount);
    void* valuePtr = memberData(displayable);
    size_t byteSize = getSize(displayable);
    SkASSERT(sizeof(value[0].fScalar) == sizeof(value[0])); // no support for 64 bit pointers, yet
    memcpy(value, valuePtr, byteSize);
}

void SkMemberInfo::setString(SkDisplayable* displayable, SkString* value) const {
    SkString* string = (SkString*) memberData(displayable);
    string->set(*value);
    displayable->dirty();
}

void SkMemberInfo::setValue(SkDisplayable* displayable, const SkOperand values[],
                            int count) const {
    SkASSERT(sizeof(values[0].fScalar) == sizeof(values[0]));   // no support for 64 bit pointers, yet
    char* dst = (char*) memberData(displayable);
    if (fType == SkType_Array) {
        SkTDScalarArray* array = (SkTDScalarArray* ) dst;
        array->setCount(count);
        dst = (char*) array->begin();
    }
    memcpy(dst, values, count * sizeof(SkOperand));
    displayable->dirty();
}


static inline bool is_between(int c, int min, int max)
{
    return (unsigned)(c - min) <= (unsigned)(max - min);
}

static inline bool is_hex(int c)
{
    if (is_between(c, '0', '9'))
        return true;
    c |= 0x20;  // make us lower-case
    if (is_between(c, 'a', 'f'))
        return true;
    return false;
}


bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage,
    int storageOffset, int maxStorage, SkDisplayable* displayable, SkDisplayTypes outType,
    const char rawValue[], size_t rawValueLen) const
{
    SkString valueStr(rawValue, rawValueLen);
    SkScriptValue scriptValue;
    scriptValue.fType = SkType_Unknown;
    scriptValue.fOperand.fS32 = 0;
    SkDisplayTypes type = getType();
    SkAnimatorScript engine(maker, displayable, type);
    if (arrayStorage)
        displayable = NULL;
    bool success = true;
    void* untypedStorage = NULL;
    if (displayable && fType != SkType_MemberProperty && fType != SkType_MemberFunction)
        untypedStorage = (SkTDOperandArray*) memberData(displayable);

    if (type == SkType_ARGB) {
        // for both SpiderMonkey and SkiaScript, substitute any #xyz or #xxyyzz first
            // it's enough to expand the colors into 0xFFxxyyzz
        const char* poundPos;
        while ((poundPos = strchr(valueStr.c_str(), '#')) != NULL) {
            size_t offset = poundPos - valueStr.c_str();
            if (valueStr.size() - offset < 4)
                break;
            char r = poundPos[1];
            char g = poundPos[2];
            char b = poundPos[3];
            if (is_hex(r) == false || is_hex(g) == false || is_hex(b) == false)
                break;
            char hex = poundPos[4];
            if (is_hex(hex) == false) {
                valueStr.insertUnichar(offset + 1, r);
                valueStr.insertUnichar(offset + 3, g);
                valueStr.insertUnichar(offset + 5, b);
            }
            *(char*) poundPos = '0'; // overwrite '#'
            valueStr.insert(offset + 1, "xFF");
        }
    }
    if (SkDisplayType::IsDisplayable(&maker, type) || SkDisplayType::IsEnum(&maker, type) || type == SkType_ARGB)
        goto scriptCommon;
    switch (type) {
        case SkType_String:
#if 0
            if (displayable && displayable->isAnimate()) {

                goto noScriptString;
            }
            if (strncmp(rawValue, "#string:", sizeof("#string:") - 1) == 0) {
                SkASSERT(sizeof("string") == sizeof("script"));
                char* stringHeader = valueStr.writable_str();
                memcpy(&stringHeader[1], "script", sizeof("script") - 1);
                rawValue = valueStr.c_str();
                goto noScriptString;
            } else
#endif
            if (strncmp(rawValue, "#script:", sizeof("#script:") - 1) != 0)
                goto noScriptString;
            valueStr.remove(0, 8);
        case SkType_Unknown:
        case SkType_Int:
        case SkType_MSec:  // for the purposes of script, MSec is treated as a Scalar
        case SkType_Point:
        case SkType_3D_Point:
        case SkType_Float:
        case SkType_Array:
scriptCommon: {
                const char* script = valueStr.c_str();
                success = engine.evaluateScript(&script, &scriptValue);
                if (success == false) {
                    maker.setScriptError(engine);
                    return false;
                }
            }
            SkASSERT(success);
            if (scriptValue.fType == SkType_Displayable) {
                if (type == SkType_String) {
                    const char* charPtr = NULL;
                    maker.findKey(scriptValue.fOperand.fDisplayable, &charPtr);
                    scriptValue.fOperand.fString = new SkString(charPtr);
                    scriptValue.fType = SkType_String;
                    engine.SkScriptEngine::track(scriptValue.fOperand.fString);
                    break;
                }
                SkASSERT(SkDisplayType::IsDisplayable(&maker, type));
                if (displayable)
                    displayable->setReference(this, scriptValue.fOperand.fDisplayable);
                else
                    arrayStorage->begin()[0].fDisplayable = scriptValue.fOperand.fDisplayable;
                return true;
            }
            if (type != scriptValue.fType) {
                if (scriptValue.fType == SkType_Array) {
                    engine.forget(scriptValue.getArray());
                    goto writeStruct; // real structs have already been written by script
                }
                switch (type) {
                    case SkType_String:
                        success = engine.convertTo(SkType_String, &scriptValue);
                        break;
                    case SkType_MSec:
                    case SkType_Float:
                        success = engine.convertTo(SkType_Float, &scriptValue);
                        break;
                    case SkType_Int:
                        success = engine.convertTo(SkType_Int, &scriptValue);
                        break;
                    case SkType_Array:
                        success = engine.convertTo(arrayType(), &scriptValue);
                        // !!! incomplete; create array of appropriate type and add scriptValue to it
                        SkASSERT(0);
                        break;
                    case SkType_Displayable:
                    case SkType_Drawable:
                        return false;   // no way to convert other types to this
                    default:    // to avoid warnings
                        break;
                }
                if (success == false)
                    return false;
            }
            if (type == SkType_MSec)
                scriptValue.fOperand.fMSec = SkScalarRoundToInt(scriptValue.fOperand.fScalar * 1000);
            scriptValue.fType = type;
        break;
        noScriptString:
        case SkType_DynamicString:
            if (fType == SkType_MemberProperty && displayable) {
                SkString string(rawValue, rawValueLen);
                SkScriptValue scriptValue;
                scriptValue.fOperand.fString = &string;
                scriptValue.fType = SkType_String;
                displayable->setProperty(propertyIndex(), scriptValue);
            } else if (displayable) {
                SkString* string = (SkString*) memberData(displayable);
                string->set(rawValue, rawValueLen);
            } else {
                SkASSERT(arrayStorage->count() == 1);
                arrayStorage->begin()->fString->set(rawValue, rawValueLen);
            }
            goto dirty;
        case SkType_Base64: {
            SkBase64 base64;
            base64.decode(rawValue, rawValueLen);
            *(SkBase64* ) untypedStorage = base64;
            } goto dirty;
        default:
            SkASSERT(0);
            break;
    }
//  if (SkDisplayType::IsStruct(type) == false)
    {
writeStruct:
        if (writeValue(displayable, arrayStorage, storageOffset, maxStorage,
                untypedStorage, outType, scriptValue)) {
                    maker.setErrorCode(SkDisplayXMLParserError::kUnexpectedType);
            return false;
        }
    }
dirty:
    if (displayable)
        displayable->dirty();
    return true;
}

bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage,
        int storageOffset, int maxStorage, SkDisplayable* displayable, SkDisplayTypes outType,
        SkString& raw) const {
    return setValue(maker, arrayStorage, storageOffset, maxStorage, displayable, outType, raw.c_str(),
        raw.size());
}

bool SkMemberInfo::writeValue(SkDisplayable* displayable, SkTDOperandArray* arrayStorage,
    int storageOffset, int maxStorage, void* untypedStorage, SkDisplayTypes outType,
    SkScriptValue& scriptValue) const
{
    SkOperand* storage = untypedStorage ? (SkOperand*) untypedStorage : arrayStorage ?
        arrayStorage->begin() : NULL;
    if (storage)
        storage += storageOffset;
    SkDisplayTypes type = getType();
    if (fType == SkType_MemberProperty) {
        if(displayable)
            displayable->setProperty(propertyIndex(), scriptValue);
        else {
            SkASSERT(storageOffset < arrayStorage->count());
            switch (scriptValue.fType) {
                case SkType_Boolean:
                case SkType_Float:
                case SkType_Int:
                    memcpy(&storage->fScalar, &scriptValue.fOperand.fScalar, sizeof(SkScalar));
                    break;
                case SkType_Array:
                    memcpy(&storage->fScalar, scriptValue.fOperand.fArray->begin(), scriptValue.fOperand.fArray->count() * sizeof(SkScalar));
                    break;
                case SkType_String:
                    storage->fString->set(*scriptValue.fOperand.fString);
                    break;
                default:
                    SkASSERT(0);    // type isn't handled yet
            }
        }
    } else if (fType == SkType_MemberFunction) {
        SkASSERT(scriptValue.fType == SkType_Array);
        if (displayable)
            displayable->executeFunction(displayable, this, scriptValue.fOperand.fArray, NULL);
        else {
            int count = scriptValue.fOperand.fArray->count();
    //      SkASSERT(maxStorage == 0 || count == maxStorage);
            if (arrayStorage->count() == 2)
                arrayStorage->setCount(2 * count);
            else {
                storageOffset *= count;
                SkASSERT(count + storageOffset <= arrayStorage->count());
            }
            memcpy(&(*arrayStorage)[storageOffset], scriptValue.fOperand.fArray->begin(), count * sizeof(SkOperand));
        }

    } else if (fType == SkType_Array) {
        SkTypedArray* destArray = (SkTypedArray*) (untypedStorage ? untypedStorage : arrayStorage);
        SkASSERT(destArray);
    //  destArray->setCount(0);
        if (scriptValue.fType != SkType_Array) {
            SkASSERT(type == scriptValue.fType);
    //      SkASSERT(storageOffset + 1 <= maxStorage);
            destArray->setCount(storageOffset + 1);
            (*destArray)[storageOffset] = scriptValue.fOperand;
        } else {
            if (type == SkType_Unknown) {
                type = scriptValue.fOperand.fArray->getType();
                destArray->setType(type);
            }
            SkASSERT(type == scriptValue.fOperand.fArray->getType());
            int count = scriptValue.fOperand.fArray->count();
    //      SkASSERT(storageOffset + count <= maxStorage);
            destArray->setCount(storageOffset + count);
            memcpy(destArray->begin() + storageOffset, scriptValue.fOperand.fArray->begin(), sizeof(SkOperand) * count);
        }
    } else if (type == SkType_String) {
        SkString* string = untypedStorage ? (SkString*) untypedStorage : (*arrayStorage)[storageOffset].fString;
        string->set(*scriptValue.fOperand.fString);
    } else if (type == SkType_ARGB && outType == SkType_Float) {
        SkTypedArray* array = scriptValue.fOperand.fArray;
        SkASSERT(scriptValue.fType == SkType_Int || scriptValue.fType == SkType_ARGB ||
            scriptValue.fType == SkType_Array);
        SkASSERT(scriptValue.fType != SkType_Array || (array != NULL &&
            array->getType() == SkType_Int));
        int numberOfColors = scriptValue.fType == SkType_Array ? array->count() : 1;
        int numberOfComponents = numberOfColors * 4;
    //  SkASSERT(maxStorage == 0 || maxStorage == numberOfComponents);
        if (maxStorage == 0)
            arrayStorage->setCount(numberOfComponents);
        for (int index = 0; index < numberOfColors; index++) {
            SkColor color = scriptValue.fType == SkType_Array ?
                (SkColor) array->begin()[index].fS32 : (SkColor) scriptValue.fOperand.fS32;
            storage[0].fScalar = SkIntToScalar(SkColorGetA(color));
            storage[1].fScalar = SkIntToScalar(SkColorGetR(color));
            storage[2].fScalar = SkIntToScalar(SkColorGetG(color));
            storage[3].fScalar = SkIntToScalar(SkColorGetB(color));
            storage += 4;
        }
    } else if (SkDisplayType::IsStruct(NULL /* !!! maker*/, type)) {
        if (scriptValue.fType != SkType_Array)
            return true;    // error
        SkASSERT(sizeof(SkScalar) == sizeof(SkOperand)); // !!! no 64 bit pointer support yet
        int count = scriptValue.fOperand.fArray->count();
        if (count > 0) {
            SkASSERT(fCount == count);
            memcpy(storage, scriptValue.fOperand.fArray->begin(), count * sizeof(SkOperand));
        }
    } else if (scriptValue.fType == SkType_Array) {
        SkASSERT(scriptValue.fOperand.fArray->getType() == type);
        SkASSERT(scriptValue.fOperand.fArray->count() == getCount());
        memcpy(storage, scriptValue.fOperand.fArray->begin(), getCount() * sizeof(SkOperand));
    } else {
        memcpy(storage, &scriptValue.fOperand, sizeof(SkOperand));
    }
    return false;
}


//void SkMemberInfo::setValue(SkDisplayable* displayable, const char value[], const char name[]) const {
//  void* valuePtr = (void*) ((char*) displayable + fOffset);
//  switch (fType) {
//      case SkType_Point3D: {
//          static const char xyz[] = "x|y|z";
//          int index = find_one(xyz, name);
//          SkASSERT(index >= 0);
//          valuePtr = (void*) ((char*) valuePtr + index * sizeof(SkScalar));
//          } break;
//      default:
//          SkASSERT(0);
//  }
//  SkParse::FindScalar(value, (SkScalar*) valuePtr);
//  displayable->dirty();
//}

#if SK_USE_CONDENSED_INFO == 0

// Find Nth memberInfo
const SkMemberInfo* SkMemberInfo::Find(const SkMemberInfo info[], int count, int* index) {
    SkASSERT(*index >= 0);
    if (info->fType == SkType_BaseClassInfo) {
        const SkMemberInfo* inherited = (SkMemberInfo*) info->fName;
        const SkMemberInfo* result = SkMemberInfo::Find(inherited, info->fCount, index);
        if (result != NULL)
            return result;
        if (--count == 0)
            return NULL;
        info++;
    }
    SkASSERT(info->fName);
    SkASSERT(info->fType != SkType_BaseClassInfo);
    if (*index >= count) {
        *index -= count;
        return NULL;
    }
    return &info[*index];
}

// Find named memberinfo
const SkMemberInfo* SkMemberInfo::Find(const SkMemberInfo info[], int count, const char** matchPtr) {
    const char* match = *matchPtr;
    if (info->fType == SkType_BaseClassInfo) {
        const SkMemberInfo* inherited = (SkMemberInfo*) info->fName;
        const SkMemberInfo* result = SkMemberInfo::Find(inherited, info->fCount, matchPtr);
        if (result != NULL)
            return result;
        if (--count == 0)
            return NULL;
        info++;
    }
    SkASSERT(info->fName);
    SkASSERT(info->fType != SkType_BaseClassInfo);
    int index = SkStrSearch(&info->fName, count, match, sizeof(*info));
    if (index < 0 || index >= count)
        return NULL;
    return &info[index];
}

const SkMemberInfo* SkMemberInfo::getInherited() const {
    return (SkMemberInfo*) fName;
}

#endif // SK_USE_CONDENSED_INFO == 0

#if 0
bool SkMemberInfo::SetValue(void* valuePtr, const char value[], SkDisplayTypes type,
                            int count) {
    switch (type) {
        case SkType_Animate:
        case SkType_BaseBitmap:
        case SkType_Bitmap:
        case SkType_Dash:
        case SkType_Displayable:
        case SkType_Drawable:
        case SkType_Matrix:
        case SkType_Path:
        case SkType_Text:
        case SkType_3D_Patch:
            return false; // ref to object; caller must resolve
        case SkType_MSec: {
            SkParse::FindMSec(value, (SkMSec*) valuePtr);
            } break;
        case SkType_3D_Point:
        case SkType_Point:
    //  case SkType_PointArray:
        case SkType_ScalarArray:
            SkParse::FindScalars(value, (SkScalar*) valuePtr, count);
            break;
        default:
            SkASSERT(0);
    }
    return true;
}
#endif