-- To create an animation, register an object and a set of properties of that object to change over time, using the format
-- { object=object,start_t=start_t,end_t=end_t,actions={{prop="property",start_v=start_value,end_v=end_value,type="type or nil"},...} }
function do_animation(animation,t)
    local object = animation.object
    for i=#animation.actions,1,-1 do -- iterate backwards so there isn't any confusion while deleting animations and iterating simultaneously
        local action = animation.actions[i]
        if action.type == "play_sound" then
            g2.play_sound(object)
        end
        if action.type == nil then      
            local t_span = animation.end_t - animation.start_t
            local sign = 1
            if action.end_v < action.start_v then sign = -1 end
            -- make sure property doesn't go out of range
            if sign*(object[action.prop] + (action.end_v - action.start_v) / t_span * t) > sign*(action.end_v) then
                object[action.prop] = action.end_v
            else 
                object[action.prop] = object[action.prop] + (action.end_v - action.start_v) / t_span * t
            end
            if GLOBAL.t > animation.end_t then object[action.prop] = action.end_v end
        elseif action.type == "brighten" then
            local t_span = animation.end_t - animation.start_t
            object[action.prop] = brighten(object[action.prop], 255 * t / t_span)
        elseif action.type == "tween" then
            object[action.prop] = 0.75*object[action.prop] + 0.25*action.end_v
            if GLOBAL.t > animation.end_t then object[action.prop] = action.end_v end
        elseif action.type == "toggle" then
            object[action.prop] = action.end_v 
        end
    end
end

-- return a brighter/darker render color
function brighten(color, change) -- change is in range (-255, 255), render color has r=g=b
    color = string.format("%X",color) -- dec to hex
    change = math.ceil(change - 0.5) 
    local rgb = tonumber("0x"..color:sub(1,2)) + change
    if rgb > 255 then rgb = 255 elseif rgb < 0 then rgb = 0 end
    return rgb * 2 ^ 16 + rgb * 2 ^ 8 + rgb
end

-- set up intro scene
function init_intro()
    GLOBAL.animations = {} -- holds regular animations
    GLOBAL.click_animations = {} -- queue that holds animations that only become active after the user clicks/taps
    GLOBAL.click_queue = {}
    GLOBAL.t = 0
    GLOBAL.state = "intro"
    
    g2.game_reset()
    g2.view_set(0,GLOBAL.sh * 0.125, GLOBAL.sw, GLOBAL.sh * 0.75)
    g2.state = "scene" 
    g2.bkgr_src = "black"

    g2.play_music("mus-legends-buck")

    local sf = GLOBAL.scale_f
    
    local im = INTRO_TEXTS[1]
    local intro_1 = g2.new_image("intro_1",0,0,GLOBAL.sw,GLOBAL.sh)
    local intro_text1 = g2.new_image(im.name,im.x*sf,im.y*sf,im.w*sf,im.h*sf)
    im = INTRO_TEXTS[2]
    local intro_2 = g2.new_image("intro_2",0,0,GLOBAL.sw,GLOBAL.sh)
    intro_2.render_alpha = 0
    local intro_text2 = g2.new_image(im.name,im.x*sf,im.y*sf,im.w*sf,im.h*sf)
    intro_text2.render_alpha = 0
    table.insert(GLOBAL.click_animations, {stop=false, object=intro_1,start_t=.8,end_t=1.3,actions={{prop="render_alpha",start_v=255,end_v=0}}})
    table.insert(GLOBAL.click_animations, {stop=false, object=intro_text1,start_t=.8,end_t=1.3,actions={{prop="render_alpha",start_v=255,end_v=0}}})
    table.insert(GLOBAL.click_animations, {stop=false, object=intro_2,start_t=0,end_t=.8,actions={{prop="render_alpha",start_v=0,end_v=255}}}) -- rounding errors were making it not transparent...
    table.insert(GLOBAL.click_animations, {stop=true, object=intro_text2,start_t=0,end_t=.8,actions={{prop="render_alpha",start_v=0,end_v=255}}})
end

-- set up ending scene
function init_ending()
    GLOBAL.animations = {}
    GLOBAL.click_animations = {}
    GLOBAL.click_queue = {}
    GLOBAL.t = 0
    GLOBAL.wormholes = nil
    GLOBAL.state = "ending"
    GAME = nil
    
    g2.game_reset()
    g2.view_set(0,GLOBAL.sh * 0.125, GLOBAL.sw, GLOBAL.sh * 0.75)
    g2.state = "scene" 
    g2.bkgr_src = "black"
    
    g2.play_music("mus-legends-credits")

    local sf = GLOBAL.scale_f
    
    local end_background = g2.new_image("end",0,0,GLOBAL.sw,GLOBAL.sw*1.956)
    table.insert(GLOBAL.animations, {object=end_background,start_t=0,end_t=10,actions={{prop="position_y",start_v=0,end_v=-2470*GLOBAL.sw/2048}}})
    local texts = {}
    local im = ENDING_TEXTS[1]
    texts[1] = g2.new_image(im.name,im.x*sf,im.y*sf,im.w*sf,im.h*sf)
    texts[1].render_alpha = 0
    table.insert(GLOBAL.animations, {object=texts[1],start_t=11,end_t=12,actions={{prop="render_alpha",start_v=0,end_v=255}}})
    for i=2,4 do
        im = ENDING_TEXTS[i]
        texts[i] = g2.new_image(im.name,im.x*sf,im.y*sf,im.w*sf,im.h*sf)
        texts[i].render_alpha = 0
        table.insert(GLOBAL.click_animations, {stop=false,object=texts[i],start_t=0,end_t=1,actions={{prop="render_alpha",start_v=0,end_v=255}}})
        table.insert(GLOBAL.click_animations, {stop=true,object=texts[i-1],start_t=0,end_t=1,actions={{prop="render_alpha",start_v=255,end_v=0}}})
    end
    table.insert(GLOBAL.click_animations, {stop=false,object=texts[4],start_t=0,end_t=1,actions={{prop="render_alpha",start_v=255,end_v=0}}})
    table.insert(GLOBAL.click_animations, {stop=false,object=end_background,start_t=1,end_t=11,actions={{prop="position_y",start_v=-2470*GLOBAL.sw/2048,end_v=0}}})
    table.insert(GLOBAL.click_animations,
            {stop=false,object="sfx-ending",start_t=12,end_t=12,actions={{type="play_sound"}}})
    local explosions = {}
    explosions[1] = g2.new_image("end1",0,0,GLOBAL.sw,GLOBAL.sh)
    explosions[1].render_alpha = 0
    table.insert(GLOBAL.click_animations, {stop=false,object=explosions[1],start_t=12,end_t=12.15,actions={{prop="render_alpha",start_v=0,end_v=255}}})
    for i=2,5 do
        explosions[i] = g2.new_image("end"..i,0,0,GLOBAL.sw,GLOBAL.sh)
        explosions[i].render_alpha = 0
        table.insert(GLOBAL.click_animations, {stop=false,object=explosions[i],start_t=12+0.15*(i-1),end_t=12+0.15*(i),actions={{prop="render_alpha",start_v=0,end_v=255}}})
        table.insert(GLOBAL.click_animations, {stop=false,object=explosions[i-1],start_t=12+0.15*(i),end_t=12+0.15*(i) + 0.10,actions={{prop="render_alpha",start_v=255,end_v=0}}})
    end
    local credits_bkgr = g2.new_image("black",0,0,GLOBAL.sw,GLOBAL.sh)
    credits_bkgr.render_alpha = 0
    table.insert(GLOBAL.click_animations, {stop=false,object=credits_bkgr,start_t=13,end_t=17,actions={{prop="render_alpha",start_v=0,end_v=255}}})
    local separation = 50
    local offset = 700
    local final = -100
    local speed = 50
    local label = g2.new_label(CREDITS[1],GLOBAL.sw/2,offset + 1*separation)
    local my_st = 17
    label.label_size = 90
    table.insert(GLOBAL.click_animations, {stop=false,object=label,start_t=my_st,end_t=my_st + (offset + separation - final)/speed,actions={{prop="position_y",start_v=offset + 1*separation,end_v=final}}})     
    for i=2,#CREDITS do
        local stop = false
        if i == #CREDITS then stop = true end
        if i ~= #CREDITS and CREDITS[1] ~= "" then
            label = g2.new_label(CREDITS[i],GLOBAL.sw/2,offset + i*separation)
            label.label_size = 50
            table.insert(GLOBAL.click_animations, {stop=stop,object=label,start_t=my_st,end_t=my_st + (offset + i*separation - final)/speed,actions={{prop="position_y",start_v=offset + i*separation,end_v=final}}})  
        end
    end
end

-- set up map scene
function init_map(map) 
    GAME = nil
    GLOBAL.animations = {}
    GLOBAL.click_animations = {}
    GLOBAL.click_queue = {}
    GLOBAL.t = 0
    GLOBAL.state = "map"
    GLOBAL.map_label = nil
    
    g2.game_reset()
    g2.view_set(0,0,GLOBAL.sw,GLOBAL.sh)
    g2.state = "scene" 
    g2.bkgr_src = "black"
    if map ~= nil then
       GLOBAL.map = map
    end
    
    g2.play_music("mus-legends-map")
    
    if GLOBAL.map == nil then
        if GLOBAL.highest_level > 18 then GLOBAL.map = "B" else GLOBAL.map = "A" end
    end
    
    local sf = GLOBAL.scale_f
    
    g2.new_image("background_"..GLOBAL.map,0,0,GLOBAL.sw,GLOBAL.sh)

    if GLOBAL.map == "A" then
        for i=1,18 do
           local c = CONNECTIONS[i]
           local image = g2.new_image(c.name,c.x*sf,c.y*sf,c.w*sf,c.h*sf)
           image.render_color = 0x000000
           if i <= GLOBAL.highest_level then
               local max = GLOBAL.highest_level
               if GLOBAL.highest_level > 18 then max = 18 end
               table.insert(GLOBAL.animations, {object=image,start_t=GLOBAL.t+0.3*(i-1)/(max+1),end_t=GLOBAL.t+0.3*(i-1)/(max+1)+0.5,actions={{prop="render_color",start_v=0x000000,end_v=0xffffff,type="brighten"}}})
           end
        end
    else
        for i=19,23 do
           local c = CONNECTIONS[i]
           local image = g2.new_image(c.name,c.x*sf,c.y*sf,c.w*sf,c.h*sf)
           image.render_color = 0x000000
           if i <= GLOBAL.highest_level then
               local max = GLOBAL.highest_level
               table.insert(GLOBAL.animations, {object=image,start_t=GLOBAL.t+0.3*(i-19)/(max-16),end_t=GLOBAL.t+0.3*(i-19)/(max-16)+0.5,actions={{prop="render_color",start_v=0x000000,end_v=0xffffff,type="brighten"}}})
           end
        end
    end
    
    -- glowing star around next level's planet
    local c = PLANETS[GLOBAL.highest_level]
    if c ~= nil then
        if GLOBAL.map == "A" and GLOBAL.highest_level < 19 or GLOBAL.map == "B" and GLOBAL.highest_level > 18 then
            local sz = 100
            GLOBAL.star = g2.new_image("galaxy_star",c.x*sf,c.y*sf,sz,sz)
            GLOBAL.star.image_cx = sz/2
            GLOBAL.star.image_cy = sz/2
--             GLOBAL.star.render_blend = 1
        end
    end
    
    g2.new_image("planets_"..GLOBAL.map,0,0,GLOBAL.sw,GLOBAL.sh)
    local overlay = g2.new_image("overlay_"..GLOBAL.map,0,0,GLOBAL.sw,GLOBAL.sh)
    overlay.render_blend = 1
    
    local w
    if GLOBAL.map == "A" then w = WORMHOLES[1] else w = WORMHOLES[2] end
    GLOBAL.wormholes = {
        {image=g2.new_image("wormhole_"..GLOBAL.map,w.x*sf,w.y*sf,w.r*2*sf,w.r*2*sf),rot_speed=math.pi*2/1200},
        {image=g2.new_image("wormhole_"..GLOBAL.map,w.x*sf,w.y*sf,w.r*2*sf,w.r*2*sf),rot_speed=math.pi*2/800},
        {image=g2.new_image("wormhole_"..GLOBAL.map,w.x*sf,w.y*sf,w.r*2*sf,w.r*2*sf),rot_speed=math.pi*2/500},
        {image=g2.new_image("wormhole_"..GLOBAL.map,w.x*sf,w.y*sf,w.r*2*sf,w.r*2*sf),rot_speed=math.pi*2/400}
    }
    for i=1,#GLOBAL.wormholes do
        GLOBAL.wormholes[i].image.image_cx = w.r*sf
        GLOBAL.wormholes[i].image.image_cy = w.r*sf
        GLOBAL.wormholes[i].image.render_blend = 1
        GLOBAL.wormholes[i].image.image_a = math.pi*2/i
    end
       
    if GLOBAL.map == "A" then
        if GLOBAL.highest_level == 1 then
            label = g2.new_label("Start",PLANETS[1].x*sf + 55,PLANETS[1].y*sf, 0xffffff)
            label.label_size = 30
        end
    end
    
    local next_n = GLOBAL.highest_level
    if GLOBAL.map == "A" and next_n >= 2 and next_n <= 18 then
        if next_n < 12 then
            label = g2.new_label("Next",PLANETS[next_n].x*sf + 55,PLANETS[next_n].y*sf, 0xffffff)
        else
            label = g2.new_label("Next",PLANETS[next_n].x*sf - 55,PLANETS[next_n].y*sf, 0xffffff)
        end
        label.label_size = 30
    end
    if GLOBAL.map == "B" and next_n >= 19 and next_n <= 23 then
        label = g2.new_label("Next",PLANETS[next_n].x*sf + 55,PLANETS[next_n].y*sf, 0xffffff)
        label.label_size = 30
    end
    
    -- high-score flags
    if GLOBAL.map == "A" then
        for i=1,18 do
            g2.new_image("rank"..GLOBAL.highest_ranks[i],(PLANETS[i].x-20)*sf,(PLANETS[i].y-18)*sf,40*sf,40*sf,0xffffff)
        end
    else
        for i=19,23 do
            g2.new_image("rank"..GLOBAL.highest_ranks[i],(PLANETS[i].x-20)*sf,(PLANETS[i].y-18)*sf,40*sf,40*sf,0xffffff)
        end
    end
    label = g2.new_label("Rank",62,36,0xffffff)
    label.label_size = 25
    g2.new_image("rank"..GLOBAL.current_difficulty,42,55,100*sf,100*sf,0xffffff)
    label = g2.new_label(RANKS[GLOBAL.current_difficulty].name,62,110,0xffffff)
    label.label_size = 18
end

-- start a level, beginning with the pre-game screen
function init_level(level_num)
	local level = LEVELS[level_num]
    
    GLOBAL.seed = os.time()
    GLOBAL.animations = {}
    GLOBAL.click_animations = {}
    GLOBAL.click_queue = {}
    GLOBAL.current_level = level_num
    GLOBAL.wormholes = nil
    GLOBAL.state = "pre_game"
    
    -- game instance-specific variables
    GAME = {
        WIN_TIMER_RESET=3,
        win_timer=3,
    }   
    GAME.won = nil
	
	g2.game_reset()
	g2.view_set(0,0,GLOBAL.sw,GLOBAL.sh)
	g2.bkgr_src = level.background_image
	if level_num == 6 or level_num == 21 or level_num == 23 then 
        g2.bkgr_src = "black" 
        g2.view_set(0,GLOBAL.sh * 0.125, GLOBAL.sw, GLOBAL.sh * 0.75)
    end
    g2.state = "scene"
    
    g2.play_music(level.music)
    
    local sf = GLOBAL.scale_f
    
    -- set up characters/etc
    for i,im in ipairs(level.pre_images) do
        local image
        if level.tweens ~= nil then 
            if level.tweens[i] == 1 then
                image = g2.new_image(im.name,im.x*sf-GLOBAL.sw,im.y*sf,im.w*sf,im.h*sf)
                table.insert(GLOBAL.animations, {object=image,start_t=GLOBAL.t,end_t=GLOBAL.t + 1,actions={{prop="position_x",start_v=im.x*sf-GLOBAL.sw,end_v=im.x*sf,type="tween"}} })
            elseif level.tweens[i] == 2 then
                image = g2.new_image(im.name,im.x*sf+GLOBAL.sw,im.y*sf,im.w*sf,im.h*sf)
                table.insert(GLOBAL.animations, {object=image,start_t=GLOBAL.t,end_t=GLOBAL.t + 1,actions={{prop="position_x",start_v=im.x*sf+GLOBAL.sw,end_v=im.x*sf,type="tween"}} })
            else
                image = g2.new_image(im.name,im.x*sf,im.y*sf,im.w*sf,im.h*sf)
            end
        else
            image = g2.new_image(im.name,im.x*sf,im.y*sf,im.w*sf,im.h*sf)
        end
    end
    -- do mirror effect on level 21
    if level_num == 21 then
        local im = level.pre_images[2]
        local image = g2.new_image(im.name,im.x*sf-GLOBAL.sw,im.y*sf+2*im.h*sf,im.w*sf,-im.h*sf)
        image.render_alpha = 120
        table.insert(GLOBAL.animations, {object=image,start_t=GLOBAL.t,end_t=GLOBAL.t + 1,actions={{prop="position_x",start_v=im.x*sf-GLOBAL.sw,end_v=im.x*sf,type="tween"}} })
        im = level.pre_images[3]
        image = g2.new_image(im.name,im.x*sf+GLOBAL.sw,im.y*sf+2*im.h*sf,im.w*sf,-im.h*sf)
        image.render_alpha = 120
        table.insert(GLOBAL.animations, {object=image,start_t=GLOBAL.t,end_t=GLOBAL.t + 1,actions={{prop="position_x",start_v=im.x*sf+GLOBAL.sw,end_v=im.x*sf,type="tween"}} })
    end
    -- set up dialogue
    local texts = {}
    local im = level.pre_texts[1]
    texts[1] = g2.new_image(im.name,im.x*sf,im.y*sf,im.w*sf,im.h*sf)
    texts[1].render_alpha = 0
    table.insert(GLOBAL.animations, {object=texts[1],start_t=GLOBAL.t,end_t=GLOBAL.t+0.5,actions={{prop="render_alpha",start_v=0,end_v=255}}})
    for i=2,#level.pre_texts do
        im = level.pre_texts[i]
        texts[i] = g2.new_image(im.name,im.x*sf,im.y*sf,im.w*sf,im.h*sf)
        texts[i].render_alpha = 0
        table.insert(GLOBAL.click_animations, {stop=false,object=texts[i],start_t=0,end_t=0.5,actions={{prop="render_alpha",start_v=0,end_v=255}}})
        table.insert(GLOBAL.click_animations, {stop=true,object=texts[i-1],start_t=0,end_t=0.5,actions={{prop="render_alpha",start_v=255,end_v=0}}})
    end
end

function do_tutorial(levelnum)
    if true then return end
    -- tutorial disabled
    
    if levelnum > 3 then return end
    if GLOBAL.current_difficulty > 3 or (GLOBAL.current_difficulty > 1 and GLOBAL.has_completed_tutorial[levelnum] == 1) then return end
    
    local level = LEVELS[levelnum]
    local y_sign
    if levelnum == 1 then
        g2.status = "Conquer your enemy to win!"
        local player_home = g2.search("planet owner:"..GAME.player)[1]
        if player_home.position_y < GLOBAL.sh/2 then y_sign = 1 else y_sign = -1 end
        local label = g2.new_label("Tap to select",player_home.position_x,player_home.position_y+player_home.planet_r*y_sign-20+30*y_sign)
        label.label_size = 30
        label.render_alpha = 0
        table.insert(GLOBAL.animations, {object=label,start_t=GLOBAL.t,end_t=GLOBAL.t+1,actions={{prop="render_alpha",start_v=0,end_v=255}}})
        label = g2.new_label("your planet.",player_home.position_x,player_home.position_y+player_home.planet_r*y_sign+15+30*y_sign)
        label.label_size = 30
        label.render_alpha = 0
        table.insert(GLOBAL.animations, {object=label,start_t=GLOBAL.t,end_t=GLOBAL.t+1,actions={{prop="render_alpha",start_v=0,end_v=255}}})
    
        local enemy_home = g2.search("planet owner:"..GAME.bots[1])[1]
        if enemy_home.position_y < GLOBAL.sh/2 then y_sign = 1 else y_sign = -1 end
        label = g2.new_label("Tap on or drag",enemy_home.position_x,enemy_home.position_y+enemy_home.planet_r*y_sign-40+50*y_sign)
        label.label_size = 30
        label.render_alpha = 0
        table.insert(GLOBAL.animations, {object=label,start_t=GLOBAL.t+2.5,end_t=GLOBAL.t+3.5,actions={{prop="render_alpha",start_v=0,end_v=255}}})
        label = g2.new_label("to the enemy",enemy_home.position_x,enemy_home.position_y+enemy_home.planet_r*y_sign-5+50*y_sign)
        label.label_size = 30
        label.render_alpha = 0
        table.insert(GLOBAL.animations, {object=label,start_t=GLOBAL.t+2.5,end_t=GLOBAL.t+3.5,actions={{prop="render_alpha",start_v=0,end_v=255}}})   
        label = g2.new_label("to attack",enemy_home.position_x,enemy_home.position_y+enemy_home.planet_r*y_sign+30+50*y_sign)
        label.label_size = 30
        label.render_alpha = 0
        table.insert(GLOBAL.animations, {object=label,start_t=GLOBAL.t+2.5,end_t=GLOBAL.t+3.5,actions={{prop="render_alpha",start_v=0,end_v=255}}})   
    elseif levelnum == 2 then
        g2.view_set(0, 0, GLOBAL.play_sw, GLOBAL.play_sh + 20)
        g2.status = "Take big neutral planets with few ships to grow faster."
--         g2.status = "Capture low-cost, high-production neutrals (grey planets)\nto produce more ships and conquer the enemy.\n"..
--                     "Cost is indicated by the number and production by size."
        local good = find("planet neutral",function(p) local ph = g2.search("planet owner:"..GAME.player)[1] 
                                                       return -3*p.ships_value + p.ships_production - p:distance(ph)*0.18 end)
        local bad = find("planet neutral",function(p) local ph = g2.search("planet owner:"..GAME.player)[1] 
                                                      return 3*p.ships_value - p.ships_production - p:distance(ph)*0.18 end)
        local label = g2.new_label("Good",good.position_x,good.position_y+(good.planet_r+10))
        label.label_size = 30
        label.render_alpha = 0
        table.insert(GLOBAL.animations, {object=label,start_t=GLOBAL.t,end_t=GLOBAL.t+1,actions={{prop="render_alpha",start_v=0,end_v=255}}})     

        local label = g2.new_label("Bad",bad.position_x,bad.position_y+(bad.planet_r+10))
        label.label_size = 30
        label.render_alpha = 0
        table.insert(GLOBAL.animations, {object=label,start_t=GLOBAL.t,end_t=GLOBAL.t+1,actions={{prop="render_alpha",start_v=0,end_v=255}}})                                                                                          
    elseif levelnum == 3 then
        g2.status = "Double-tap to select all. Tap percent to change fleet size."
--         g2.status = "Select multiple planets by tapping them individually,\n"..
--                     "double-tapping one of them, or by tapping an enemy planet.\n"..
--                     "Adjust the % indicator to change how many ships to send."
    end
end

-- show the winning or losing scene after a game
function init_post_game()
    local level = LEVELS[GLOBAL.current_level]
    
    GLOBAL.animations = {}
    GLOBAL.click_animations = {}
    GLOBAL.click_queue = {}
    GLOBAL.finish_t = GLOBAL.t
    GLOBAL.state = "post_game"
    
	g2.game_reset()
    g2.bkgr_src = level.background_image
    g2.view_set(0,0,GLOBAL.sw,GLOBAL.sh)
    g2.state = "scene"
    
    local sf = GLOBAL.scale_f
    
    local images
    if (GAME.won) then
        g2.play_music("mus-legends-win")
        images = level.win_images
    else 
        images = level.lose_images
    end
    for i,im in ipairs(images) do
        local image
        if i == #images then
            image = g2.new_image(im.name,10+im.x*sf,10+im.y*sf,im.w*sf,im.h*sf)
            image.render_alpha = 0
            table.insert(GLOBAL.animations, {object=image,start_t=GLOBAL.t + 0.1,end_t=GLOBAL.t + 0.6,actions={{prop="render_alpha",start_v=0,end_v=255}}})
        else
            image = g2.new_image(im.name,GLOBAL.sw+im.x*sf,im.y*sf,im.w*sf,im.h*sf)
            table.insert(GLOBAL.animations, {object=image,start_t=GLOBAL.t,end_t=GLOBAL.t + 0.7,actions={{prop="position_x",start_v=GLOBAL.sw+im.x*sf,end_v=im.x*sf,type="tween"}} })
        end
    end
end
