#include "PerformanceScenarioTest.h"
#include "../testResource.h"

USING_NS_CC;

enum
{
    TEST_COUNT = 1,
};

static int s_nScenarioCurCase = 0;

////////////////////////////////////////////////////////
//
// ScenarioMenuLayer
//
////////////////////////////////////////////////////////
void ScenarioMenuLayer::showCurrentTest()
{
    Scene* scene = nullptr;

    switch (_curCase)
    {
    case 0:
        scene = ScenarioTest::scene();
        break;
    }
    s_nScenarioCurCase = _curCase;

    if (scene)
    {
        Director::getInstance()->replaceScene(scene);
    }
}

void ScenarioMenuLayer::onEnter()
{
    PerformBasicLayer::onEnter();

    auto s = Director::getInstance()->getWinSize();

    // Title
    auto label = Label::createWithTTF(title().c_str(), "fonts/arial.ttf", 32);
    addChild(label, 1);
    label->setPosition(Vec2(s.width/2, s.height-50));

    // Subtitle
    std::string strSubTitle = subtitle();
    if(strSubTitle.length())
    {
        auto l = Label::createWithTTF(strSubTitle.c_str(), "fonts/Thonburi.ttf", 16);
        addChild(l, 1);
        l->setPosition(Vec2(s.width/2, s.height-80));
    }

    performTests();
}

std::string ScenarioMenuLayer::title() const
{
    return "no title";
}

std::string ScenarioMenuLayer::subtitle() const
{
    return "";
}

////////////////////////////////////////////////////////
//
// ScenarioTest
//
////////////////////////////////////////////////////////
int ScenarioTest::_initParticleNum = 500;
int ScenarioTest::_parStepNum = 500;
int ScenarioTest::_initSpriteNum = 2000;
int ScenarioTest::_spriteStepNum = 500;
int ScenarioTest::_initParsysNum = 10;
int ScenarioTest::_parsysStepNum = 5;

void ScenarioTest::performTests()
{
    auto listener = EventListenerTouchAllAtOnce::create();
    listener->onTouchesMoved = CC_CALLBACK_2(ScenarioTest::onTouchesMoved, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

    _particleNumber = _initParticleNum;

    // get the window size & origin position
    auto s = Director::getInstance()->getVisibleSize();
    auto origin = Director::getInstance()->getVisibleOrigin();

    // add tile map
    _map1 = TMXTiledMap::create("TileMaps/iso-test.tmx");
    _map1->setAnchorPoint( Vec2(0.5, 0.5) );
    _map1->setPosition(origin);
    this->addChild(_map1);

    _map2 = TMXTiledMap::create("TileMaps/iso-test2.tmx");
    _map2->setAnchorPoint( Vec2(0.5, 0.5) );
    _map2->setPosition(origin);
    this->addChild(_map2);

    // add toggle menu item
    MenuItemFont::setFontSize(20);
    _itemToggle = MenuItemToggle::createWithCallback(nullptr,
                                                    MenuItemFont::create( "Add/Remove Sprite" ),
                                                    MenuItemFont::create( "Add/Remove Particle"),
                                                    MenuItemFont::create( "Add/Remove Particle System"),
                                                    nullptr);
    _itemToggle->setAnchorPoint(Vec2(0.0f, 0.5f));
    _itemToggle->setPosition(Vec2(origin.x, origin.y + s.height / 2));

    // add decrease & increase menu item
    MenuItemFont::setFontSize(65);
    auto decrease = MenuItemFont::create(" - ", [&](Ref *sender) {
		int idx = _itemToggle->getSelectedIndex();
        switch (idx) {
        case 0:
            removeSprites();
            break;
        case 1:
            removeParticles();
            break;
        case 2:
            removeParticleSystem();
            break;
        default:
            break;
        }
	});
    decrease->setPosition(Vec2(origin.x + s.width / 2 - 80, origin.y + 80));
    decrease->setColor(Color3B(0,200,20));
    auto increase = MenuItemFont::create(" + ", [&](Ref *sender) {
		int idx = _itemToggle->getSelectedIndex();
        switch (idx) {
        case 0:
            addNewSprites(_spriteStepNum);
            break;
        case 1:
            addParticles();
            break;
        case 2:
            addParticleSystem(_parsysStepNum);
            break;
        default:
            break;
        }
	});
    increase->setColor(Color3B(0,200,20));
    increase->setPosition(Vec2(origin.x + s.width / 2 + 80, origin.y + 80));
    
    auto menu = Menu::create(_itemToggle, decrease, increase, nullptr);
    menu->setPosition(Vec2(0.0f, 0.0f));
    addChild(menu, 10);

    
    // add tip labels
    _spriteLabel = Label::createWithTTF("Sprites : 0", "fonts/arial.ttf", 15);
    _spriteLabel->setAnchorPoint(Vec2(0.0f, 0.5f));
    addChild(_spriteLabel, 10);
    _spriteLabel->setPosition(Vec2(origin.x, origin.y + s.height/2 + 70));
    
    char str[32] = { 0 };
    sprintf(str, "Particles : %d", _particleNumber);
    _particleLabel = Label::createWithTTF(str, "fonts/arial.ttf", 15);
    _particleLabel->setAnchorPoint(Vec2(0.0f, 0.5f));
    addChild(_particleLabel, 10);
    _particleLabel->setPosition(Vec2(origin.x, origin.y + s.height/2 + 45));
    
    _parsysLabel = Label::createWithTTF("Particle System : 0", "fonts/arial.ttf", 15);
    _parsysLabel->setAnchorPoint(Vec2(0.0f, 0.5f));
    addChild(_parsysLabel, 10);
    _parsysLabel->setPosition(Vec2(origin.x, origin.y + s.height/2 + 20));

    // add sprites
    addNewSprites(_initSpriteNum);
    
    // add particle system
    addParticleSystem(_initParsysNum);
}

void ScenarioTest::onTouchesMoved(const std::vector<Touch*>& touches, Event  *event)
{
    auto touch = touches[0];
    
    auto diff = touch->getDelta();
    auto currentPos1 = _map1->getPosition();
    _map1->setPosition(currentPos1 + diff);
    
    auto currentPos2 = _map2->getPosition();
    _map2->setPosition(currentPos2 + diff);
}

void ScenarioTest::addParticles()
{
    _particleNumber += _parStepNum;

    for (auto par : _parsysArray) {
        par->setTotalParticles(_particleNumber);
    }

    char str[25] = { 0 };
    sprintf(str, "Particles : %d", _particleNumber);
    _particleLabel->setString(str);
}

void ScenarioTest::removeParticles()
{
    if (_particleNumber <= 0) {
        return;
    }
    
    int removeNum = MIN(_particleNumber, _parStepNum);
    _particleNumber -= removeNum;

    for (auto par : _parsysArray) {
        par->setTotalParticles(_particleNumber);
    }

    char str[25] = { 0 };
    sprintf(str, "Particles : %d", _particleNumber);
    _particleLabel->setString(str);
}

void ScenarioTest::addNewSprites(int num)
{
    auto s = Director::getInstance()->getVisibleSize();
    auto origin = Director::getInstance()->getVisibleOrigin();

    for (int i = 0; i < num; ++i) {
        int idx = (int)(CCRANDOM_0_1() * 1400.0f / 100.0f);
        int x = (idx%5) * 85;
        int y = (idx/5) * 121;

        auto sprite = Sprite::create("Images/grossini_dance_atlas.png", Rect(x,y,85,121) );
        addChild( sprite );
        
        float randomx = CCRANDOM_0_1();
        float randomy = CCRANDOM_0_1();
        sprite->setPosition(origin + Vec2(randomx * s.width, randomy * s.height));
        
        ActionInterval* action;
        float random = CCRANDOM_0_1();
        
        if( random < 0.20 )
            action = ScaleBy::create(3, 2);
        else if(random < 0.40)
            action = RotateBy::create(3, 360);
        else if( random < 0.60)
            action = Blink::create(1, 3);
        else if( random < 0.8 )
            action = TintBy::create(2, 0, -255, -255);
        else
            action = FadeOut::create(2);
        auto action_back = action->reverse();
        auto seq = Sequence::create( action, action_back, nullptr );
        
        sprite->runAction( RepeatForever::create(seq) );

        _spriteArray.pushBack(sprite);
    }

    char str[20] = {0};
    sprintf(str, "Sprites : %d", (int)_spriteArray.size());
    _spriteLabel->setString(str);
}

void ScenarioTest::removeSprites()
{
    ssize_t number = _spriteArray.size();
    if (number <= 0) {
        return;
    }

    ssize_t removeNum = MIN(number, _spriteStepNum);
    for (int i = 0; i < removeNum; ++i) {
        auto sprite = _spriteArray.getRandomObject();
        removeChild(sprite);
        _spriteArray.eraseObject(sprite);
    }
    
    char str[20] = {0};
    sprintf(str, "Sprites : %d", (int)_spriteArray.size());
    _spriteLabel->setString(str);
}

static const std::string _particleFiles[] = {
    "Particles/BoilingFoam.plist",
    "Particles/Galaxy.plist",
    "Particles/SmallSun.plist",
    "Particles/lines.plist",
    "Particles/Comet.plist",
    "Particles/LavaFlow.plist",
    "Particles/SpinningPeas.plist",
    "Particles/Flower.plist",
    "Particles/Phoenix.plist",
    "Particles/debian.plist",
};
void ScenarioTest::addParticleSystem(int num)
{
    int filesSize = sizeof(_particleFiles) / sizeof(std::string);
    auto s = Director::getInstance()->getVisibleSize();
    auto origin = Director::getInstance()->getVisibleOrigin();

    for (int i = 0; i < num; ++i)
    {
        float randomIdx = CCRANDOM_0_1();
        int idx = (filesSize - 1) * randomIdx;
        std::string fileName = _particleFiles[idx];
        auto par = ParticleSystemQuad::create(fileName);

        float randomx = CCRANDOM_0_1();
        float randomy = CCRANDOM_0_1();
        par->setPosition(origin + Vec2(s.width * randomx, s.height * randomy));
        par->setTotalParticles(_particleNumber);
        addChild(par, 9);

        _parsysArray.pushBack(par);
    }

    char str[40] = {0};
    sprintf(str, "Particle System : %d", (int)_parsysArray.size());
    _parsysLabel->setString(str);
}

void ScenarioTest::removeParticleSystem()
{
    ssize_t number = _parsysArray.size();
    if (number <= 0) {
        return;
    }
    
    ssize_t removeNum = MIN(number, _parsysStepNum);
    for (int i = 0; i < removeNum; ++i) {
        auto par = _parsysArray.getRandomObject();
        removeChild(par);
        _parsysArray.eraseObject(par);
    }
    
    char str[40] = {0};
    sprintf(str, "Particle System : %d", (int)_parsysArray.size());
    _parsysLabel->setString(str);
}

std::string ScenarioTest::title() const
{
    return "Scenario Performance Test";
}

//
//std::string ScenarioTest::subtitle() const
//{
//    return "See console for results";
//}

Scene* ScenarioTest::scene()
{
    auto scene = Scene::create();
    ScenarioTest *layer = new (std::nothrow) ScenarioTest(false, TEST_COUNT, s_nScenarioCurCase);
    scene->addChild(layer);
    layer->release();

    return scene;
}

void runScenarioTest()
{
    s_nScenarioCurCase = 0;
    auto scene = ScenarioTest::scene();
    Director::getInstance()->replaceScene(scene);
}