/* vim: set ts=4 sw=4 tw=99 et:
 *
 * Copyright (C) 2012 David Anderson
 *
 * This file is part of JITCraft.
 *
 * JITCraft 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.
 * 
 * Foobar 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
 * JITCraft. If not, see http://www.gnu.org/licenses/.
 */
#include <string.h>
#include "Types.h"
#include "TypeManager.h"
#include "Zone.h"
#include "Strings.h"
#include "Spaces-inl.h"
#include "Heap-inl.h"

using namespace ke;


TypeManager::TypeManager(Zone *zone)
  : zone_(zone)
{
}

bool
TypeManager::initialize()
{
    voidType_ = Type::NewVoid(zone_);
    if (!voidType_)
        return false;

    uncheckedType_ = Type::NewUnchecked(zone_);
    if (!uncheckedType_)
        return false;

    if ((primitiveTypes_[PrimitiveType_Int32] = Type::NewPrimitive(zone_, PrimitiveType_Int32)) == NULL)
        return false;
    if ((primitiveTypes_[PrimitiveType_Float] = Type::NewPrimitive(zone_, PrimitiveType_Float)) == NULL)
        return false;
    if ((primitiveTypes_[PrimitiveType_Native] = Type::NewPrimitive(zone_, PrimitiveType_Native)) == NULL)
        return false;
    if ((primitiveTypes_[PrimitiveType_Char] = Type::NewPrimitive(zone_, PrimitiveType_Char)) == NULL)
        return false;
    if ((primitiveTypes_[PrimitiveType_Bool] = Type::NewPrimitive(zone_, PrimitiveType_Bool)) == NULL)
        return false;

    Local<Type> type(zone_);
    for (unsigned i = 0; i < PrimitiveTypes_Total; i++) {
        type = primitiveTypes_[i];
        if ((referenceTypes_[i] = ReferenceType::New(zone_, type)) == NULL)
            return false;
    }

    return true;
}

Type *
TypeManager::newArray(Handle<Type> contained, int elements)
{
    // :TODO: cache this.
    return ArrayType::New(zone_, contained, elements);
}

Type *
TypeManager::newExternalArray(Handle<Type> contained)
{
    return ArrayType::NewExternal(zone_, contained);
}

Type *
TypeManager::getReference(Handle<Type> type, bool isConst)
{
    if (!isConst && type->isPrimitive())
        return referenceTypes_[type->primitive()];

    // :TODO: cache this.
    return ReferenceType::New(zone_, type);
}

Type *
TypeManager::newEnum(Handle<String> name)
{
    return Type::NewEnum(zone_, name);
}

void
TypeManager::accept(VirtualObjectVisitor *visitor)
{
    visitor->visit(voidType_.address());
    visitor->visit(uncheckedType_.address());
    for (unsigned i = 0; i < PrimitiveTypes_Total; i++) {
        visitor->visit(primitiveTypes_[i].address());
        visitor->visit(referenceTypes_[i].address());
    }
}
