#include "TreeTemplate.h"

#include "Dimension.h"
#include "Game.h"
#include "MultiblockTree.h"

TreeTemplate::TreeTemplate()
    : GeneratorTemplate(),
      woodBlockTypeId(0),
      leaveBlockType(0),
      minHeight(0),
      maxHeight(0)
{
    setMinPosOffset(Framework::Vec3<int>(-2, -2, 0));
}

GeneratedStructure* TreeTemplate::generateAt(
    Framework::Vec3<int> location, Noise* zNoise, int dimensionId)
{
    double noise = zNoise->getNoise((double)location.x + 40,
        (double)location.y + 70,
        (double)location.z - 20);
    int height = (int)(minHeight + (noise * (maxHeight - minHeight)));
    Dimension* zDim = Game::INSTANCE->zDimension(dimensionId);
    MultiblockStructure* treeStructure = zDim->zStructureByPosition(location);
    if (!treeStructure)
    {
        treeStructure = new MultiblockTree(
            dimensionId, zDim->getNextStructureId(), location);
        zDim->addStructure(treeStructure);
    }
    Framework::Vec3<int> minAffected
        = Framework::Vec3<int>(-2, -2, 0) + location;
    GeneratedStructure* generated
        = new GeneratedStructure(dynamic_cast<GeneratorTemplate*>(getThis()),
            location,
            Framework::Vec3<int>(5, 5, height),
            minAffected);
    for (int x = 1; x < 4; x++)
    {
        for (int y = 1; y < 4; y++)
        {
            generated->setBlockAt(
                leaveBlockType, Framework::Vec3<int>(x, y, height - 1));
            generated->setBlockAt(
                leaveBlockType, Framework::Vec3<int>(x, y, height - 5));
        }
    }
    for (int z = height - 2; z >= height - 4; z--)
    {
        for (int x = 1; x < 4; x++)
        {
            generated->setBlockAt(
                leaveBlockType, Framework::Vec3<int>(x, 0, z));
            generated->setBlockAt(
                leaveBlockType, Framework::Vec3<int>(0, x, z));
            generated->setBlockAt(
                leaveBlockType, Framework::Vec3<int>(x, 4, z));
            generated->setBlockAt(
                leaveBlockType, Framework::Vec3<int>(4, x, z));
            for (int y = 1; y < 4; y++)
                generated->setBlockAt(
                    leaveBlockType, Framework::Vec3<int>(x, y, z));
        }
    }
    for (int i = 0; i < height - 1; i++)
        generated->setBlockAt(woodBlockTypeId, Framework::Vec3<int>(2, 2, i));
    generated->addAllBlocksToStructure(
        dynamic_cast<MultiblockStructure*>(treeStructure->getThis()));
    return generated;
}

void TreeTemplate::setWoodTypeId(int id)
{
    woodBlockTypeId = id;
}

void TreeTemplate::setLeavesTypeId(int id)
{
    leaveBlockType = id;
}

const BlockType* TreeTemplate::zWoodType() const
{
    return Game::INSTANCE->zBlockType(woodBlockTypeId);
}

const BlockType* TreeTemplate::zLeavesType() const
{
    return Game::INSTANCE->zBlockType(leaveBlockType);
}

void TreeTemplate::setMinHeight(int height)
{
    minHeight = height;
}

void TreeTemplate::setMaxHeight(int height)
{
    maxHeight = height;
    setMaxSize(Framework::Vec3<int>(5, 5, maxHeight));
}

int TreeTemplate::getMinHeight() const
{
    return minHeight;
}

int TreeTemplate::getMaxHeight() const
{
    return maxHeight;
}

TreeTemplateFactory::TreeTemplateFactory()
    : GeneratorTemplateFactory()
{}

TreeTemplate* TreeTemplateFactory::createValue(
    Framework::JSON::JSONObject* zJson) const
{
    return new TreeTemplate();
}

TreeTemplate* TreeTemplateFactory::fromJson(
    Framework::JSON::JSONObject* zConfig) const
{
    TreeTemplate* result = GeneratorTemplateFactory::fromJson(zConfig);
    result->setWoodTypeId(BlockType::getTypeId(
        zConfig->asObject()->zValue("wood")->asString()->getString()));
    result->setLeavesTypeId(BlockType::getTypeId(
        zConfig->asObject()->zValue("leaves")->asString()->getString()));
    result->setMinHeight(
        (int)(zConfig->asObject()->zValue("minSize")->asNumber()->getNumber()));
    result->setMaxHeight(
        (int)(zConfig->asObject()->zValue("maxSize")->asNumber()->getNumber()));
    return result;
}

Framework::JSON::JSONObject* TreeTemplateFactory::toJsonObject(
    TreeTemplate* zObject) const
{
    Framework::JSON::JSONObject* result
        = GeneratorTemplateFactory::toJsonObject(zObject);
    result->addValue("propability",
        new Framework::JSON::JSONNumber((double)zObject->getPropability()));
    result->addValue("wood",
        new Framework::JSON::JSONString(zObject->zWoodType()->getName()));
    result->addValue("leaves",
        new Framework::JSON::JSONString(zObject->zLeavesType()->getName()));
    result->addValue("minSize",
        new Framework::JSON::JSONNumber((double)zObject->getMinHeight()));
    result->addValue("maxSize",
        new Framework::JSON::JSONNumber((double)zObject->getMaxHeight()));
    return result;
}

JSONObjectValidationBuilder* TreeTemplateFactory::addToValidator(
    JSONObjectValidationBuilder* builder) const
{
    Framework::RCArray<Framework::Text> blockTypeNames;
    for (int i = 0; i < Game::INSTANCE->getBlockTypeCount(); i++)
    {
        if (Game::INSTANCE->zBlockType(i))
        {
            blockTypeNames.add(
                new Framework::Text(Game::INSTANCE->zBlockType(i)->getName()));
        }
    }
    return GeneratorTemplateFactory::addToValidator(
        builder->withRequiredString("wood")
            ->whichIsOneOf(blockTypeNames)
            ->finishString()
            ->withRequiredString("leaves")
            ->whichIsOneOf(blockTypeNames)
            ->finishString()
            ->withRequiredNumber("minSize")
            ->whichIsGreaterThen(0)
            ->finishNumber()
            ->withRequiredNumber("maxSize")
            ->whichIsGreaterThen(0)
            ->finishNumber()
            ->withRequiredNumber("propability")
            ->whichIsGreaterThen(0)
            ->finishNumber());
}

const char* TreeTemplateFactory::getTypeToken() const
{
    return "Tree";
}