CollapseCore = {}

CollapseCore.mCells = {}
CollapseCore.mDisplay = nil

CollapseCore.WIDTH = 10
CollapseCore.HEIGHT = 12
CollapseCore.COLOR = {'red', 'green', 'blue', 'dark_blue', 'orange', 'yellow', 'violet'}
CollapseCore.TYPE = {['chip'] = 'chip', ['bomb'] = 'bomb', ['color_bomb'] = 'color_bomb', ['shuffle'] = 'shuffle', ['freeze'] = 'freeze'}
CollapseCore.SHAKE_TIME = 1.0

CollapseCore.scores =
{
    ['kill.chip'] = 20,
    ['kill.bomb'] = 50,
    ['kill.color_bomb'] = 50,
    ['kill.shuffle'] = 50,
    --['kill.freeze'] = 50,
    ['clean.field'] = 100
}

CollapseCore.colorBars = {}

CollapseCore.close_callback = nil

----------------------------------------------------------------------------------------------------
function CollapseCore:setCloseCallback(func)
	CollapseCore.close_callback = func
end


----------------------------------------------------------------------------------------------------
function CollapseCore:new(colors, newLineTime, powerups)

    lineStartTimer = 0.0
    CollapseCore.mAllowClick = false
    CollapseCore.mNeedLines = 3

    CollapseCore.mLinesLeft = 30

    CollapseCore.mBonusSet = {}
    if powerups ~= nil then
        CollapseCore.mBonusSet = powerups
    end

    CollapseCore.mColors = #colors
    for i = 1, #CollapseCore.COLOR do
        if i <= #colors then
            local colorBar = CollapseCore.colorBars[CollapseCore.COLOR[i]]
            local tElem = colorBar.elem
            tElem:setVisible(true)
            play_clip(tElem:getCharacter(), {frame_begin = 99, frame_end = 99})

            colorBar.text:setText(colors[i])
            colorBar.goal = colors[i]
            colorBar.current = 0

            CollapseCore:setColorBar(CollapseCore.COLOR[i], 0)
        else
            CollapseCore.colorBars[CollapseCore.COLOR[i]].elem:setVisible(false)
        end
    end

    CollapseCore.mNewLineTime = newLineTime

    CollapseCore.mKillCounter = 0

    --CollapseCore.mBonusSet = {'color_bomb'}
    --CollapseCore.mBonusSet = {'freeze'}

    CollapseCore.mLineTimer = 0.0
    CollapseCore.mMovingChips = 0
    
    CollapseCore.mBombRadius = 3

    CollapseCore.mGroups = {}

    CollapseCore.mIsEnd = false
    CollapseCore.mIsWin = false

    if CollapseCore.newLine ~= nil then
        for i = 1, #CollapseCore.newLine do
            if CollapseCore.newLine[i].chip ~= nil then
                CollapseCore.newLine[i].chip:destroy()
            end
        end
        CollapseCore.newLine = nil
    end

    for x = 1, CollapseCore.WIDTH do
        for y = 1, CollapseCore.HEIGHT do
            if CollapseCore.mCells[x][y].chip ~= nil then
                CollapseCore.mCells[x][y].chip:destroy()
                CollapseCore.mCells[x][y].chip = nil
            end
        end
    end
    
    CollapseCore.mScore = 0
    CollapseCore.mDisplay:get_element('btn_score_number'):setText('0')
    
    CollapseCore.mPlacedChips = 0

    CollapseCore.mScheduler = Scheduler:new(CollapseCore.onStamp)
    CollapseCore.mScheduler:addStamp(1.0, 'and i very smart')
    CollapseCore.mScheduler:addStamp(0.2, 'hello, i\'m little scheduler')

    local tChipCount = 0
    for i = 1, #CollapseChip.sPool do
        if not CollapseChip.sPool[i].used then
            tChipCount = tChipCount + 1
        end
    end

    assert(tChipCount == #CollapseChip.sPool)

end

----------------------------------------------------------------------------------------------------
function CollapseCore:onStamp(stamp)
    trace('STAMP: '..stamp)
end

----------------------------------------------------------------------------------------------------
function CollapseCore:setColorBar(color, value)
    local bar = CollapseCore.colorBars[color]
    bar.current = value

    local frame = math.floor((bar.current / bar.goal) * 99.9)
    --trace('BAR '..color..' FRAME: '..frame)
    play_clip(bar.elem:getCharacter(), {frame_begin = frame, frame_end = frame})

    bar.text:setText(bar.goal - bar.current)

    if value ~= 0 then
        CollapseCore.mDisplay:add_effect('Scores_'..color, bar.elem:getPos(true), true)
    end
end

----------------------------------------------------------------------------------------------------
function CollapseCore:onColorMatch(color)
    local bar = CollapseCore.colorBars[color]

    if bar.current == bar.goal then
        return
    end

    bar.current = bar.current + 1
    CollapseCore:setColorBar(color, bar.current)

    local filledBars = 0
    for i = 1, CollapseCore.mColors do
        local colorBar = CollapseCore.colorBars[CollapseCore.COLOR[i]]
        if colorBar.goal == colorBar.current then
            filledBars = filledBars + 1
        end
    end

    if CollapseCore.mColors == filledBars then
        CollapseCore:onEnd(true)
    end
end

----------------------------------------------------------------------------------------------------
function CollapseCore:init()
    CollapseCore.mDisplay = level_mgr:load_level("Collapse")

    local index = 1
    for x = 1, CollapseCore.WIDTH do
        CollapseCore.mCells[x] = {}
        for y = 1, CollapseCore.HEIGHT do
            local tChip = CollapseCore.mDisplay:get_element('fish_'..index)

            CollapseCore.mCells[x][y] = {pos = Vec2(tChip:getPos().x, tChip:getPos().y), row = y, col = x}
            CollapseChip.sPool[index] = {chip = tChip, used = false}

            local size = CollapseCore.mDisplay:get_element_count()
            for i = 0, size - 1 do
                elem = CollapseCore.mDisplay:get_element_at(i)
                if tChip:isParentOf(elem) then
                    local elemName = elem:getName()

                    for n = 1, #CollapseChip.LAYERS do
                        if CollapseChip.LAYERS[n] == elemName then
                            CollapseChip.sPool[index][elemName] = elem
                            break
                        end
                    end

                    --if CollapseCore.TYPE[elemName] ~= nil then
                    --    CollapseChip.sPool[index][CollapseCore.TYPE[elemName]] = elem
                    --end
                end
            end

            tChip:setActive(false)
            tChip:setVisible(false)

            index = index + 1
        end
    end

    CollapseCore.mChipRoot = CollapseCore.mDisplay:get_element('chip_root')
    CollapseCore.mInitRootPos = Vec2(CollapseCore.mChipRoot:getPos().x, CollapseCore.mChipRoot:getPos().y)

    for i = 1, #CollapseCore.COLOR do
        local tElem = CollapseCore.mDisplay:get_element('bar_'..CollapseCore.COLOR[i])
        CollapseCore.colorBars[CollapseCore.COLOR[i]] = {
            elem = tElem,
            text = CollapseCore.mDisplay:get_element_child(tElem, 'text'),
            goal = 0,
            current = 0}
    end

    CollapseCore.mDisplay:set_element_eh('col_remark', function() CollapseCore:onRemarkClick() end)
    CollapseCore.mDisplay:set_element_eh('col_win', function() CollapseCore:onWinClick() end)

    CollapseCore.mDisplay:get_element('col_remark'):setActive(false)
    CollapseCore.mDisplay:get_element('col_remark'):setVisible(false)
    CollapseCore.mDisplay:get_element('col_win'):setActive(false)
    CollapseCore.mDisplay:get_element('col_win'):setVisible(false)
end


----------------------------------------------------------------------------------------------------
function CollapseCore:exitAction(skip)
	if CollapseCore.close_callback then CollapseCore.close_callback(skip) end
    if Collapse.ExitLevel ~= "" then
        local level = Collapse.ExitLevel
        Collapse.ExitLevel = ""
        level_mgr:switch_to_level(level) fade_panel:fade_out()
    else
        level_mgr:switch_to_level('Map') fade_panel:fade_out() 
    end
end

----------------------------------------------------------------------------------------------------
function CollapseCore:onShow()
    Panel:show(false)
    Panel:showTimer(false)

    CollapseCore.mDisplay:play_clip('col_remark', {frame_begin = 0, frame_end = 0})
    CollapseCore.mDisplay:get_element('col_remark'):setActive(true)
    CollapseCore.mDisplay:get_element('safe_closed_text'):setVisible(true)
    CollapseCore.mDisplay:get_element('safe_closed_text_02'):setVisible(true)

    CollapseCore.mDisplay:play_clip('col_win', {frame_begin = 0, frame_end = 0})
    CollapseCore.mDisplay:get_element('col_win'):setActive(false)
    CollapseCore.mDisplay:get_element('col_win'):setVisible(false)
    CollapseCore.mDisplay:get_element('safe_open_text'):setVisible(false)
    CollapseCore.mDisplay:get_element('safe_open_text_02'):setVisible(false)
end

----------------------------------------------------------------------------------------------------
function CollapseCore:onRemarkClick()
    CollapseCore.mDisplay:get_element('col_remark'):setActive(false)
    set_frame_listener(CollapseCore.mDisplay:get_element('col_remark'), 'hide_text', function() CollapseCore:hideText() end)
    set_frame_listener(CollapseCore.mDisplay:get_element('col_remark'), 'remark_closed', function() CollapseCore:start() end)
    CollapseCore.mDisplay:play_clip('col_remark', {frame_begin = 0, label_end = 'remark_closed'})
end

----------------------------------------------------------------------------------------------------
function CollapseCore:onWinClick()
    CollapseCore.mDisplay:get_element('col_win'):setActive(false)
    CollapseCore.mDisplay:get_element('col_win'):setVisible(false)

    CollapseCore:exitAction()
end

----------------------------------------------------------------------------------------------------
function CollapseCore:hideText()
    CollapseCore.mDisplay:get_element('safe_closed_text'):setVisible(false)
    CollapseCore.mDisplay:get_element('safe_closed_text_02'):setVisible(false)
end

----------------------------------------------------------------------------------------------------
function CollapseCore:start()
    CollapseCore.mDisplay:get_element('col_remark'):setVisible(false)
    CollapseCore.mDisplay:play_clip('amb_lamp', {looped = true})
    if Collapse.tutorial then
        Tutorial:enterChapter('Collapse')
    end
    addUpdateListener(CollapseCore)

    CollapseCore.mLineTimer = 0 --CollapseCore.mNewLineTime
    CollapseCore.mDisplay:set_element_eh("new_line", function() CollapseCore:onNewLineClick() end)
    CollapseCore.mDisplay:set_element_eh("btn_skip_view", function() if not msg_box:isVisible() then CollapseCore:exitAction(true) end end)
    
    CollapseCore.mIsEnd = false
    CollapseCore.mIsWin = false
    CollapseCore:setLinesLeft(CollapseCore.mLinesLeft)
    --addChip(aX, aY, aType)
end

----------------------------------------------------------------------------------------------------
function CollapseCore:onHide()
    stop_all_effects()
    removeUpdateListener(CollapseCore)
    CollapseCore.mDisplay:set_element_eh("new_line", nil)
    CollapseCore.mDisplay:set_element_eh("btn_skip_view", function()  end)
end

----------------------------------------------------------------------------------------------------
function CollapseCore:onNewLineClick()
    if CollapseCore.mIsEnd then
        return
    end

    CollapseCore.mLineTimer = CollapseCore.mNewLineTime
    CollapseCore.mNeedLines = CollapseCore.mNeedLines + 1
    CollapseCore:setLinesLeft(CollapseCore.mLinesLeft - 1)
    if Collapse.tutorial then
        Tutorial:sendMessage('collapse.pull')
    end
    --CollapseCore:createNewLine()
end

----------------------------------------------------------------------------------------------------
local function onScoreAction(action, pos)

    CollapseCore.mDisplay:add_effect('score.'..action, CollapseCore.mDisplay:get_element('score_effect'):getPos(true), true)
    if CollapseCore.scores[action] ~= nil then
        CollapseCore.mScore = CollapseCore.mScore + CollapseCore.scores[action]
    end

    CollapseCore.mDisplay:get_element('btn_score_number'):setText(CollapseCore.mScore)
end

----------------------------------------------------------------------------------------------------
local function onChipClick(aChip)
    if CollapseCore.mIsEnd then
        return
    end

    local tBonus = aChip.mBonus

    if tBonus == 'shuffle' then
        CollapseCore:startShuffle(aChip)
        Media:soundEvent('collapse.shuffle')
    else
        local isBonus = true

        if tBonus == 'bomb' then
            start_effect_at('collapse_bomb', aChip.mChip:getPos(true))
            onScoreAction('kill.bomb', aChip.mChip:getPos(true))
            Media:soundEvent('collapse.bomb')
            CollapseCore:shakeField()
        elseif tBonus == 'color_bomb' then
            start_effect_at('collapse_color_bomb', aChip.mChip:getPos(true))
            onScoreAction('kill.color_bomb', aChip.mChip:getPos(true))
            Media:soundEvent('collapse.colorBomb')
            CollapseCore:shakeField()
        elseif tBonus == 'freeze' then
            CollapseCore.mFreezeTimer = 10.0
            start_effect_at('collapse_freeze', aChip.mChip:getPos(true))
            onScoreAction('kill.freeze', aChip.mChip:getPos(true))
            Media:soundEvent('collapse.freeze')
            if CollapseCore.mFrostEffect == nil then
                --CollapseCore.mFrostEffect = start_effect_at_character('collapse_frost', CollapseCore.mDisplay:get_element('new_line'):getCharacter())
                start_effect_at('collapse_frost', Vec2(512, 384))
                --CollapseCore.mDisplay:link_effect('collapse_frost', 'field')
                CollapseCore.mFrostEffect = 'collapse_frost'
            end
        else
            isBonus = false
        end

        local tGroup = CollapseCore:getChipGroup(aChip)

        if tGroup > 0 then

            tGroupMoving = false
            for i = 1, #CollapseCore.mGroups[tGroup] do
                local cell = CollapseCore:getCellByChip(CollapseCore.mGroups[tGroup][i])
                if cell.chip:isMoving() then
                    tGroupMoving = true
                    break
                end
            end

            if tGroupMoving then
                return
            end

            for i = 1, #CollapseCore.mGroups[tGroup] do
                local cell = CollapseCore:getCellByChip(CollapseCore.mGroups[tGroup][i])
                start_effect_at("collapse_chip_destroy", cell.chip.mChip:getPos(true))
                if not (isBonus and aChip == cell.chip) then
                    onScoreAction('kill.chip', cell.chip.mChip:getPos(true))
                end

                if cell.chip.mBonus == nil then
                    CollapseCore:onColorMatch(cell.chip.mType)
                end

                cell.chip = nil
                CollapseCore.mGroups[tGroup][i]:destroy()

                CollapseCore.mPlacedChips = CollapseCore.mPlacedChips - 1
                if CollapseCore.mPlacedChips == 0 then
                    onScoreAction('clean.field', Vec2(512, 384))
                end
            end

            --if not isBonus then
            --    CollapseCore:onColorMatch(aChip.mType)
            --end

            if Collapse.tutorial then
                Tutorial:sendMessage('collapse.destroyChips')
            end
            Media:soundEvent('collapse.tiles')

            table.remove(CollapseCore.mGroups, tGroup)

            CollapseCore:fall()

            if CollapseCore.mMovingChips == 0 then
                CollapseCore:collapse()
            end
            
            CollapseCore:refreshGroups()
            CollapseCore.mKillCounter = CollapseCore.mKillCounter + 1
        end
    end
end

----------------------------------------------------------------------------------------------------
function onChipRollOver(aChip)
    if CollapseCore.mIsEnd then
        return
    end

    local tGroup = CollapseCore:getChipGroup(aChip)
    if tGroup > 0 then
        for i = 1, #CollapseCore.mGroups[tGroup] do
            CollapseCore.mGroups[tGroup][i]:setState(CollapseChip.STATE_HLIGHTED)
        end
    end

end

----------------------------------------------------------------------------------------------------
function onChipRollOut(aChip)
    local tGroup = CollapseCore:getChipGroup(aChip)
    if tGroup > 0 then
        for i = 1, #CollapseCore.mGroups[tGroup] do
            CollapseCore.mGroups[tGroup][i]:setState(CollapseChip.STATE_NORMAL)
        end
    end
end

----------------------------------------------------------------------------------------------------
function CollapseCore:addChip(aX, aY, aType, aGenBonus)
    local chip = CollapseChip:new(aType, CollapseCore.mDisplay)

    if aGenBonus and #CollapseCore.mBonusSet > 0 then
        local bonus = get_random(1, #CollapseCore.mBonusSet + 1)
        chip:setBonus(CollapseCore.mBonusSet[bonus])
    end

    CollapseCore:placeChip(chip, aX, aY)
end

----------------------------------------------------------------------------------------------------
function CollapseCore:placeChip(aChip, aX, aY)
    CollapseCore.mCells[aX][aY].chip = aChip
    aChip.cell = CollapseCore.mCells[aX][aY]
    aChip:setVisible(true)

    CollapseCore.mCells[aX][aY].chip:setPos(CollapseCore.mCells[aX][aY].pos.x, CollapseCore.mCells[aX][aY].pos.y)

    CollapseCore.mCells[aX][aY].chip:setClickListener(onChipClick)
    CollapseCore.mCells[aX][aY].chip:setRollOverListener(onChipRollOver)
    CollapseCore.mCells[aX][aY].chip:setRollOutListener(onChipRollOut)
    CollapseCore.mCells[aX][aY].chip:setStopListener(onChipStop)
end

----------------------------------------------------------------------------------------------------
function CollapseCore:getCellByChip(aChip)
    return aChip.cell
end

----------------------------------------------------------------------------------------------------
function CollapseCore:moveChip(aSrcCell, aDstCell, aTime)
    if aSrcCell == aDstCell or aSrcCell.chip == nil then
        return
    end
    aSrcCell.chip:setState(CollapseChip.STATE_NORMAL)
    aSrcCell.chip:moveTo(aDstCell.pos.x, aDstCell.pos.y, aTime)
    aDstCell.chip = aSrcCell.chip
    aDstCell.chip.cell = aDstCell
    aSrcCell.chip = nil
    
    if aSrcCell.row == CollapseCore.HEIGHT then
        CollapseCore.mPlacedChips = CollapseCore.mPlacedChips + 1
    end

  
    CollapseCore.mMovingChips = CollapseCore.mMovingChips + 1
end

----------------------------------------------------------------------------------------------------
function onChipStop(aChip)
    CollapseCore.mMovingChips = CollapseCore.mMovingChips - 1
    
    if CollapseCore.mMovingChips == 0 then
        CollapseCore:collapse()
        CollapseCore:refreshGroups()
    end
end

----------------------------------------------------------------------------------------------------
function CollapseCore:setLinesLeft(aLines)
    CollapseCore.mLinesLeft = aLines
    if aLines < 0 then
        aLines = 0
    end
    CollapseCore.mDisplay:get_element("lines_left"):setText(aLines, false)
    CollapseCore.mDisplay:play_clip("lines_bar", {frame_begin = aLines, frame_end = aLines})
end


CollapseCore.newLine = nil

----------------------------------------------------------------------------------------------------
function CollapseCore:createNewLine()
    if CollapseCore.newLine ~= nil and #CollapseCore.newLine > 0 then
        return
    end

    CollapseCore.newLine = {}

    local tBonusPos = {}
    if CollapseCore.mKillCounter > 0 then
        local rand_generator = {}
        for i = 1, CollapseCore.WIDTH do
            rand_generator[i] = i
        end

        while CollapseCore.mKillCounter > 0 do
            CollapseCore.mKillCounter = CollapseCore.mKillCounter - 3
            local rand = get_random(1, #rand_generator + 1)
            tBonusPos[rand_generator[rand] ] = rand_generator[rand]
            table.remove(rand_generator, rand)
        end
        CollapseCore.mKillCounter = 0
    end

    for i = 1, CollapseCore.WIDTH do
        local rand = get_random(1, CollapseCore.mColors + 1)
        local tChip = CollapseChip:new(CollapseCore.COLOR[rand], CollapseCore.mDisplay)
        assert(tChip)

        if tBonusPos[i] ~= nil and #CollapseCore.mBonusSet > 0 then
            local bonus = get_random(1, #CollapseCore.mBonusSet + 1)
            tChip:setBonus(CollapseCore.mBonusSet[bonus])
        end

        table.insert(CollapseCore.newLine, {cell = i, chip = tChip})
    end
    trace('new line generated')
end

----------------------------------------------------------------------------------------------------
function CollapseCore:placeNewLine()
    if CollapseCore.newLine == nil then
        return true
    end

    local cellX = CollapseCore.newLine[1].cell
    local chip = CollapseCore.newLine[1].chip

    CollapseCore:placeChip(chip, cellX, CollapseCore.HEIGHT)

    table.remove(CollapseCore.newLine, 1)
    if #CollapseCore.newLine == 0 then
        trace('placed')
        CollapseCore.newLine = nil
        return true
    end
end

local lineStartTimer = 0.0

----------------------------------------------------------------------------------------------------
function CollapseCore:update(aElapsed)

    if isGameFocusOff() then
        return
    end

    CollapseCore.mScheduler:update(aElapsed)

    if CollapseCore.mShakeTime ~= nil and CollapseCore.mShakeTime > 0.0 then
        CollapseCore.mShakeTime = CollapseCore.mShakeTime - aElapsed

        if CollapseCore.mShakeTime < 0.0 then
            CollapseCore.mShakeTime = nil
            CollapseCore.mChipRoot:setPos(CollapseCore.mInitRootPos)

        else
            local time = CollapseCore.SHAKE_TIME - CollapseCore.mShakeTime
            local x, y = CollapseCore:getShakePoint(time)

            if x ~= nil and y ~= nil then
                CollapseCore.mChipRoot:setPos(Vec2(CollapseCore.mInitRootPos.x + x, CollapseCore.mInitRootPos.y + y))
            end
        end
    end

    if CollapseCore.mFreezeTimer ~= nil and CollapseCore.mFreezeTimer > 0.0 then
        CollapseCore.mFreezeTimer = CollapseCore.mFreezeTimer - aElapsed
        aElapsed = 0.5 * aElapsed
        if CollapseCore.mFreezeTimer <= 0.0 then
            CollapseCore.mFreezeTimer = nil
            if CollapseCore.mFrostEffect ~= nil then
                stop_effect(CollapseCore.mFrostEffect)
                CollapseCore.mFrostEffect = nil
            end
        end
    end

    for x = 1, CollapseCore.WIDTH do
        for y = 1, CollapseCore.HEIGHT do
            if CollapseCore.mCells[x][y].chip ~= nil then
                CollapseCore.mCells[x][y].chip:update(aElapsed)
            end
        end
    end

    if CollapseCore.mIsEnd then
        return
    end

    CollapseCore.mLineTimer = CollapseCore.mLineTimer - aElapsed
    if CollapseCore.mLineTimer <= 0.0 then
        --if CollapseCore.mLinesLeft > 0 then
            CollapseCore.mLineTimer = CollapseCore.mNewLineTime
            CollapseCore.mNeedLines = CollapseCore.mNeedLines + 1
            --CollapseCore:setLinesLeft(CollapseCore.mLinesLeft - 1)
            --CollapseCore:createNewLine()
        --else
            --CollapseCore:onEnd(true)
        --end
    elseif CollapseCore.newLine ~= nil then
        
        local x = CollapseCore.WIDTH - math.ceil((CollapseCore.mNewLineTime - CollapseCore.mLineTimer) / CollapseCore.mNewLineTime * CollapseCore.WIDTH)

        --trace(x..' of '..#CollapseCore.newLine)
        while CollapseCore.newLine ~= nil and x < #CollapseCore.newLine do
            CollapseCore:placeNewLine()
        end
        --if CollapseCore.newLine ~= nil and x > #CollapseCore.newLine then
            --CollapseCore:placeNewLine()
        --end
        --[[if CollapseCore.mCells[x][CollapseCore.HEIGHT].chip == nil then
            CollapseCore:addChip(x, CollapseCore.HEIGHT, CollapseCore.COLOR[rand])
        end
        for i = 1, CollapseCore.WIDTH do
            local rand = get_random(1, CollapseCore.mColors + 1)
            CollapseCore:addChip(i, CollapseCore.HEIGHT, CollapseCore.COLOR[rand])
        end]]
    end

    if lineStartTimer > 0.0 then
        lineStartTimer = lineStartTimer - aElapsed
        if lineStartTimer <= 0.0 then
            CollapseCore:createNewLine()
        end
    end

    if CollapseCore.mNeedLines > 0 and CollapseCore.mMovingChips == 0 then

        while not CollapseCore:placeNewLine() do end
        --[[local tBonusPos = {}
        if CollapseCore.mKillCounter > 0 then
            local rand_generator = {}
            for i = 1, CollapseCore.WIDTH do
                rand_generator[i] = i
            end

            while CollapseCore.mKillCounter > 0 do
                CollapseCore.mKillCounter = CollapseCore.mKillCounter - 2
                local rand = get_random(1, #rand_generator + 1)
                tBonusPos[rand_generator[rand] ] = rand_generator[rand]
                table.remove(rand_generator, rand)
            end
            CollapseCore.mKillCounter = 0
        end

        for i = 1, CollapseCore.WIDTH do
            local rand = get_random(1, CollapseCore.mColors + 1)
            CollapseCore:addChip(i, CollapseCore.HEIGHT, CollapseCore.COLOR[rand], tBonusPos[i] ~= nil)
        end

        ]]
        for y = 1, CollapseCore.HEIGHT do
            for x = 1, CollapseCore.WIDTH do
                if not CollapseCore.mIsEnd then
                    if CollapseCore.mCells[x][y].chip ~= nil and y == 1 then
                        CollapseCore:onEnd(false)
                        break
                    end

                    if CollapseCore.mCells[x][y].chip ~= nil and y > 1 then
                        CollapseCore:moveChip(CollapseCore.mCells[x][y], CollapseCore.mCells[x][y - 1], 0.1)
                    end
                end
            end
        end
        
        CollapseCore.mNeedLines = CollapseCore.mNeedLines - 1
        if CollapseCore.mNeedLines > 0 then
            CollapseCore:createNewLine()
        else
            lineStartTimer = 0.1
        end
    end
end

----------------------------------------------------------------------------------------------------
function CollapseCore:refreshGroups()
    CollapseCore.mGroups = {}

    for y = 1, CollapseCore.HEIGHT do
        for x = 1, CollapseCore.WIDTH do
            CollapseCore.mCells[x][y].group = nil
        end
    end

    for y = 1, CollapseCore.HEIGHT - 1 do
        for x = 1, CollapseCore.WIDTH do
            if CollapseCore.mCells[x][y].chip ~= nil then
                local tGroup = {}
                local tBonus = CollapseCore.mCells[x][y].chip.mBonus

                if tBonus == nil then
                    tGroup = {CollapseCore.mCells[x][y].chip}

                    CollapseCore:addChipToGroup(x - 1, y, tGroup)
                    CollapseCore:addChipToGroup(x + 1, y, tGroup)
                    CollapseCore:addChipToGroup(x, y + 1, tGroup)
                    CollapseCore:addChipToGroup(x, y - 1, tGroup)
                    
                    if #tGroup >= 3 then
                        table.insert(CollapseCore.mGroups, #CollapseCore.mGroups + 1, tGroup)
                    end

                elseif tBonus == 'bomb' then
                    CollapseCore.mCells[x][y].group = tGroup
                    for i = -CollapseCore.mBombRadius, CollapseCore.mBombRadius do
                        for j = -CollapseCore.mBombRadius, CollapseCore.mBombRadius do
                            local tX = x + i
                            local tY = y + j
                            if tX > 0 and tX <= CollapseCore.WIDTH and tY > 0 and tY < CollapseCore.HEIGHT then
                                table.insert(tGroup, CollapseCore.mCells[tX][tY].chip)
                            end
                        end
                    end
                    table.insert(CollapseCore.mGroups, tGroup)

                elseif tBonus == 'color_bomb' then
                    CollapseCore.mCells[x][y].group = tGroup
                    local color = CollapseCore.mCells[x][y].chip:getType()

                    for tY = 1, CollapseCore.HEIGHT - 1 do
                        for tX = 1, CollapseCore.WIDTH do
                            local chip = CollapseCore.mCells[tX][tY].chip
                            if chip ~= nil and chip:getType() == color then
                                table.insert(tGroup, chip)
                            end
                        end
                    end
                    table.insert(CollapseCore.mGroups, tGroup)

                elseif tBonus == 'freeze' or tBonus == 'shuffle' then
                    tGroup = {CollapseCore.mCells[x][y].chip}
                    CollapseCore.mCells[x][y].group = tGroup
                    table.insert(CollapseCore.mGroups, tGroup)

                end
            end
        end
    end    
end

----------------------------------------------------------------------------------------------------
function CollapseCore:addChipToGroup(aX, aY, aGroup)
    if aX <= 0 or aX > CollapseCore.WIDTH or aY <= 0 or aY >= CollapseCore.HEIGHT then
        return
    end

    local tChip = CollapseCore.mCells[aX][aY].chip
    if tChip == nil then
        return
    end

    if tChip.mBonus == 'bomb' then
        return
    end

    for i = 1, #aGroup do
        if aGroup[i] == tChip then
            return
        end
    end

    if tChip:getType() ~= aGroup[1]:getType() then
        return
    end

    table.insert(aGroup, #aGroup + 1, tChip)

    CollapseCore:addChipToGroup(aX - 1, aY, aGroup)
    CollapseCore:addChipToGroup(aX + 1, aY, aGroup)
    CollapseCore:addChipToGroup(aX, aY + 1, aGroup)
    CollapseCore:addChipToGroup(aX, aY - 1, aGroup)

    tBonus = CollapseCore.mCells[aX][aY].chip.mBonus
    if tBonus == nil then
        CollapseCore.mCells[aX][aY].group = aGroup
    end
end

----------------------------------------------------------------------------------------------------
function CollapseCore:getChipGroup(aChip)
    local cell = CollapseCore:getCellByChip(aChip)

    for i = 1, #CollapseCore.mGroups do
        if CollapseCore.mGroups[i] == cell.group then
            return i
        end
    end
    
    return 0
end

----------------------------------------------------------------------------------------------------
function CollapseCore:fall()
    for x = 1, CollapseCore.WIDTH do
        local tNewColumn = {}
        local tIndex = 1

        for y = CollapseCore.HEIGHT - 1, 1, -1 do
            if CollapseCore.mCells[x][y].chip ~= nil then
                tNewColumn[tIndex] =
                {
                    src = CollapseCore.mCells[x][y],
                    dst = CollapseCore.mCells[x][CollapseCore.HEIGHT - tIndex]
                }
                tIndex = tIndex + 1
            end
        end

        for i = 1, #tNewColumn do
            CollapseCore:moveChip(tNewColumn[i].src, tNewColumn[i].dst, 0.1)
        end
    end
end

----------------------------------------------------------------------------------------------------
function CollapseCore:onEnd(aIsWin)
    CollapseCore.mIsEnd = true
    CollapseCore.mIsWin = aIsWin
    stop_all_effects()
    --CollapseCore.mDisplay:unlink_effect('field')
    if aIsWin then
        --msg_box:show("ids_you_win", 3.0)
        --timer_mgr:add_timer(function() level_mgr:switch_to_level('Map') fade_panel:fade_out() end, 3.0, 1)
--        start_effect_at("crystall", Vec2(512, 384))
        --MinigameWin:show(1,function()  CollapseCore:exitAction(false)  end)

        set_frame_listener(CollapseCore.mDisplay:get_element('col_win'), 'win_closed',
            function()
                 CollapseCore.mDisplay:get_element('col_win'):setActive(true)
            end)

        set_frame_listener(CollapseCore.mDisplay:get_element('col_win'), 'show_text',
            function()
                CollapseCore.mDisplay:get_element('safe_open_text'):setVisible(true)
                CollapseCore.mDisplay:get_element('safe_open_text_02'):setVisible(true)
            end)
        CollapseCore.mDisplay:play_clip('col_win', {frame_begin = 0, label_end = 'win_closed'})

    else
        msg_box:show("ids_you_loose", 7.0,
            function()
                level_mgr:switch_to_level('Collapse')
--                msg_box:hide()
                fade_panel:fade_out()
            end)
--[[        timer_mgr:add_timer(function()
            level_mgr:switch_to_level('Collapse')
            msg_box:hide()
            fade_panel:fade_out()
        end, 3.0, 1)]]
    end

    for i = 1, #CollapseChip.sPool do
        CollapseChip.sPool[i].used = false
    end
end

----------------------------------------------------------------------------------------------------
function CollapseCore:startShuffle(aChip)
    local tCells = {}
    for x = 1, CollapseCore.WIDTH do
        for y = CollapseCore.HEIGHT - 1, 1, -1 do
            if CollapseCore.mCells[x][y].chip ~= nil and CollapseCore.mCells[x][y].chip ~= aChip then
                table.insert(tCells, CollapseCore.mCells[x][y])
            end
        end
    end

    local tMovePercent = 0.7
    local tStartSize = #tCells

    if tStartSize <= 1 then
        return
    end

    --while #tCells > tMovePercent * tStartSize do
        --local remove = get_random(1, #tCells + 1)
--        table.remove(tCells, remove)
    --end

    local tMoves = {}

    while #tCells > 0 do
        local rand = get_random(1, #tCells + 1)
        table.insert(tMoves, tCells[rand])
        table.remove(tCells, rand)

        --CollapseCore:moveChip(tSrc, tDst, 0.2)
    end

    local tSafeChip = tMoves[1].chip
    tMoves[1].chip = nil

    local fSwap = function(dst)

        if dst.chip == tSafeChip then
            trace('GOT IT!!!')
        end
        tSafeChip:moveTo(dst.pos.x, dst.pos.y, 0.4)
        chip = dst.chip
        dst.chip = tSafeChip
        tSafeChip.cell = dst
        tSafeChip = chip
        CollapseCore.mMovingChips = CollapseCore.mMovingChips + 1
    end

    for i = 1, #tMoves - 1 do
        fSwap(tMoves[i + 1])
        --tSafeChip = tMoves[i + 1].chip
        --CollapseCore:moveChip(tMoves[i], tMoves[i + 1])
    end
    fSwap(tMoves[1])
    --CollapseCore:moveChip(tMoves[#tMoves], tMoves[1], 0.2)

    aChip:setBonus(nil)

end

----------------------------------------------------------------------------------------------------
function CollapseCore:shakeField()
    CollapseCore.mShakeTime = CollapseCore.SHAKE_TIME

    local minTick = CollapseCore.mShakeTime / 50.0
    local maxTick = CollapseCore.mShakeTime / 20.0

    local xtime = 0.0
    local ytime = 0.0
    CollapseCore.mShakePath = {x = {}, y = {}}

    local mult = 5.0
    while xtime < CollapseCore.mShakeTime do

        value = 10 * mult * (2.0 * (math.random() - 0.5))
        mult = - mult * 0.9

        table.insert(CollapseCore.mShakePath.x, {time = xtime, val = value})

        local dt = minTick + (maxTick - minTick) * math.random()
        xtime = xtime + dt
    end

    mult = 5.0
    while ytime < CollapseCore.mShakeTime do

        value = 10 * mult * (2.0 * (math.random() - 0.5))
        mult = - mult * 0.9

        table.insert(CollapseCore.mShakePath.y, {time = ytime, val = value})

        local dt = minTick + (maxTick - minTick) * math.random()
        ytime = ytime + dt
    end

end

----------------------------------------------------------------------------------------------------
function CollapseCore:getShakePoint(aTime)
    if CollapseCore.mShakePath == nil then
        return 0, 0
    end

    local x, y

    for i = 1, #CollapseCore.mShakePath.x do
        if CollapseCore.mShakePath.x[i].time > aTime then
            x = CollapseCore.mShakePath.x[i].val
            break
        end
    end

    for i = 1, #CollapseCore.mShakePath.y do
        if CollapseCore.mShakePath.y[i].time > aTime then
            y = CollapseCore.mShakePath.y[i].val
            break
        end
    end

    return x, y
end

----------------------------------------------------------------------------------------------------
function CollapseCore:collapse()

    local tMoveColumns = {}
    local tIndex = 1
    local tHalf = CollapseCore.WIDTH / 2

    for x = tHalf, 1, -1 do

        local tEmptyColumn = true
        for y = 1, CollapseCore.HEIGHT - 1 do
            if CollapseCore.mCells[x][y].chip ~= nil then
                tEmptyColumn = false
                break
            end
        end

        if not tEmptyColumn then
            table.insert(tMoveColumns, {src = x, dst = CollapseCore.WIDTH - (tHalf + tIndex - 1)})
            tIndex = tIndex + 1
        end
    end

    tHalf = tHalf + 1
    tIndex = 1

    for x = tHalf, CollapseCore.WIDTH do

        local tEmptyColumn = true
        for y = 1, CollapseCore.HEIGHT - 1 do
            if CollapseCore.mCells[x][y].chip ~= nil then
                tEmptyColumn = false
                break
            end
        end

        if not tEmptyColumn then
            table.insert(tMoveColumns, {src = x, dst = tHalf + tIndex - 1})
            tIndex = tIndex + 1
        end
    end

    for i = 1, #tMoveColumns do
        for y = 1, CollapseCore.HEIGHT - 1 do
            CollapseCore:moveChip(CollapseCore.mCells[tMoveColumns[i].src][y], CollapseCore.mCells[tMoveColumns[i].dst][y], 0.2)
        end
    end
end


