--[[
CPP>>LUA
OnWork - сработал таймер обьекта
OnInit -   инициализация уровня
OnDrag - попытка взять обьект
OnDrop - попытка отпустить обьект
OnDieng - обьект умират
OnIntersection - обьект был пересечён
OnTimer - вызывается каждую секунду
-----------------------------------------
LUA>>CPP
CPP_AddToWorkList - время, через которое вызовится OnWork для обьекта
CPP_PlaySound - проиграть звук
CPP_CreateObject - создать обьект
CPP_DieObject - попытатся уничтожить обьект
CPP_BindIntersections  - сделать обьект приёмником
CPP_BindMouse - сделать обьект кправляемым с клавиатуры
CPP_SetState - установить визуальное состояние
CPP_SetMovingVector - установить вектор смещения
CPP_SetMovingTarget - установить точку перемещения
CPP_AddScores
CPP_SetCounter( идентификатор, значение счётчика)  - привязать к обьекту счётчик
CPP_SetActiveRect( идентификатор, x1, y1, x2, y2 ) - установить регион, занимаемый обьектов


соятояния   normal(нормальное), fall(падающее), fly(летящее)
стадии:       apear, work, disapear, intersection 
--]]
DragList={};
kInfinity = -1;
-- состояния обьекта
kNormal = 0; -- норрмальное
kFly    = 1; -- перемещаемое
kFall   = 2; -- падающее 
-- игровые сообщения
msgEndLevel = 1; -- уровень закончен
msgNextDayPart=2; -- изменить значение счётчика времени

TimerFramesCount = 52;

Statistics=
{
    Scores = 0,
    GatherFructs={},
};


StatForFruct=
{
    Apple = "apple";
    Grapes = "grapes";
    Orange = "orange";
};

end_level_info={};

-- получить случайное значение из диапазона
function GetRandomValue(aValues)
    local count = table.getn(aValues);
    if(count==0)then return 0 end;
    local res  = Random(1, count);
    return aValues[res];
end



-- действие на событие по умолчанию
function defOnTimer()
    Options.LevelTime = Options.LevelTime - 1;
    
    
    if(Options.LevelTime < 0)then
        Statistics.EndGame=1;
    end
    
    if( temp_data.CloseTimer)then
        temp_data.CloseTimer = temp_data.CloseTimer - 1;
        if(temp_data.CloseTimer <=0)then
            CPP_GameMessage(msgEndLevel, 0,"");
        end
    end
end

-- передаётся управление каждую секунду для динамической модификации
function OnTimer() 
    defOnTimer();
end


function defOnWork(aIndex)
    local aCell = Cells[aIndex];
    if aCell == nil then return; end;
    if( Statistics.EndGame==1)then return; end;
    
    aCell.CurrentName = nil;
    
    local ind = Random(1, aCell.sumLikelihood);
    
    for i=1, table.getn(aCell.FrutisLikelihood) do

        ind = ind - aCell.FrutisLikelihood[i];
        if( ind <=0 ) then
            aCell.CurrentName = aCell.FrutisNames[i];
            CurrentItem = Fruites[aCell.FrutisNames[i]];
            if( CurrentItem == nil or aCell.CurrentName==nil)then
                ScriptDump("Попытка создать несуществующий фрукт("..i);
                return;
            end;
            break;
        end
    end
    
    if( aCell.CurrentName == nil )then
        ScriptDump("На "..aIndex.." ячейке неправильно сработал алгоритм выбора вероятностей");
        ScriptDump("aCell.FrutisLikelihood="..aCell.sumLikelihood);
        ScriptDump("ind="..ind);
    end


    if( aCell.Region ==nil )then
        x = aCell.x;
        y = aCell.y;
    else
        x = Random( aCell.Region[1], aCell.Region[3]);
        y = Random( aCell.Region[2], aCell.Region[4]);
    end
    
    if ( Statistics.CreateObj == nil )then
        Statistics.CreateObj = 1;
    else
        Statistics.CreateObj = Statistics.CreateObj + 1;
    end    
 
    local moving=0;
    if(Options.MovingItem~=nil)then moving = 1; end
 
    CPP_CreateObject( tostring(aIndex), x, y,
        moving, CurrentItem.image, GetRandomValue(CurrentItem.life));

end

-- вызывается когда истекает время отведённое в CPP_AddToWorkList
function OnWork(aIndex)
    defOnWork(aIndex);
end

function defOnDieng(aName, aManual, aState)
    if( aState == kFly and aManual==0)then return kInfinity; end

    local index = tonumber(aName);
   
    local cell = Cells[index];
    local fruct = Fruites[cell.CurrentName];

    if( aManual==0 and aState==0)then
        if( Options.AnjornDieng~=1)then
            CPP_AddScores( aName, fruct.scores.ToDie, 0 );
            AddStatParamI("FruitLost", 1);
        end    

        CPP_SetState(  aName, kFall);
        
        if(fruct.downspeed)then
            local down_speed = GetRandomValue(fruct.downspeed);           
            return CPP_SetMovingTarget( aName, down_speed, 0, -20);
        end;
    end 
    
    if( aManual==0 and aState==kFall)then    
        if( Options.AnjornDieng==1)then
            CPP_AddScores( aName, fruct.scores.ToDie, 0 );
            AddStatParamI("FruitLost", 1);
        end       
    end

   if( Statistics.DestroyObj==nil )then Statistics.DestroyObj = 0;end;
   Statistics.DestroyObj = Statistics.DestroyObj + 1;
    
    if( Statistics.EndGame==1)then 
        if( Statistics.DestroyObj >= Statistics.CreateObj)then
            temp_data.CloseTimer = 2;
        end
        return
    end
    -- включается таймер для ечейки, по истечению которого будет создан новый обьект    
    CPP_AddToWorkList( index, GetRandomValue( cell.frenzy));
end



-- вызывается когда время жизни обьекта истекает или вызывана , если возвращает nil или 0 - обьект уничтожается, иначе время его жизни добавляется
function OnDieng(aName, aManual, aState)
    return defOnDieng(aName, aManual, aState);
end



-- вызывается когда обьект или обьекты пытаются положить
function defOnDrop( aResiver, aState)
    if Frails==nil then
        Frails = {};
    end
    

    local frail = Frails[aResiver];

    if( frail == nil or aState~=kNormal)then return 0;end

    local fruct_name = Cells[tonumber(DragList[1])].CurrentName;
    local fruct = Fruites[fruct_name];
    local count = table.getn(DragList);
    
    if( fruct.Frail == aResiver)then
    
        SetStatParamMaxI( "HighestChain", count);
        if fruct.scores.Combos then
            CPP_AddScores( aResiver, (fruct.scores.ToFrail*count) + (fruct.scores.Combos[count]), 0); 
        end
        --CPP_AddScores( aResiver, fruct.scores.Combos[count], 1); 
        if Statistics.GatherFructs[fruct_name]==nil then
            Statistics.GatherFructs[fruct_name] = 1;
        else
            Statistics.GatherFructs[fruct_name] = Statistics.GatherFructs[fruct_name] + 1;
        end;                    
        
        
        CPP_PlaySound("success_bonus_sound");
        frail.count =  frail.count + count;
        CPP_SetCounter( aResiver, frail.count);
    else
        CPP_PlaySound("error_bonus_sound");
        CPP_AddScores( aResiver, fruct.scores.ToErrorFrail*count,0); 
    end
    
    for i=1, count do
        CPP_DieObject(DragList[i]);
    end
   
    DragList = {};
    return 1;   
end

-- вызывается когда обьект или обьекты пытаются положить
function OnDrop( aResiver, aState)
    return defOnDrop(aResiver, aState);
end



-- вызывается когда обьект пытаются взять, возвращает 1 если взять можно
function defOnDrag(aName, aState)
    if(aState == kNormal)then
        
        local cell = Cells[tonumber(aName)];
        if not cell then return 0; end;
        
        local fruct_name = cell.CurrentName;
        
        
        
        local fruct = Fruites[fruct_name];
        
        if( Options.MovingItem==1 and fruct.Frail and Frails)then
            local aFrail = Frails[fruct.Frail];
            if(aFrail ~=nil)then                
                
                local count = table.getn(DragList);
                
                if( count >= Options.MultiDrag) then return 0; end
                
                if( count > 0 )then
                    if fruct_name ~= Cells[tonumber(DragList[1])].CurrentName then                  
                        return 0;
                    end;    
                end
            
                table.insert(DragList, aName);
                CPP_DragObj(aName);
                CPP_PlaySound("success_bonus_sound");
                return 1;               
            end
        end
        

        local current_score = fruct.scores.Click;
        if current_score==nil then current_score=0; end
        
        if(  last_drag == fruct_name )then
            last_frag_count = last_frag_count + 1;
            
            if( not fruct.scores.Combos)then fruct.scores.Combos={}; end;
            
            SetStatParamMaxI( "HighestChain", last_frag_count);
            
            if( last_frag_count > table.getn(fruct.scores.Combos))then
                last_frag_count = table.getn(fruct.scores.Combos);
            end
            
            if( last_frag_count > 0 )then           
                current_score = current_score + fruct.scores.Combos[last_frag_count];
                --CPP_AddScores( aName, fruct.scores.Combos[last_frag_count], 1); 
            end
        else
            last_drag = fruct_name;
            last_frag_count = 1;
        end
        
        if Statistics.GatherFructs[fruct_name]==nil then
            Statistics.GatherFructs[fruct_name] = 1;
        else
            Statistics.GatherFructs[fruct_name] = Statistics.GatherFructs[fruct_name] + 1;
        end;           
        
        
        CPP_AddScores( aName, current_score, 0);     
        
        CPP_DieObject( aName );
        CPP_PlaySound("success_bonus_sound");
        return 1;
    end
    return 0;
end

function OnDrag(aName, aState)
    return defOnDrag(aName, aState)
end

CurrentCell = nil;
function FrutisEnumerator( aKey, aValue)
    if( aValue == nil or aValue == 0 )then return nil;end;
    CurrentCell.sumLikelihood = CurrentCell.sumLikelihood + aValue;
    table.insert( CurrentCell.FrutisNames, aKey );
    table.insert( CurrentCell.FrutisLikelihood, aValue );
    return nil;
end


function  InitCell(aCell)
    CurrentCell = aCell;
    CurrentCell.sumLikelihood = 0;
    CurrentCell.FrutisNames={};
    CurrentCell.FrutisLikelihood ={};   
    table.foreach( aCell.fruites, FrutisEnumerator );
end;

-- Создаёт корзины
function CreateFrail(aKey, aValue)
    CPP_CreateObject( aKey, aValue.x, aValue.y, 0, aValue.image, kInfinity);
    if(Options.HookIntersections==1)then
        CPP_BindIntersections(aKey);
    end
    
    if( aValue.region~=nil)then
        CPP_SetActiveRect( aKey, aValue.region[1], aValue.region[2],aValue.region[3],aValue.region[4]);
    end    
    
    if(Options.Sensibility~= nil and Options.Sensibility~=0 and 
            aValue.MovingRect~=nil and aValue.MovingRect[4]~=nil)then
        CPP_BindMouse( aKey, aValue.MovingRect[1], aValue.MovingRect[2], aValue.MovingRect[3], aValue.MovingRect[4]);
    end
    
    CPP_SetCounter( aKey, 0);
    aValue.count = 0;
end


-- инициализация бонусного уровня
function defOnInit()

    temp_data={};
    ClearStatParam("FruitLost");
    ClearStatParam("HighestChain");    
    


    local aCellCount = table.getn(Cells);                   
    for i = 1, aCellCount do
        InitCell(Cells[i]);
        
        if Cells[i].first == nil then 
            timer = GetRandomValue(Cells[i].life);
        else
            timer = Cells[i].first;
        end    
      
        CPP_AddToWorkList(i, timer);
    end
 
    if Frails then
        table.foreach( Frails, CreateFrail );
    end
    CPP_GameMessage(msgNextDayPart, Options.LevelTime, "");
end

-- инициализация бонусного уровня
function OnInit()
    return defOnInit()
end


function AddFrutis( aCellIndex, aName, aLikelihood)
    Cells[aCellIndex].fruites[aName] = aLikelihood;
    InitCell(Cells[aCellIndex]);
end

function RemoveFrutis( aCellIndex, aName)
    AddFrutis( aCellIndex, aName, nil);
end

function OnIntersection( aResiver, aTarget)
    local cell = Cells[tonumber(aTarget)];
    
    if Frails == nil then Frails ={}; end
    
    local frail = Frails[aResiver];
    
    if( cell == nil or frail==nil )then return; end;
    local fruct = Fruites[cell.CurrentName];
    if( fruct == nil)then return;end;
    CPP_SetState( aTarget, kFly);
    if( fruct.Frail == aResiver)then
        CPP_AddScores( aResiver, fruct.scores.ToFrail, 0); 
        CPP_PlaySound("success_bonus_sound");
        frail.count =  frail.count + 1;
        CPP_SetCounter( aResiver, frail.count);

        if Statistics.GatherFructs[cell.CurrentName]==nil then
            Statistics.GatherFructs[cell.CurrentName] = 1;
        else
            Statistics.GatherFructs[cell.CurrentName] = Statistics.GatherFructs[cell.CurrentName] + 1;
        end;                
        
    else
        CPP_PlaySound("error_bonus_sound");
        CPP_AddScores( aResiver, fruct.scores.ToErrorFrail, 0); 
    end    
    CPP_DieObject(aTarget);
end


function CreateFructStat(aKey, aValue)
    if StatForFruct[aKey] then
            end_level_info["value_line"..current_line]={};
            end_level_info["value_line"..current_line].score = aValue; -- TODO: установить нормальные очки мож. понадобиться;
            end_level_info["value_line"..current_line].value = aValue;
            end_level_info["value_line"..current_line].name = StatForFruct[aKey];
            current_line = current_line + 1;
    end
end

function PrepearStatistic()
    end_level_info={};
    current_line = 1;

    if Statistics and Statistics.GatherFructs then
        table.foreach( Statistics.GatherFructs, CreateFructStat);
    end   
end




