require "cocos.3d.3dConstants"

local size = cc.Director:getInstance():getWinSize()
local scheduler = cc.Director:getInstance():getScheduler()
local attributeNames = 
{
    "a_position",
    "a_color",
    "a_texCoord",
    "a_texCoord1",
    "a_texCoord2",
    "a_texCoord3",
    "a_texCoord4",
    "a_texCoord5",
    "a_texCoord6",
    "a_texCoord7",
    "a_normal",
    "a_blendWeight",
    "a_blendIndex",
}

----------------------------------------
----Sprite3DBasicTest
----------------------------------------

local Sprite3DBasicTest = {}
Sprite3DBasicTest.__index = Sprite3DBasicTest

function Sprite3DBasicTest.onTouchesEnd(touches, event)
    for i = 1,table.getn(touches) do
        local location = touches[i]:getLocation()
        Sprite3DBasicTest.addNewSpriteWithCoords(Helper.currentLayer, location.x, location.y )
    end
end

function Sprite3DBasicTest.addNewSpriteWithCoords(parent,x,y)
    local sprite = cc.Sprite3D:create("Sprite3DTest/boss1.obj")
    sprite:setScale(3.0)
    sprite:setTexture("Sprite3DTest/boss.png")

    parent:addChild(sprite)
    sprite:setPosition(cc.p(x,y))

    local random = math.random()
    local action = nil
    if random < 0.2 then
        action = cc.ScaleBy:create(3,2)
    elseif random < 0.4 then
        action = cc.RotateBy:create(3, 360)
    elseif random < 0.6 then
        action = cc.Blink:create(1, 3)
    elseif random < 0.8 then
        action = cc.TintBy:create(2, 0, -255, -255)
    else
        action  = cc.FadeOut:create(2)
    end

    local action_back = action:reverse()
    local seq = cc.Sequence:create(action, action_back)

    sprite:runAction(cc.RepeatForever:create(seq))
end

function Sprite3DBasicTest.create()
    local layer = cc.Layer:create()
    Helper.initWithLayer(layer)
    Helper.titleLabel:setString("Testing Sprite3D")
    Helper.subtitleLabel:setString("Tap screen to add more sprites")

    local listener = cc.EventListenerTouchAllAtOnce:create()
    listener:registerScriptHandler(Sprite3DBasicTest.onTouchesEnd,cc.Handler.EVENT_TOUCHES_ENDED )

    local eventDispatcher = layer:getEventDispatcher()
    eventDispatcher:addEventListenerWithSceneGraphPriority(listener, layer)

    Sprite3DBasicTest.addNewSpriteWithCoords(layer, size.width / 2, size.height / 2)
    return layer
end

----------------------------------------
----Sprite3DHitTest
----------------------------------------
local Sprite3DHitTest = {}
Sprite3DHitTest.__index = Sprite3DHitTest

function Sprite3DHitTest.create()
    local layer = cc.Layer:create()
    Helper.initWithLayer(layer)
    Helper.titleLabel:setString("Testing Sprite3D Touch in 2D")
    Helper.subtitleLabel:setString("Tap Sprite3D and Drag")

    local sprite1 = cc.Sprite3D:create("Sprite3DTest/boss1.obj")
    sprite1:setScale(4.0)
    sprite1:setTexture("Sprite3DTest/boss.png")
    sprite1:setPosition( cc.p(size.width/2, size.height/2) )
    sprite1:runAction(cc.RepeatForever:create(cc.RotateBy:create(3, 360)))
    layer:addChild(sprite1)

    local sprite2 = cc.Sprite3D:create("Sprite3DTest/boss1.obj")
    sprite2:setScale(4.0)
    sprite2:setTexture("Sprite3DTest/boss.png")
    sprite2:setPosition( cc.p(size.width/2, size.height/2) )
    sprite2:setAnchorPoint(cc.p(0.5, 0.5))
    sprite2:runAction(cc.RepeatForever:create(cc.RotateBy:create(3, -360)))
    layer:addChild(sprite2)

    local listener = cc.EventListenerTouchOneByOne:create()
    listener:setSwallowTouches(true)
    listener:registerScriptHandler(function (touch, event)
        local target = event:getCurrentTarget()
        local rect   = target:getBoundingBox()
        if cc.rectContainsPoint(rect, touch:getLocation()) then
            print(string.format("sprite3d began... x = %f, y = %f", touch:getLocation().x, touch:getLocation().y))
            target:setOpacity(100)
            return true
        end

        return false
    end,cc.Handler.EVENT_TOUCH_BEGAN )

    listener:registerScriptHandler(function (touch, event)
        local target = event:getCurrentTarget()
        local x,y = target:getPosition()
        target:setPosition(cc.p(x + touch:getDelta().x, y + touch:getDelta().y))
    end, cc.Handler.EVENT_TOUCH_MOVED)

    listener:registerScriptHandler(function (touch, event)
        local target = event:getCurrentTarget()
        print("sprite3d onTouchEnd")
        target:setOpacity(255)
    end, cc.Handler.EVENT_TOUCH_ENDED)

    local eventDispatcher = layer:getEventDispatcher()
    eventDispatcher:addEventListenerWithSceneGraphPriority(listener, sprite1)
    eventDispatcher:addEventListenerWithSceneGraphPriority(listener:clone(), sprite2)

    return layer
end

----------------------------------------
----Sprite3DWithSkinTest
----------------------------------------
local Sprite3DWithSkinTest = {}
Sprite3DWithSkinTest.__index = Sprite3DWithSkinTest
Sprite3DWithSkinTest._animateQuality = cc.Animate3DQuality.QUALITY_HIGH
Sprite3DWithSkinTest._sprites = {}

function Sprite3DWithSkinTest.onTouchesEnd(touches, event)
    for i = 1,table.getn(touches) do
        local location = touches[i]:getLocation()
        Sprite3DWithSkinTest.addNewSpriteWithCoords(Helper.currentLayer, location.x, location.y )
    end
end

function Sprite3DWithSkinTest.addNewSpriteWithCoords(parent,x,y)
    local sprite = cc.Sprite3D:create("Sprite3DTest/orc.c3b")
    sprite:setScale(3)
    sprite:setRotation3D({x = 0, y = 180, z = 0})
    sprite:setPosition(cc.p(x, y))
    parent:addChild(sprite)
    table.insert(Sprite3DWithSkinTest._sprites, sprite)

    local animation = cc.Animation3D:create("Sprite3DTest/orc.c3b")
    if nil ~= animation then
        local animate = cc.Animate3D:create(animation)
        local inverse = false
        if math.random() == 0 then
            inverse = true
        end

        local rand2 = math.random()
        local speed = 1.0

        if rand2 < 1/3 then
            speed = animate:getSpeed() + math.random()  
        elseif rand2 < 2/3 then
            speed = animate:getSpeed() - 0.5 *  math.random()
        end

        if inverse then
            animate:setSpeed(-speed)
        else
            animate:setSpeed(speed)
        end
        animate:setTag(110)
        animate:setQuality(Sprite3DWithSkinTest._animateQuality)
        local repeatAction = cc.RepeatForever:create(animate)
        repeatAction:setTag(110)
        sprite:runAction(repeatAction)
    end
end

function Sprite3DWithSkinTest.create()
    local layer = cc.Layer:create()
    Helper.initWithLayer(layer)
    Helper.titleLabel:setString("Testing Sprite3D for animation from c3t")
    Helper.subtitleLabel:setString("Tap screen to add more sprite3D")

    local listener = cc.EventListenerTouchAllAtOnce:create()
    listener:registerScriptHandler(Sprite3DWithSkinTest.onTouchesEnd,cc.Handler.EVENT_TOUCHES_ENDED )

    local eventDispatcher = layer:getEventDispatcher()
    eventDispatcher:addEventListenerWithSceneGraphPriority(listener, layer)
    Sprite3DWithSkinTest._sprites = {}
    Sprite3DWithSkinTest.addNewSpriteWithCoords(layer, size.width / 2, size.height / 2)
    
    cc.MenuItemFont:setFontName("fonts/arial.ttf")
    cc.MenuItemFont:setFontSize(15)
    local menuItem =  cc.MenuItemFont:create("High Quality")
    Sprite3DWithSkinTest._animateQuality = cc.Animate3DQuality.QUALITY_HIGH
    menuItem:registerScriptTapHandler(function(tag, sender)
        Sprite3DWithSkinTest._animateQuality  = Sprite3DWithSkinTest._animateQuality  + 1

        if Sprite3DWithSkinTest._animateQuality > cc.Animate3DQuality.QUALITY_HIGH then
            Sprite3DWithSkinTest._animateQuality = cc.Animate3DQuality.QUALITY_NONE
        end

        if Sprite3DWithSkinTest._animateQuality == cc.Animate3DQuality.QUALITY_NONE then
            menuItem:setString("None Quality")
        elseif Sprite3DWithSkinTest._animateQuality == cc.Animate3DQuality.QUALITY_LOW then
            menuItem:setString("Low Quality")
        elseif Sprite3DWithSkinTest._animateQuality == cc.Animate3DQuality.QUALITY_HIGH then
            menuItem:setString("High Quality")
        end

        for i,spriteIter in ipairs(Sprite3DWithSkinTest._sprites) do
            local repAction = spriteIter:getActionByTag(110)
            local animate3D = repAction:getInnerAction()
            animate3D:setQuality(Sprite3DWithSkinTest._animateQuality)
        end
    end)
    local menu = cc.Menu:create(menuItem)
    menu:setPosition(cc.p(0.0, 0.0))
    menuItem:setPosition(VisibleRect:left().x + 50, VisibleRect:top().y -70)
    layer:addChild(menu, 1)

    return layer
end


----------------------------------------
----Animate3DTest
----------------------------------------
local State = 
{
    SWIMMING = 0,
    SWIMMING_TO_HURT = 1,
    HURT = 2,
    HURT_TO_SWIMMING = 3,
}

local Animate3DTest = {}
Animate3DTest.__index = Animate3DTest

function Animate3DTest.extend(target)
    local t = tolua.getpeer(target)
    if not t then
        t = {}
        tolua.setpeer(target, t)
    end
    setmetatable(t, Animate3DTest)
    return target
end



function Animate3DTest:onEnter()

    self._hurt = nil
    self._swim = nil
    self._sprite = nil
    self._moveAction = nil
    self._transTime = 0.1
    self._elapseTransTime = 0.0

    local function renewCallBack()
        self._sprite:stopActionByTag(101)
        self._state = State.HURT_TO_SWIMMING
    end
    local function onTouchesEnd(touches, event )
        for i = 1,table.getn(touches) do
            local location = touches[i]:getLocation()
            if self._sprite ~= nil then
                local len = cc.pGetLength(cc.pSub(cc.p(self._sprite:getPosition()), location))
                if len < 40 then
                    if self._state == State.SWIMMING then
                        self._sprite:runAction(self._hurt)
                        local delay = cc.DelayTime:create(self._hurt:getDuration() - 0.1)
                        local seq = cc.Sequence:create(delay, cc.CallFunc:create(renewCallBack))
                        seq:setTag(101)
                        self._sprite:runAction(seq)
                        self._state = State.SWIMMING_TO_HURT
                    end
                    return
                end
            end
        end  
    end

    self:addSprite3D()

    local listener = cc.EventListenerTouchAllAtOnce:create()
    listener:registerScriptHandler(onTouchesEnd,cc.Handler.EVENT_TOUCHES_ENDED )

    local eventDispatcher = self:getEventDispatcher()
    eventDispatcher:addEventListenerWithSceneGraphPriority(listener, self)

    local function update(dt)
        if self._state == State.HURT_TO_SWIMMING then
            self._elapseTransTime = self._elapseTransTime + dt
            local t = self._elapseTransTime / self._transTime
            
            if t >= 1.0 then
                t = 1.0
                self._sprite:stopAction(self._hurt)
                self._state = State.SWIMMING
            end
            self._swim:setWeight(t)
            self._hurt:setWeight(1.0 - t)
        elseif self._state == State.SWIMMING_TO_HURT then
            self._elapseTransTime = self._elapseTransTime + dt
            local t = self._elapseTransTime / self._transTime
            if t >= 1.0 then
                t = 1.0
                self._state = State.HURT
            end
            self._swim:setWeight(1.0 - t)
            self._hurt:setWeight(t)
        end
    end

    self:scheduleUpdateWithPriorityLua(update,0)
end

function Animate3DTest:onExit()
    self._moveAction:release()
    self._hurt:release()
    self._swim:release()
    self:unscheduleUpdate()
end

function Animate3DTest:addSprite3D()
    -- body
    local fileName = "Sprite3DTest/tortoise.c3b"
    local sprite = cc.Sprite3D:create(fileName)
    sprite:setScale(0.1)
    local winSize = cc.Director:getInstance():getWinSize()
    sprite:setPosition(cc.p(winSize.width * 4.0 / 5.0, winSize.height / 2.0))
    self:addChild(sprite)

    self._sprite = sprite

    local animation = cc.Animation3D:create(fileName)
    if nil ~= animation then
        local animate = cc.Animate3D:create(animation, 0.0, 1.933)
        sprite:runAction(cc.RepeatForever:create(animate))
        self._swim = animate
        self._swim:retain()
        self._hurt = cc.Animate3D:create(animation, 1.933, 2.8)
        self._hurt:retain()
        self._state = State.SWIMMING
    end

    self._moveAction = cc.MoveTo:create(4.0, cc.p(winSize.width / 5.0, winSize.height / 2.0))
    self._moveAction:retain()

    local function reachEndCallBack()
        local winSize = cc.Director:getInstance():getWinSize()
        self._sprite:stopActionByTag(100)
        local inverse = cc.MoveTo:create(4.0, cc.p(winSize.width - self._sprite:getPositionX(), winSize.height / 2.0))
        inverse:retain()
        self._moveAction:release()
        self._moveAction = inverse
        local rot = cc.RotateBy:create(1.0, { x = 0.0, y = 180.0, z = 0.0})
        local seq = cc.Sequence:create(rot, self._moveAction, cc.CallFunc:create(reachEndCallBack))
        seq:setTag(100)
        self._sprite:runAction(seq)
    end

    local seq = cc.Sequence:create(self._moveAction, cc.CallFunc:create(reachEndCallBack))
    seq:setTag(100)
    sprite:runAction(seq)
end

function Animate3DTest.create()
    local layer =  Animate3DTest.extend(cc.Layer:create())

    if nil ~= layer then
        Helper.initWithLayer(layer)
        Helper.titleLabel:setString("Testing Animate3D")
        Helper.subtitleLabel:setString("Touch to beat the tortoise")

        local function onNodeEvent(event)
            if "enter" == event then
                layer:onEnter()
            elseif "exit" == event then
                layer:onExit()
            end
        end
        layer:registerScriptHandler(onNodeEvent)
    end

    return layer
end

----------------------------------------
----AttachmentTest
----------------------------------------
local AttachmentTest = {}
AttachmentTest.__index = AttachmentTest

function AttachmentTest.create()
    local layer = cc.Layer:create()
    Helper.initWithLayer(layer)
    Helper.titleLabel:setString("Testing Sprite3D Attachment")
    Helper.subtitleLabel:setString("touch to switch weapon")

    local _sprite = nil
    local _hasWeapon = nil

    local function addNewSpriteWithCoords(pos)
        local fileName = "Sprite3DTest/orc.c3b"
        local sprite = cc.Sprite3D:create(fileName)
        sprite:setScale(5)
        sprite:setRotation3D({x = 0, y =180, z = 0})
        layer:addChild(sprite)
        sprite:setPosition( cc.p( pos.x, pos.y) )
    
        --test attach
        local sp = cc.Sprite3D:create("Sprite3DTest/axe.c3b")
        sprite:getAttachNode("Bip001 R Hand"):addChild(sp)
    
        local animation = cc.Animation3D:create(fileName)
        if nil ~=animation then
            local animate = cc.Animate3D:create(animation)
            sprite:runAction(cc.RepeatForever:create(animate))
        end 

        _sprite = sprite
        _hasWeapon = true
    end

    addNewSpriteWithCoords(cc.p(size.width / 2, size.height / 2))

    local listener = cc.EventListenerTouchAllAtOnce:create()
    listener:registerScriptHandler(function (touches, event)
        if _hasWeapon then
            _sprite:removeAllAttachNode()
        else
            local sp = cc.Sprite3D:create("Sprite3DTest/axe.c3b")
            _sprite:getAttachNode("Bip001 R Hand"):addChild(sp)
        end

        _hasWeapon = not _hasWeapon
    end,cc.Handler.EVENT_TOUCHES_ENDED)

    local eventDispatcher = layer:getEventDispatcher()
    eventDispatcher:addEventListenerWithSceneGraphPriority(listener, layer)

    return layer
end

----------------------------------------
----Sprite3DReskinTest
----------------------------------------
local Sprite3DReskinTest = {}
Sprite3DReskinTest.__index = Sprite3DReskinTest

function Sprite3DReskinTest.extend(target)
    local t = tolua.getpeer(target)
    if not t then
        t = {}
        tolua.setpeer(target, t)
    end
    setmetatable(t, Sprite3DReskinTest)
    return target
end

function Sprite3DReskinTest:init()
    self._girlPants = {}
    self._usePantsId = 0
    self._girlUpperBody = {}
    self._useUpBodyId = 0
    self._girlShoes = {}
    self._useShoesId = 0
    self._girlHair   = {}
    self._useHairId  = 0
    self._sprite     = nil 

    self:addNewSpriteWithCoords(cc.p(size.width / 2, size.height / 2))

    local listener = cc.EventListenerTouchAllAtOnce:create()
    listener:registerScriptHandler(function (touches, event)
        
    end,cc.Handler.EVENT_TOUCHES_ENDED)

    local eventDispatcher = self:getEventDispatcher()
    eventDispatcher:addEventListenerWithSceneGraphPriority(listener, self)

    local ttfConfig = {}
    ttfConfig.fontFilePath = "fonts/arial.ttf"
    ttfConfig.fontSize = 20

    local label1 = cc.Label:createWithTTF(ttfConfig,"Hair")
    local item1  = cc.MenuItemLabel:create(label1)
    item1:registerScriptTapHandler(function (tag, sender ) -- swithHair
        self._useHairId = self._useHairId + 1
        if self._useHairId > 1 then
            self._useHairId = 0
        end

        if self._useHairId >= 0 and self._sprite ~= nil then
            for i=1,2 do
                local subMesh = self._sprite:getMeshByName(self._girlHair[i])
                if nil ~= subMesh then
                    if (i - 1) == self._useHairId then
                        subMesh:setVisible(true)
                    else
                        subMesh:setVisible(false)
                    end
                end
            end
        end
    end)
    local label2 = cc.Label:createWithTTF(ttfConfig,"Glasses")
    local item2 = cc.MenuItemLabel:create(label2)
    item2:registerScriptTapHandler(function (tag, sender ) -- switchGlasses
        local subMesh = self._sprite:getMeshByName("Girl_Glasses01")
        if nil ~= subMesh then
            if subMesh:isVisible() then
                subMesh:setVisible(false)
            else
                subMesh:setVisible(true)
            end
        end
    end)

    local label3 = cc.Label:createWithTTF(ttfConfig,"Coat")
    local item3 = cc.MenuItemLabel:create(label3)
    item3:registerScriptTapHandler(function (tag, sender ) -- switchCoat
        self._useUpBodyId = self._useUpBodyId + 1
        if self._useUpBodyId > 1 then
            self._useUpBodyId = 0
        end
        if self._useUpBodyId >= 0  and nil ~= self._sprite then
            for i=1,2 do
                local subMesh = self._sprite:getMeshByName(self._girlUpperBody[i])
                if nil ~=subMesh then
                    if (i - 1) == self._useUpBodyId then
                        subMesh:setVisible(true)
                    else
                        subMesh:setVisible(false)
                    end
                end
            end
        end
    end)

    local label4 = cc.Label:createWithTTF(ttfConfig,"Pants")
    local item4 = cc.MenuItemLabel:create(label4)
    item4:registerScriptTapHandler(function (tag, sender ) -- switchPants

        self._usePantsId = self._usePantsId + 1
        if self._usePantsId > 1 then
            self._usePantsId = 0
        end

        if self._usePantsId >= 0  and nil ~= self._sprite then
            for i=1,2 do
                local subMesh = self._sprite:getMeshByName(self._girlPants[i])
                if nil ~= subMesh then
                    if (i - 1) == self._usePantsId then
                        subMesh:setVisible(true)
                    else
                        subMesh:setVisible(false)
                    end
                end
            end
        end
    end)

    local label5 = cc.Label:createWithTTF(ttfConfig,"Shoes")
    local item5 = cc.MenuItemLabel:create(label5)
    item5:registerScriptTapHandler(function (tag, sender ) -- switchShoes

        self._useShoesId = self._useShoesId + 1
        if self._useShoesId > 1 then
            self._useShoesId = 0
        end

        if self._useShoesId >= 0  and nil ~= self._sprite then
            for i=1,2 do
                local subMesh = self._sprite:getMeshByName(self._girlShoes[i])
                if nil ~= subMesh then
                    if (i - 1) == self._useShoesId then
                        subMesh:setVisible(true)
                    else
                        subMesh:setVisible(false)
                    end
                end
            end
        end
    end)

    item1:setPosition( cc.p(VisibleRect:left().x+50, VisibleRect:bottom().y+item1:getContentSize().height*4 ) )
    item2:setPosition( cc.p(VisibleRect:left().x+50, VisibleRect:bottom().y+item1:getContentSize().height *5 ) )
    item3:setPosition( cc.p(VisibleRect:left().x+50, VisibleRect:bottom().y+item1:getContentSize().height*6 ) )
    item4:setPosition( cc.p(VisibleRect:left().x+50, VisibleRect:bottom().y+item1:getContentSize().height *7 ) )
    item5:setPosition( cc.p(VisibleRect:left().x+50, VisibleRect:bottom().y+item1:getContentSize().height *8 ) )
    local menu = cc.Menu:create(item1,item2,item3,item4,item5)
    menu:setPosition(cc.p(0,0))
    self:addChild(menu, 10)
end


function Sprite3DReskinTest:addNewSpriteWithCoords(pos)
    self._girlPants = {"Girl_LowerBody01", "Girl_LowerBody02"}
    self._girlUpperBody = {"Girl_UpperBody01", "Girl_UpperBody02"}
    self._girlShoes  = {"Girl_Shoes01", "Girl_Shoes02"}
    self._girlHair  = {"Girl_Hair01", "Girl_Hair02"}
    self._usePantsId = 0
    self._useUpBodyId = 0
    self._useShoesId   =0
    self._useHairId = 0

    local fileName = "Sprite3DTest/ReskinGirl.c3b"
    local sprite = cc.Sprite3D:create(fileName)
    sprite:setScale(4)
    sprite:setRotation3D({x = 0, y =0 ,z = 0})
    local girlPants = sprite:getMeshByName(self._girlPants[2])
    if nil ~= girlPants then
        girlPants:setVisible(false)
    end

    local girlShoes = sprite:getMeshByName(self._girlShoes[2])
    if nil ~= girlShoes then
        girlShoes:setVisible(false)
    end

    local girlHair = sprite:getMeshByName(self._girlHair[2])
    if nil ~= girlHair then
        girlHair:setVisible(false)
    end

    local girlUpBody = sprite:getMeshByName( self._girlUpperBody[2])
    if nil ~=girlUpBody then
        girlUpBody:setVisible(false)
    end

    self:addChild(sprite)
    sprite:setPosition( cc.p( pos.x, pos.y-60) )
    local animation = cc.Animation3D:create(fileName)
    if nil ~= animation then
        local animate = cc.Animate3D:create(animation)
        sprite:runAction(cc.RepeatForever:create(animate))
    end
    self._sprite = sprite
end

function Sprite3DReskinTest.create()
    local layer =  Sprite3DReskinTest.extend(cc.Layer:create())

    if nil ~= layer then
        Helper.initWithLayer(layer)
        Helper.titleLabel:setString("Testing Sprite3D Reskin")
        layer:init()
    end

    return layer
end

----------------------------------------
----Sprite3DWithOBBPerfromanceTest
----------------------------------------
local Sprite3DWithOBBPerfromanceTest = class("Sprite3DWithOBBPerfromanceTest",function()
    return cc.Layer:create()
end)

function Sprite3DWithOBBPerfromanceTest:ctor()
    self._obb = {}
    local listener = cc.EventListenerTouchAllAtOnce:create()
    listener:registerScriptHandler(function (touches, event)
        for i,touch in ipairs(touches) do
            local location = touch:getLocationInView()
            if nil ~= self._obb and #self._obb > 0 then
                self._intersetList = {}
                local ray = cc.Ray:new()
                self:calculateRayByLocationInView(ray, location)

                for idx,value in ipairs(self._obb) do
                    if ray:intersects(value) then
                        table.insert(self._intersetList, idx)
                        return
                    end
                end
            end
        end
    end,cc.Handler.EVENT_TOUCHES_BEGAN)

    listener:registerScriptHandler(function (touches, event)
        
    end,cc.Handler.EVENT_TOUCHES_ENDED)

    listener:registerScriptHandler(function (touches, event)
        for i,touch in ipairs(touches) do
            local location = touch:getLocation()

            for idx,value in ipairs(self._obb) do
                for lstIdx,lstValue in ipairs(self._intersetList) do
                    if idx == lstValue then
                        self._obb[idx]._center = cc.vec3(location.x,location.y,0)
                    end
                end
            end
        end
    end,cc.Handler.EVENT_TOUCHES_MOVED)

    local eventDispatcher = self:getEventDispatcher()
    eventDispatcher:addEventListenerWithSceneGraphPriority(listener, self)

    local s = cc.Director:getInstance():getWinSize()
    self:initDrawBox()

    self:addNewSpriteWithCoords(cc.p(s.width/2, s.height/2))

    cc.MenuItemFont:setFontName("fonts/arial.ttf")
    cc.MenuItemFont:setFontSize(65)

    local decrease =  cc.MenuItemFont:create(" - ")
    decrease:registerScriptTapHandler(function(tag, sender)
        self:delOBBWithCount(10)
    end)
    decrease:setColor(cc.c3b(0, 200, 20))

    local increase =  cc.MenuItemFont:create(" + ")
    increase:registerScriptTapHandler(function(tag, sender)
        self:addOBBWithCount(10)
    end)
    increase:setColor(cc.c3b(0, 200, 20))

    local menu = cc.Menu:create(decrease, increase)
    menu:alignItemsHorizontally()
    menu:setPosition(cc.p(s.width/2, s.height - 90))
    self:addChild(menu, 1)

    local ttfConfig = {}
    ttfConfig.fontFilePath = "fonts/Marker Felt.ttf"
    ttfConfig.fontSize = 30
    self._labelCubeCount = cc.Label:createWithTTF(ttfConfig,"0 cubes")
    self._labelCubeCount:setColor(cc.c3b(0,200,20))
    self._labelCubeCount:setPosition(cc.p(s.width/2, s.height-90))
    self:addChild(self._labelCubeCount)

    self:addOBBWithCount(10)
    self:scheduleUpdateWithPriorityLua(function(dt)
        self._labelCubeCount:setString(string.format("%u cubes", #self._obb))
        if nil ~= self._drawDebug then
            self._drawDebug:clear()
            local mat = self._sprite:getNodeToWorldTransform()

            self._obbt._xAxis = cc.vec3(mat[1], mat[2], mat[3])
            self._obbt._xAxis = cc.vec3normalize(self._obbt._xAxis)

            self._obbt._yAxis = cc.vec3(mat[5], mat[6], mat[7])
            self._obbt._yAxis = cc.vec3normalize(self._obbt._yAxis)

            self._obbt._zAxis = cc.vec3(-mat[9], -mat[10], -mat[11])
            self._obbt._zAxis = cc.vec3normalize(self._obbt._zAxis)

            self._obbt._center = self._sprite:getPosition3D()

            local corners = {}
            for i=1,8 do
                corners[i] = {}
            end
            corners = self._obbt:getCorners(corners)
            self._drawDebug:drawCube(corners, cc.c4f(0, 0, 1, 1))
        end

        if #self._obb > 0 then
            self._drawOBB:clear()

            for i= 1, #self._obb do
                local corners = {}
                for i=1,8 do
                    corners[i] = {}
                end

                corners = self._obb[i]:getCorners(corners)
                if self._obbt:intersects(self._obb[i]) then
                    self._drawOBB:drawCube(corners, cc.c4f(1, 0, 0, 1))
                else
                    self._drawOBB:drawCube(corners, cc.c4f(0, 1, 0, 1))
                end

            end
        end
    end, 0)
end

function Sprite3DWithOBBPerfromanceTest:addOBBWithCount( value )
    for i=1,value do
        local randompos = cc.p(math.random() * cc.Director:getInstance():getWinSize().width, math.random() * cc.Director:getInstance():getWinSize().height)
        local extents = cc.vec3(10, 10, 10)
        local aabb = cc.AABB:new({x = -10, y = -10, z = -10}, extents)
        local obb = cc.OBB:new(aabb)
        obb._center = cc.vec3(randompos.x,randompos.y,0)
        table.insert(self._obb, obb)
    end
end

function Sprite3DWithOBBPerfromanceTest:delOBBWithCount( value )
    if #self._obb >= 10 then
        for i= 1, 10 do
            table.remove(self._obb)
        end 
        self._drawOBB:clear()
    end 
end

function Sprite3DWithOBBPerfromanceTest:initDrawBox()
    self._drawOBB = cc.DrawNode3D:create()
    self:addChild(self._drawOBB )
end

function Sprite3DWithOBBPerfromanceTest:unproject( viewProjection, viewport, src, dst)
    assert(viewport.width ~= 0.0 and viewport.height ~= 0)
    local screen = cc.vec4(src.x / viewport.width, (viewport.height - src.y) / viewport.height, src.z, 1.0)
    screen.x = screen.x * 2.0 - 1.0
    screen.y = screen.y * 2.0 - 1.0
    screen.z = screen.z * 2.0 - 1.0
    local inversed = cc.mat4.new(viewProjection:getInversed())
    screen = inversed:transformVector(screen)
    if screen.w ~= 0.0 then
        screen.x = screen.x / screen.w
        screen.y = screen.y / screen.w
        screen.z = screen.z / screen.w
    end

    dst.x = screen.x
    dst.y = screen.y
    dst.z = screen.z
    return viewport, src, dst
end

function Sprite3DWithOBBPerfromanceTest:calculateRayByLocationInView(ray, location)
    local dir = cc.Director:getInstance()
    local view = dir:getWinSize()
    local mat = cc.mat4.new(dir:getMatrix(cc.MATRIX_STACK_TYPE.PROJECTION))
    local src = cc.vec3(location.x, location.y, -1)
    local nearPoint = {}
    view, src, nearPoint = self:unproject(mat, view, src, nearPoint)
    src = cc.vec3(location.x, location.y, 1)
    local farPoint = {}
    view, src, farPoint = self:unproject(mat, view, src, farPoint)
    local direction = {}
    direction.x = farPoint.x - nearPoint.x
    direction.y = farPoint.y - nearPoint.y
    direction.z = farPoint.z - nearPoint.z
    direction   = cc.vec3normalize(direction)
    
    ray._origin    = nearPoint
    ray._direction = direction
end

function Sprite3DWithOBBPerfromanceTest:addNewSpriteWithCoords(vec2)
    local fileName = "Sprite3DTest/tortoise.c3b"
    local sprite = cc.Sprite3D:create(fileName)
    sprite:setScale(0.1)
    local s = cc.Director:getInstance():getWinSize()
    sprite:setPosition(cc.p(s.width * 4.0 / 5.0, s.height / 2.0))
    self:addChild(sprite)
    self._sprite = sprite
    local animation = cc.Animation3D:create(fileName)
    if  nil ~= animation then
        local animate = cc.Animate3D:create(animation, 0.0, 1.933)
        sprite:runAction(cc.RepeatForever:create(animate))
    end
    
    self._moveAction = cc.MoveTo:create(4.0, cc.p(s.width / 5.0, s.height / 2.0))
    self._moveAction:retain()
    local function reachEndCallBack()
    local s = cc.Director:getInstance():getWinSize()
        self._sprite:stopActionByTag(100)
        local inverse = cc.MoveTo:create(4.0, cc.p(s.width - self._sprite:getPositionX(), s.height / 2.0))
        inverse:retain()
        self._moveAction:release()
        self._moveAction = inverse
        local rot = cc.RotateBy:create(1.0, { x = 0.0, y = 180.0, z = 0.0})
        local seq = cc.Sequence:create(rot, self._moveAction, cc.CallFunc:create(reachEndCallBack))
        seq:setTag(100)
        self._sprite:runAction(seq)
    end
    local seq = cc.Sequence:create(self._moveAction, cc.CallFunc:create(reachEndCallBack))
    seq:setTag(100)
    sprite:runAction(seq)
 
    local aabb = self._sprite:getAABB()
    self._obbt = cc.OBB:new(aabb)
    
    self._drawDebug = cc.DrawNode3D:create()
    self:addChild(self._drawDebug)
end

function Sprite3DWithOBBPerfromanceTest.create()
    local layer = Sprite3DWithOBBPerfromanceTest.new()
    Helper.initWithLayer(layer)
    Helper.titleLabel:setString("OBB Collision Perfromance Test")
    return layer
end

----------------------------------------
----Sprite3DMirrorTest
----------------------------------------
local Sprite3DMirrorTest = {}
Sprite3DMirrorTest.__index = Sprite3DMirrorTest

function Sprite3DMirrorTest.create()
    local layer = cc.Layer:create()
    Helper.initWithLayer(layer)
    Helper.titleLabel:setString("Sprite3D Mirror Test")

    local fileName = "Sprite3DTest/orc.c3b"
    local sprite = cc.Sprite3D:create(fileName)
    sprite:setScale(5.0)
    sprite:setRotation3D({x = 0, y = 180, z = 0})
    sprite:setPosition( cc.p(size.width/2 - 80, size.height/2) )
    layer:addChild(sprite)

    local sp = cc.Sprite3D:create("Sprite3DTest/axe.c3b")
    sprite:getAttachNode("Bip001 R Hand"):addChild(sp)

    local animation = cc.Animation3D:create(fileName)
    if nil ~= animation then
        local animate = cc.Animate3D:create(animation)
        
        sprite:runAction(cc.RepeatForever:create(animate))
    end

    --create mirror Sprite3D
    sprite = cc.Sprite3D:create(fileName)
    sprite:setScale(5)
    sprite:setScaleX(-5)
    sprite:setCullFace(ccb.CullMode.FRONT)
    sprite:setRotation3D({x = 0, y = 180,z = 0})
    layer:addChild(sprite)
    sprite:setPosition( cc.p( size.width/2 + 80, size.height/2))

    --test attach
    sp = cc.Sprite3D:create("Sprite3DTest/axe.c3b")
    sprite:getAttachNode("Bip001 R Hand"):addChild(sp)
    
    animation = cc.Animation3D:create(fileName)
    if nil ~= animation then
        local animate = cc.Animate3D:create(animation)
        
        sprite:runAction(cc.RepeatForever:create(animate))
    end

    return layer
end


----------------------------------------
----AsyncLoadSprite3DTest
----------------------------------------
local AsyncLoadSprite3DTest = class("AsyncLoadSprite3DTest", function ()
    local layer = cc.Layer:create()
    Helper.initWithLayer(layer)
    return layer
end)

function AsyncLoadSprite3DTest:ctor()
    -- body
    self:init()
end

function AsyncLoadSprite3DTest:init()
    Helper.titleLabel:setString(self:title())
    Helper.subtitleLabel:setString(self:subtitle())

    self:registerScriptHandler(function (event)
        if event == "enter" then
            self:onEnter()
        elseif event == "exit" then
            self:onExit()
        end
    end)
end

function AsyncLoadSprite3DTest:title()
    return "Testing Sprite3D:createAsync"
end

function AsyncLoadSprite3DTest:subtitle()
    return ""
end

function AsyncLoadSprite3DTest:onEnter()

    local ttfConfig = {}
    ttfConfig.fontFilePath = "fonts/arial.ttf"
    ttfConfig.fontSize = 15

    local paths = {"Sprite3DTest/girl.c3b", "Sprite3DTest/orc.c3b", "Sprite3DTest/ReskinGirl.c3b", "Sprite3DTest/axe.c3b"}

    local label1 = cc.Label:createWithTTF(ttfConfig,"AsyncLoad Sprite3D")
    local item1 = cc.MenuItemLabel:create(label1)

    function menuCallback_asyncLoadSprite(tag, sender)
        --Note that you must stop the tasks before leaving the scene.
        cc.AsyncTaskPool:getInstance():stopTasks(cc.AsyncTaskPool.TaskType.TASK_IO)
    
        local node = self:getChildByTag(101)
        --remove all loaded sprite
        node:removeAllChildren()
    
        --remove cache data
        cc.Sprite3DCache:getInstance():removeAllSprite3DData()

        local function callback(sprite, index)
            local node = self:getChildByTag(101)
            local s = cc.Director:getInstance():getWinSize()
            local width = s.width / (#paths)
            local point = cc.p(width * (0.5 + index), s.height / 2.0)
            sprite:setPosition(point)
            node:addChild(sprite)
        end

        cc.Sprite3D:createAsync(paths[1], function(sprite)
            callback(sprite, 0)
        end)

        cc.Sprite3D:createAsync(paths[2], function(sprite)
            callback(sprite, 1)
        end)

        cc.Sprite3D:createAsync(paths[3], function(sprite)
            callback(sprite, 2)
        end)

        cc.Sprite3D:createAsync(paths[4], function(sprite)
            callback(sprite, 3)
        end)

        cc.Sprite3D:createAsync(paths[5], function(sprite)
            callback(sprite, 4)
        end)
    end
    item1:registerScriptTapHandler(menuCallback_asyncLoadSprite)
    
    local s = cc.Director:getInstance():getWinSize()
    item1:setPosition( s.width * 0.5, s.height * 0.8)
    
    local menu = cc.Menu:create(item1)
    menu:setPosition(cc.p(0,0))
    self:addChild(menu, 10)
    
    local node = cc.Node:create()
    node:setTag(101)
    self:addChild(node)
    
    menuCallback_asyncLoadSprite()
end

function AsyncLoadSprite3DTest:onExit()
end

----------------------------------------
----Sprite3DCubeTexture
----------------------------------------
local Sprite3DCubeMapTest = class("Sprite3DCubeMapTest", function ()
    local layer = cc.Layer:create()
    return layer
end)

function Sprite3DCubeMapTest:ctor()
    -- body
    self:init()
    self._textureCube = nil
    self._skyBox = nil
    self._teapot = nil
end

function Sprite3DCubeMapTest:init()
    self:registerScriptHandler(function (event)
        if event == "enter" then
            self:onEnter()
        elseif event == "exit" then
            self:onExit()
        end
    end)
end

function Sprite3DCubeMapTest:title()
    return "CubeMap & Skybox Test"
end

function Sprite3DCubeMapTest:subtitle()
    return ""
end

function Sprite3DCubeMapTest:onEnter()
    local s = cc.Director:getInstance():getWinSize()
    self:addNewSpriteWithCoords(cc.p(s.width / 2, s.height / 2))
    Helper.initWithLayer(self)
    Helper.titleLabel:setString(self:title())
    Helper.subtitleLabel:setString(self:subtitle())
end

function Sprite3DCubeMapTest:onExit()
    local targetPlatform = cc.Application:getInstance():getTargetPlatform()
    if targetPlatform == cc.PLATFORM_OS_ANDROID  or targetPlatform == cc.PLATFORM_OS_WINRT  or targetPlatform == cc.PLATFORM_OS_WP8  then
        cc.Director:getInstance():getEventDispatcher():removeEventListener(self._backToForegroundListener)
    end
end

function Sprite3DCubeMapTest:addNewSpriteWithCoords(pos)
    local visibleSize = cc.Director:getInstance():getVisibleSize()
    local camera = cc.Camera:createPerspective(60, visibleSize.width / visibleSize.height, 10, 1000)
    camera:setPosition3D(cc.vec3(0.0, 0.0, 50.0))
    camera:setCameraFlag(cc.CameraFlag.USER1)
    --create a teapot
    self._teapot = cc.Sprite3D:create("Sprite3DTest/teapot.c3b")

    local vertexShader = cc.FileUtils:getInstance():getStringFromFile("Sprite3DTest/cube_map.vert")
    local fragmentShader = cc.FileUtils:getInstance():getStringFromFile("Sprite3DTest/cube_map.frag")
    local program = ccb.Device:getInstance():newProgram(vertexShader, fragmentShader)
    local programState = ccb.ProgramState:new(program)
    program:release()
    
    self._textureCube = cc.TextureCube:create("Sprite3DTest/skybox/left.jpg", "Sprite3DTest/skybox/right.jpg",
        "Sprite3DTest/skybox/top.jpg", "Sprite3DTest/skybox/bottom.jpg",
        "Sprite3DTest/skybox/front.jpg", "Sprite3DTest/skybox/back.jpg")

    --set texture parameters
    local tRepeatParams = { magFilter=ccb.SamplerFilter.LINEAR , minFilter=ccb.SamplerFilter.LINEAR , sAddressMode=ccb.SamplerAddressMode.MIRROR_REPEAT  , tAddressMode=ccb.SamplerAddressMode.MIRROR_REPEAT }
    self._textureCube:setTexParameters(tRepeatParams)

    --pass the texture sampler to our custom shader
    local locCubeTex = programState:getUniformLocation("u_cubeTex")
    local cubeTexture = self._textureCube:getBackendTexture()
    programState:setTexture(locCubeTex, 0, cubeTexture)

    self._teapot:setProgramState(programState)
    self._teapot:setPosition3D(cc.vec3(0, -5, 0))
    self._teapot:setRotation3D(cc.vec3(-90, 180, 0))

    local rotate_action = cc.RotateBy:create(1.5, cc.vec3(0, 30, 0))
    self._teapot:runAction(cc.RepeatForever:create(rotate_action))

    --pass mesh's attribute to shader

    local attributeNames = 
    {
        "a_position",
        "a_color",
        "a_texCoord",
        "a_texCoord1",
        "a_texCoord2",
        "a_texCoord3",
        "a_normal",
        "a_blendWeight",
        "a_blendIndex",
    }

    self:addChild(self._teapot)

    --config skybox
    self._skyBox = cc.Skybox:create()

    self._skyBox:setTexture(self._textureCube)
    self:addChild(self._skyBox)

    self:addChild(camera)
    self:setCameraMask(2)

end

----------------------------------------
----Sprite3DNormalMappingTest
----------------------------------------
local Sprite3DNormalMappingTest = class("Sprite3DNormalMappingTest", function ()
    local layer = cc.Layer:create()
    Helper.initWithLayer(layer)
    return layer
end)

function Sprite3DNormalMappingTest:ctor()
    -- body
    self:init()
end

function Sprite3DNormalMappingTest:init()
    Helper.titleLabel:setString(self:title())
    Helper.subtitleLabel:setString(self:subtitle())

    self:registerScriptHandler(function (event)
        if event == "enter" then
            self:onEnter()
        elseif event == "exit" then
            self:onExit()
        end
    end)
end

function Sprite3DNormalMappingTest:title()
    return "Testing Normal Mapping"
end

function Sprite3DNormalMappingTest:subtitle()
    return ""
end

function Sprite3DNormalMappingTest:onEnter()

    local sprite3d = cc.Sprite3D:create("Sprite3DTest/sphere.c3b")
    sprite3d:setScale(2.0)
    sprite3d:setPosition(cc.p(-30,0))
    sprite3d:setRotation3D(cc.vec3(90.0, 0.0, 0.0))
    sprite3d:setTexture("Sprite3DTest/brickwork-texture.jpg")
    sprite3d:setCameraMask(2)
    self:addChild(sprite3d)

    local sprite3dBumped = cc.Sprite3D:create("Sprite3DTest/sphere_bumped.c3b")
    sprite3dBumped:setScale(20.0)
    sprite3dBumped:setPosition(cc.p(30,0))
    sprite3dBumped:setRotation3D(cc.vec3(90.0, 0.0, 0.0))
    sprite3dBumped:setCameraMask(2)
    self:addChild(sprite3dBumped)

    local radius = 100.0
    local angle = 0.0
    local reverseDir = false
    local light = cc.PointLight:create(cc.vec3(0.0, 0.0, 0.0), cc.c3b(255, 255, 255), 1000.0)
    local function lightUpdate()
        light:setPosition3D(cc.vec3(radius * math.cos(angle), 0.0, radius * math.sin(angle)))
        if reverseDir == true then
            angle = angle - 0.01
            if angle < 0.0 then
                reverseDir = false
            end
        else
            angle = angle + 0.01
            if 3.14159 < angle then
                reverseDir = true
            end
        end
    end

    local seq = cc.Sequence:create(cc.CallFunc:create(lightUpdate))
    light:runAction(cc.RepeatForever:create(seq))
    self:addChild(light)

    local visibleSize = cc.Director:getInstance():getVisibleSize()
    local camera = cc.Camera:createPerspective(60, visibleSize.width / visibleSize.height, 10, 1000)
    camera:setPosition3D(cc.vec3(0.0, 0.0, 100.0))
    camera:lookAt(cc.vec3(0.0, 0.0, 0.0))
    camera:setCameraFlag(cc.CameraFlag.USER1)
    self:addChild(camera)

end

function Sprite3DNormalMappingTest:onExit()
end

----------------------------------------
----Sprite3DMaterialTest
----------------------------------------
local Sprite3DMaterialTest = class("Sprite3DMaterialTest", function ()
    local layer = cc.Layer:create()
    Helper.initWithLayer(layer)
    return layer
end)

function Sprite3DMaterialTest:ctor()
    -- body
    self:init()
end

function Sprite3DMaterialTest:init()
    Helper.titleLabel:setString(self:title())
    Helper.subtitleLabel:setString(self:subtitle())

    self:registerScriptHandler(function (event)
        if event == "enter" then
            self:onEnter()
        elseif event == "exit" then
            self:onExit()
        end
    end)
end

function Sprite3DMaterialTest:title()
    return "Testing Sprite3DMaterial"
end

function Sprite3DMaterialTest:subtitle()
    return ""
end

function Sprite3DMaterialTest:onEnter()

    local material = cc.Sprite3DMaterial:createWithFilename("Sprite3DTest/outline.material")
    local sprite = cc.Sprite3D:create("Sprite3DTest/sphere_bumped.c3b")
        :setScale(20.0)
        :setPosition(cc.p(0,0))
        :setRotation3D(cc.vec3(90.0, 0.0, 0.0))
        :setCameraMask(2)
        :setMaterial(material)
    self:addChild(sprite)


    local camera = cc.Camera:create()
        :setPosition3D(cc.vec3(0.0, 0.0, 100.0))
        :lookAt(cc.vec3(0.0, 0.0, 0.0))
        :setCameraFlag(cc.CameraFlag.USER1)
    self:addChild(camera)
end

function Sprite3DMaterialTest:onExit()
end

function Sprite3DTest()
    local scene = cc.Scene:create()

    Helper.createFunctionTable =
    {
        Sprite3DBasicTest.create,
        Sprite3DHitTest.create,
        Sprite3DWithSkinTest.create,
        Animate3DTest.create,
        AttachmentTest.create,
        Sprite3DReskinTest.create,
        Sprite3DWithOBBPerfromanceTest.create,
        Sprite3DMirrorTest.create,
        AsyncLoadSprite3DTest.create,
        Sprite3DCubeMapTest.create,
        Sprite3DNormalMappingTest.create,
        Sprite3DMaterialTest.create,
    }
    Helper.index = 1

    scene:addChild(Sprite3DBasicTest.create())
    scene:addChild(CreateBackMenuItem())

    return scene
end
