//
//  GameScene.cpp
//  RedCore2
//
//  Created by Gabriel Capella on 31/05/17.
//
//

#include "GameScene.h"
#include "SimpleAudioEngine.h"
#include "BlocksLayer.h"
#include "Ball.h"
#include "params.h"

USING_NS_CC;

Scene* GameScene::createScene(int level) {
    auto scene = Scene::createWithPhysics();
    Size visibleSize = Director::getInstance()->getVisibleSize();
    
    //choose whitch part need to draw, Joint, Shape, Contact, None or All
    // scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
    
    GameScene* layer = GameScene::create();
    scene->addChild(layer);
    
    auto material = PHYSICSBODY_MATERIAL_DEFAULT;
    material.density = 1.0f;
    material.restitution = 1.0f;
    material.friction = 0.0f;
    
    // the edge of the screen
    auto body = PhysicsBody::createEdgeBox(visibleSize, material);
    auto edgeNode = Node::create();
    edgeNode->setPosition(Point(visibleSize.width/2,visibleSize.height/2));
    body->setDynamic(false);
    edgeNode->setPhysicsBody(body);
    scene->addChild(edgeNode);
    
    auto blocks = BlocksLayer::create();
    blocks->setLevel(level);
    blocks->setPosition(visibleSize.width/2, visibleSize.height);
    layer->addChild(blocks, 20);
    
    layer->setLevel(level);
    
    auto delay_save_level = DelayTime::create(10.0);
    CallFunc *runCallback_save_level = CallFunc::create(CC_CALLBACK_0(GameScene::saveLevel, layer));
    auto seq_save_level = Sequence::create(delay_save_level, runCallback_save_level, nullptr);
    auto delay_triple_balls = DelayTime::create(10.0);
    CallFunc *runCallback_triple_balls = CallFunc::create(CC_CALLBACK_0(GameScene::tripleBalls, layer));
    auto seq_triple_balls = Sequence::create(delay_triple_balls, runCallback_triple_balls, nullptr);
    layer->runAction(seq_save_level);
    layer->runAction(seq_triple_balls);
    
    return scene;
}

// on "init" you need to initialize your instance
bool GameScene::init() {
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() ) {
        return false;
    }
    
    this->over = false;
    
    // https://www.freesound.org/people/schademans/sounds/13290/
    FileUtils::getInstance()->addSearchPath("res");
    auto audio = CocosDenshion::SimpleAudioEngine::getInstance();
    audio->preloadEffect("pipe.wav");
    audio->preloadEffect("metal.wav");
    audio->preloadEffect("bomb.wav");
    audio->preloadEffect("win.wav");
    
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    
    // Adiona o fundo
    auto bg = LayerColor::create(COLOR_back);
    this->addChild(bg);
    
    
    auto ball = Ball::create();
    ball->setPosition(visibleSize.width / 2, RAQUETE_ALTURA+BALL_SIZE);
    ball->throwBall();
    this->addChild(ball, 21);
    UserDefault *userdata = UserDefault::getInstance();
    userdata->setIntegerForKey("ball_count", 1);
    userdata->flush();
    
    auto raquete = DrawNode::create();
    float py = RAQUETE_HEIGHT/2.0;
    float pxl = - RAQUETE_WIDTH/2;
    float pxr =  RAQUETE_WIDTH/2;
    raquete->drawSegment(Vec2(pxl, py), Vec2(pxr, py), py, Color4F(COLOR_grey));
    raquete->setPositionX(visibleSize.width/2);
    raquete->setPositionY(RAQUETE_ALTURA);
    
    auto bsize = Size(RAQUETE_WIDTH+RAQUETE_HEIGHT, RAQUETE_HEIGHT);
    auto physicsBody = PhysicsBody::createBox(bsize, PhysicsMaterial(0.1f, 1.0f, 0.0f));
    physicsBody->setPositionOffset(Vec2(0, RAQUETE_HEIGHT/2));
    physicsBody->setGravityEnable(false);
    physicsBody->setDynamic(false);
    physicsBody->setContactTestBitmask(0xFFFFFFFF);
    raquete->setTag(RACKET_TAG);
    raquete->addComponent(physicsBody);
    
    this->addChild(raquete);
    
    auto listener1 = EventListenerTouchOneByOne::create();
    
    // trigger when you push down
    listener1->onTouchBegan = [=](Touch* touch, Event* event){
        raquete->setPositionX(touch->getLocation().x);
        float px = touch->getLocation().x;
        if (px >= RAQUETE_WIDTH/2 && px < visibleSize.width - RAQUETE_WIDTH/2)
            raquete->setPositionX(touch->getLocation().x);
        return true; // if you are consuming it
    };
    
    // trigger when moving touch
    listener1->onTouchMoved = [=](Touch* touch, Event* event){
        float px = touch->getLocation().x;
        if (px >= RAQUETE_WIDTH/2 && px <= visibleSize.width - RAQUETE_WIDTH/2)
            raquete->setPositionX(touch->getLocation().x);
    };
    

    // Add listener
    _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, this);

    // the edge of bottom
    auto body = PhysicsBody::createEdgeBox(Size(visibleSize.width, 0));
    auto edgeNode = Node::create();
    edgeNode->setPosition(Point(visibleSize.width/2, 0));
    body->setDynamic(false);
    edgeNode->setTag(BOTTOM_TAG);
    body->setContactTestBitmask(0xFFFFFFFF);
    edgeNode->setPhysicsBody(body);
    this->addChild(edgeNode);
    
    // Evento no contato das bolas
    auto contactListener = EventListenerPhysicsContact::create();
    contactListener->onContactBegin = CC_CALLBACK_1(GameScene::onContactBegin, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
    
    return true;
}

bool GameScene::onContactBegin(PhysicsContact& contact) {
    auto nodeA = contact.getShapeA()->getBody()->getNode();
    auto nodeB = contact.getShapeB()->getBody()->getNode();
    
    if (nodeA && nodeB && nodeA->getTag() != nodeB->getTag() && !this->over) {
        //CCLOG("%d %d", nodeB->getTag(), nodeA->getTag());
        
        if (nodeB->getTag() > nodeA->getTag()) {
            auto tmp = nodeB;
            nodeB = nodeA;
            nodeA = tmp;
        }
        
        // sempre B < A
        if (nodeB->getTag() == BLOCK_TAG) {
            auto audio = CocosDenshion::SimpleAudioEngine::getInstance();
            audio->playEffect("pipe.wav");
            nodeB->removeFromParentAndCleanup(true);
        } else if (nodeB->getTag() == INDESTRUCTIBLE_BLOCK_TAG) {
            auto audio = CocosDenshion::SimpleAudioEngine::getInstance();
            audio->playEffect("metal.wav");
        } else if (nodeB->getTag() == BOTTOM_TAG && nodeA->getTag() == BALL_TAG) {
            ball_collision(nodeA);
        }  else if (nodeB->getTag() == CORE_TAG && nodeA->getTag() == BALL_TAG) {
            ball_core(nodeB, nodeA);
        }  else if (nodeB->getTag() == BALL_TAG && nodeA->getTag() == CORE_TAG) {
            ball_core(nodeA, nodeB);
        }  else if (nodeB->getTag() == SAVE_TAG && nodeA->getTag() == RACKET_TAG) {
            
            CCLOG(">>> %d", this->level);
            
            Size visibleSize = Director::getInstance()->getVisibleSize();
            // Salva o nivel!
            UserDefault *userdata = UserDefault::getInstance();
            userdata->setIntegerForKey("level", this->level);
            userdata->setDoubleForKey("time", (double) time(NULL));
            userdata->flush();
            nodeB->removeFromParentAndCleanup(true);
            auto text = Label::createWithTTF("Level Saved!", "fonts/Marker Felt.ttf", 40);
            text->setPosition(visibleSize.width/2, visibleSize.height/2);
            this->addChild(text);
            text->runAction(FadeOut::create(3));
        } else if (nodeB->getTag() == THREE_BALLS_TAG && nodeA->getTag() == RACKET_TAG) {

            Size visibleSize = Director::getInstance()->getVisibleSize();
            // Salva na "database" o número de bolas na tela
            UserDefault *userdata = UserDefault::getInstance();
            int ball_count = userdata->getIntegerForKey("ball_count");
            userdata->setIntegerForKey("ball_count", ball_count * 3);
            userdata->flush();
            nodeB->removeFromParentAndCleanup(true);
            auto text = Label::createWithTTF("Triple Balls!", "fonts/Marker Felt.ttf", 40);
            text->setPosition(visibleSize.width/2, visibleSize.height/2 + 2);
            this->addChild(text);
            text->runAction(FadeOut::create(3));
            // cria as bolas e as adiciona no cenário
            for (int i = ball_count; i < (ball_count * 3); i++) {
                auto ball = Ball::create();
                ball->setPosition(nodeB->getPositionX(), RAQUETE_ALTURA+BALL_SIZE);
                ball->throwBall();
                this->addChild(ball, 21);
            }
        } else if (nodeB->getTag() == SAVE_TAG && nodeA->getTag() == BOTTOM_TAG) {
            nodeB->removeFromParentAndCleanup(true);
        } else if (nodeB->getTag() == THREE_BALLS_TAG && nodeA->getTag() == BOTTOM_TAG) {
            nodeB->removeFromParentAndCleanup(true);
        }

    }
    return true;
}

void GameScene::ball_collision (Node *ball) {
    UserDefault *userdata = UserDefault::getInstance();
    int ball_count = userdata->getIntegerForKey("ball_count");

    if (ball_count <= 1) {
        this->over = true;
        auto visibleSize = Director::getInstance()->getVisibleSize();
        auto text = Label::createWithTTF("Game Over...", "fonts/Marker Felt.ttf", 40);
        text->setPosition(visibleSize.width/2, visibleSize.height/2);
        this->addChild(text);
        
        UserDefault *userdata = UserDefault::getInstance();
        level = userdata->getIntegerForKey("level", 0);
        auto menu_item_start = MenuItemFont::create("Restart", CC_CALLBACK_1(GameScene::NextLevel, this));
        menu_item_start->setPosition(text->getPosition());
        menu_item_start->setPositionY(menu_item_start->getPositionY()-50);
        auto *menu = Menu::create(menu_item_start, NULL);
        menu->setPosition(Point(0, 0));
        this->addChild(menu, 30);
        
        auto audio = CocosDenshion::SimpleAudioEngine::getInstance();
        audio->playEffect("bomb.wav");
        ParticleSun* m_emitter = ParticleSun::create();
        m_emitter->setPosition(ball->getPosition());
        m_emitter->setDuration(1);
        this->getScene()->addChild(m_emitter);
        ball->removeFromParentAndCleanup(true);
    }
    else {           
        // Diminui o contador de bolas do jogo
        userdata->setIntegerForKey("ball_count", (ball_count-1));
        userdata->flush();

        // Destrói a bola que tocou o fundo
        auto audio = CocosDenshion::SimpleAudioEngine::getInstance();
        audio->playEffect("bomb.wav");
        ParticleSun* m_emitter = ParticleSun::create();
        m_emitter->setPosition(ball->getPosition());
        m_emitter->setDuration(1);
        this->getScene()->addChild(m_emitter);
        ball->removeFromParentAndCleanup(true);
    }
}

void GameScene::ball_core (Node *core, Node *ball) {
    auto scaleBy = ScaleBy::create(1.0f, 20.0f);
    core->getPhysicsBody()->setEnabled(false);
    core->runAction(scaleBy);
    auto audio = CocosDenshion::SimpleAudioEngine::getInstance();
    audio->playEffect("win.wav");
    
    auto callbackRotate = CallFunc::create([=](){
        auto visibleSize = Director::getInstance()->getVisibleSize();
        auto menu_item_start = MenuItemFont::create("Next Level", CC_CALLBACK_1(GameScene::NextLevel, this));
        menu_item_start->setPosition(Point(visibleSize.width / 2, (visibleSize.height / 2)));
        auto *menu = Menu::create(menu_item_start, NULL);
        menu->setPosition(Point(0, 0));
        this->addChild(menu, 30);
    });
    
    // create a sequence with the actions and callbacks
    auto seq = Sequence::create(scaleBy, callbackRotate, nullptr);
    core->runAction(seq);
    ball->removeFromParentAndCleanup(true);
}

void GameScene::NextLevel(Ref *pSender) {
    auto scene = GameScene::createScene(this->level + 1);
    Director::getInstance()->replaceScene(scene);
    //Director::getInstance()->end();
}

void GameScene::setLevel(int level) {
    this->level = level;
    char level_text[256];
    sprintf(level_text,"Level %d", this->level);
    auto text = Label::createWithTTF(level_text, "fonts/Marker Felt.ttf", 30);
    text->setAnchorPoint(Vec2());
    text->setPosition(10, 10);
    this->addChild(text);
}

// Power-up: salva o nível atual
void GameScene::saveLevel() {
    if (rand_0_1() < DROP_LEVEL_SAVE && this->over == false){
        Size visibleSize = Director::getInstance()->getVisibleSize();
        auto ball_draw = DrawNode::create();
        ball_draw->drawDot(Vec2(0, 0), BALL_SIZE/3.0, Color4F(COLOR_green));
    
        auto material = PHYSICSBODY_MATERIAL_DEFAULT;
        material.density = 0.0f;
        material.restitution = 1.0f;
        material.friction = 0.0f; //set friction here
    
        auto physicsBody = PhysicsBody::createCircle(BALL_SIZE/3.0, material);
        physicsBody->setGravityEnable(true);
        physicsBody->setVelocity(Vec2(0,0));
        physicsBody->setLinearDamping(0.0);
        physicsBody->setMass(1.0f);
        physicsBody->setContactTestBitmask(0xFFFFFFFF);
        physicsBody->setGroup(-1);
        ball_draw->addComponent(physicsBody);
        ball_draw->setTag(SAVE_TAG);
        
        ball_draw->setPosition(visibleSize.width * rand_0_1(), visibleSize.height);
        this->addChild(ball_draw);
    }
    
    auto delay = DelayTime::create(rand_0_1()*10.0+3.0);
    CallFunc *runCallback = CallFunc::create(CC_CALLBACK_0(GameScene::saveLevel, this));
    auto seq = Sequence::create(delay, runCallback, nullptr);
    //this->runAction(seq);
    
}

// Power-up: triplica o número de bolas na tela 
void GameScene::tripleBalls() {
    if (rand_0_1() < DROP_TRIPLE_BALL && this->over == false){
            Size visibleSize = Director::getInstance()->getVisibleSize();
            auto ball_draw = DrawNode::create();
            ball_draw->drawDot(Vec2(0, 0), BALL_SIZE/3.0, Color4F(COLOR_pink));
        
            auto material = PHYSICSBODY_MATERIAL_DEFAULT;
            material.density = 0.0f;
            material.restitution = 1.0f;
            material.friction = 0.0f;
        
            auto physicsBody = PhysicsBody::createCircle(BALL_SIZE/3.0, material);
            physicsBody->setGravityEnable(true);
            physicsBody->setVelocity(Vec2(0,0));
            physicsBody->setLinearDamping(0.0);
            physicsBody->setMass(1.0f);
            physicsBody->setContactTestBitmask(0xFFFFFFFF);
            physicsBody->setGroup(-1);
            ball_draw->addComponent(physicsBody);
            ball_draw->setTag(THREE_BALLS_TAG);
            
            ball_draw->setPosition(visibleSize.width * rand_0_1(), visibleSize.height);
            this->addChild(ball_draw);
    }
    
    auto delay = DelayTime::create(rand_0_1()*50.0+3.0);
    CallFunc *runCallback = CallFunc::create(CC_CALLBACK_0(GameScene::tripleBalls, this));
    auto seq = Sequence::create(delay, runCallback, nullptr);
    this->runAction(seq);
}

