circuit = {}
--[[
circuit.params = 
{
	-- описание источника питания: 
	battery = 
	{
		item_nums = {1,1}, -- к какому элементу электрической сети подключен источник.
		item_connection = 1 -- каким соединением элемент связывается с источником. 1,2,3,4 - левым, верхним, правым, нижним соответственно.
	},
	
	items = 
	{	
		-- первая цифра в наборе обозначает на тип элемента электрической цепи: 1 - провод, 2 - "плохая" лампочка, 3 - "хорошая" лампочка.
		-- остальные четыре цифры указывают, есть ли связь в заданную сторону (влево, вверх, вправо, вниз) : 1 - есть, 0 - нет.
		{{1, 0,1,0,1}, {1, 1,1,1,0}, {1, 0,1,0,1}, {1, 1,1,0,0}, {3, 1,0,0,0}},
		{{1, 0,1,1,1}, {1, 1,0,1,1}, {2, 1,0,0,0}, {1, 1,0,1,1}, {1, 1,1,0,0}},
		{{1, 1,0,1,0}, {1, 0,1,1,1}, {1, 1,0,1,0}, {1, 0,0,1,1}, {1, 0,0,1,1}},
		{{1, 0,0,1,1}, {1, 0,1,1,1}, {2, 1,0,0,0}, {1, 1,0,1,0}, {1, 1,0,1,0}},
		{{3, 1,0,0,0}, {1, 0,1,1,0}, {1, 1,0,1,0}, {1, 0,0,1,1}, {3, 0,0,1,0}}
	},
	
	-- область расположения элементов (X, Y, Width, Height)
	game_rect = sf.misc.FloatRect(20, 20, 100, 100)
}
]]
circuit.enum = 
{
	wire = 1,		-- провод
	bad_lamp = 2,	-- "плохая" лампочка
	good_lamp = 3	-- "хорошая" лампочка
}

circuit.item_wire_types = 
{
	'1010', '0101', '1010', '0101',
	'1100', '0110', '0011', '1001',
	'1110', '0111', '1011', '1101',
	'1111', '1111', '1111', '1111'
}

circuit.item_lamp_types = 
{
	'1000', '0100', '0010', '0001',
	'1100', '0110', '0011', '1001',
	'1110', '0111', '1011', '1101',
	'1111', '1111', '1111', '1111'
}

function circuit.GetItemSubtypeAndAngleByConnections(_item_types, _connections)
	local str = ''	
	for i = 1, #_connections do str = str.._connections[i] end
	
	for i = 1, #_item_types do
		if _item_types[i] == str then
			local t = math.floor((i-1) / 4 + 1)
			local a = math.mod(i+4, 4) - 1
			if a == -1 then a = 3 end
			return t, a * math.pi / 2.0
		end
	end
	
	return nil,nil
end

function circuit.OnPreferredSize(_owner)
	return sminigames.OnPreferredSize(_owner)
end

function circuit.OnUpdate(_owner)
	if  sf.core.g_Application.GetTimeManager().GetTimer(_T("qe_level")).GetK() < 0.01 then
		return
	end

	if (circuit.all_bad_lamps_turn_off and circuit.all_good_lamps_turn_on) then
        circuit.EndGame(_owner)
    end
end

function circuit.OnDoubleClick(_owner, _pos, _button, _state, _broadcast)
	return circuit.OnMouseDown(_owner, _pos, _button, _state, _broadcast)
end

function circuit.OnMouseDown(_owner, _pos, _button, _state, _broadcast)
    local ret = false
	local item = circuit.GetItemByPos(_pos)
	if item then
		if  _button == sf.gui.CBaseWidget.LeftButton then	
			circuit.RotateItemRight(item)
			ret = true
		elseif  _button == sf.gui.CBaseWidget.RightButton then
			circuit.RotateItemLeft(item)
			ret = true
		end
	end
    if ret then
        circuit.UpdateMatrixConnections()
        circuit.UpdateItems(_owner)
    end
    return ret
end

function circuit.EndGame(_owner)
	_owner.SetGameResult(1)
	_owner.OnEndGame()
end

function circuit.OnMouseUp(_owner, _pos, _button, _state, _broadcast)
    return false
end

function circuit.Init(_owner)
	sminigames.SetStandartScriptName(_owner, circuit)
	
	-- загружаем изображения
	
	-- изображение фона
	circuit.back_image = __cast(_owner.GetWidget(_T("back"), true), sf.gui.CImageWidget)
	assert(circuit.back_image)
	
	-- изображения проводов
	circuit.wire_images = {}
	for i = 1, 4 do
		local image = __cast(_owner.GetWidget(_T("wire"..i), true), sf.gui.CImageWidget)
		if image then
			table.insert(circuit.wire_images, image)
			image.AddFlags(sf.gui.CBaseWidget.FlagHidden)
		end
	end
	
    -- изображения выключенных "плохих" лампочек
    circuit.off_bad_lamp_images = {}
	for i = 1, 4 do
		local image = __cast(_owner.GetWidget(_T("off_bad_lamp"..i), true), sf.gui.CImageWidget)
		if image then
			table.insert(circuit.off_bad_lamp_images, image)
			image.AddFlags(sf.gui.CBaseWidget.FlagHidden)
		end
	end

    -- изображения включенных "плохих" лампочек
    circuit.on_bad_lamp_images = {}
	for i = 1, 4 do
		local image = __cast(_owner.GetWidget(_T("on_bad_lamp"..i), true), sf.gui.CImageWidget)
		if image then
			table.insert(circuit.on_bad_lamp_images, image)
			image.AddFlags(sf.gui.CBaseWidget.FlagHidden)
		end
	end

    -- изображения выключенных "хороших" лампочек
    circuit.off_good_lamp_images = {}
	for i = 1, 4 do
		local image = __cast(_owner.GetWidget(_T("off_good_lamp"..i), true), sf.gui.CImageWidget)
		if image then
			table.insert(circuit.off_good_lamp_images, image)
			image.AddFlags(sf.gui.CBaseWidget.FlagHidden)
		end
	end

    -- изображения включенных "хороших" лампочек
    circuit.on_good_lamp_images = {}
	for i = 1, 4 do
		local image = __cast(_owner.GetWidget(_T("on_good_lamp"..i), true), sf.gui.CImageWidget)
		if image then
			table.insert(circuit.on_good_lamp_images, image)
			image.AddFlags(sf.gui.CBaseWidget.FlagHidden)
		end
	end

	--local size = circuit.back_image.GetSize()
	local rect = circuit.params.game_rect or sf.misc.FloatRect(0, 0, circuit.back_image.GetSize().X, circuit.back_image.GetSize().Y)
	
	assert(#circuit.params.items > 0)
	assert(#circuit.params.items[1] > 0)
	
	-- создаем элементы электрической цепи		
	circuit.items = {}
	
	local rows, cols = #circuit.params.items, #circuit.params.items[1]
	local item_size = sf.misc.FloatVector(rect.Width / cols, rect.Height / rows)
	
	for i = 1, rows do
		for j = 1, cols do
			local connections = {}
			for k = 1, 4 do connections[k] = circuit.params.items[i][j][k+1] end
			
			local center = sf.misc.FloatVector(rect.X + j*item_size.X - item_size.X/2.0, rect.Y + i*item_size.Y - item_size.Y/2.0)
			
			local item_type = circuit.params.items[i][j][1]
			local widgets_src = {}			
			local subtype, angle
			
			if item_type == circuit.enum.wire then
				subtype, angle = circuit.GetItemSubtypeAndAngleByConnections(circuit.item_wire_types, connections)
				assert(subtype and angle)
				print('connections = '..connections[1]..','..connections[2]..','..connections[3]..','..connections[4])
				print('subtype = '..subtype)				
				io.flush()
				table.insert(widgets_src, circuit.wire_images[subtype])
			elseif item_type == circuit.enum.bad_lamp then
				subtype, angle = circuit.GetItemSubtypeAndAngleByConnections(circuit.item_lamp_types, connections)
				assert(subtype and angle)
				table.insert(widgets_src, circuit.off_bad_lamp_images[subtype])
                table.insert(widgets_src, circuit.on_bad_lamp_images[subtype])
			elseif item_type == circuit.enum.good_lamp then
				subtype, angle = circuit.GetItemSubtypeAndAngleByConnections(circuit.item_lamp_types, connections)
				assert(subtype and angle)
				table.insert(widgets_src, circuit.off_good_lamp_images[subtype])
                table.insert(widgets_src, circuit.on_good_lamp_images[subtype])
			end

			assert(#widgets_src>0)
			
            local widgets = {}
            for _, ws in pairs(widgets_src) do
                local widget = sf.gui.CImageWidget(ws.GetImage(), _T(""), 0, 0)
                local widget_ptr = sf.gui.CImageWidgetPtrT(widget)
                _owner.AddWidget(widget_ptr)
                table.insert(widgets, widget)
			end

			local item = 
			{
				type = item_type,
				subtype = subtype,
				center = center,
				connections = connections,
				widgets = widgets,
				angle = angle
			}
			
			table.insert(circuit.items, item)
			
			circuit.UpdateItemWidgetsRotation(item)            
		end
	end
	
	circuit.UpdateMatrixConnections()
    circuit.UpdateItems()
end

function circuit.UpdateItemWidgetsRotation(_item)
    for _, w in pairs(_item.widgets) do
        local size = w.GetSize()
        local offset = w.GetOffset()
        local matrix = sf.misc.MatrixTranslation(-offset.X - size.X / 2.0, -offset.Y - size.Y / 2.0)
        matrix = matrix * sf.misc.MatrixRotation(_item.angle, 0.0, 0.0)
        matrix = matrix * sf.misc.MatrixTranslation(_item.center.X, _item.center.Y)
        w.SetRotation(matrix)
    end
end

function circuit.GetItemByPos(_pos)
	for _, i in pairs(circuit.items) do
        for _, w in pairs(i.widgets) do
            if w.GetPoly().IsContains(_pos.X, _pos.Y) then
                return i
            end
        end
	end
	return nil
end

function circuit.RotateItemLeft(_item)
	sound("select_part")
	local first = _item.connections[1]
	for i = 1, 3 do _item.connections[i] = _item.connections[i+1] end
	_item.connections[4] = first
	_item.angle = _item.angle - math.pi / 2.0
	circuit.UpdateItemWidgetsRotation(_item)	
end

function circuit.RotateItemRight(_item)
	sound("select_part")
	local last = _item.connections[4]
    for i = 1, 3 do _item.connections[5-i] = _item.connections[4-i] end
	_item.connections[1] = last
	_item.angle = _item.angle + math.pi / 2.0
	circuit.UpdateItemWidgetsRotation(_item)	
end

function circuit.UpdateMatrixConnections()
	circuit.matrix_connections = {}
	
	local items_count = #circuit.items + 1 -- +1 - источник питания
	
	-- заполняем матрицу связности нолями
	for i = 1, items_count do
		circuit.matrix_connections[i] = {}
		for j = 1, items_count do
			circuit.matrix_connections[i][j] = 0
		end
	end
	
	local rows, cols = #circuit.params.items, #circuit.params.items[1]
	
	-- пробегаем по всем элементам. если два соседних элемента имеют связи, навправленные друг к другу, то 
	-- выставляем в матрице связности в соответствующих им ящейках 1
	for i, item in pairs(circuit.items) do
		local row = math.floor( (i - 1) / cols) + 1
		local col = i - (row - 1) * cols
		
		-- проверяем свзяь с правым элементом
		if item.connections[3] == 1 then -- если ли связь справа
			if col < cols then -- есть ли справа элемент
				local left_item_num = i + 1
				local left_item = circuit.items[left_item_num]
				if left_item.connections[1] == 1 then -- есть ли у правого элемента связь слева
					circuit.matrix_connections[i+1][left_item_num+1] = 1
					circuit.matrix_connections[left_item_num+1][i+1] = 1
				end
			end
		end
		
		-- проверяем свзяь с нижним элементом
		if item.connections[4] == 1 then -- если ли связь снизу
			if row < rows then -- есть ли снизу элемент
				local bottom_item_num = row * cols + col
				local bottom_item = circuit.items[bottom_item_num]
				if bottom_item.connections[2] == 1 then -- есть ли у нижнего элемента связь сверху
					circuit.matrix_connections[i+1][bottom_item_num+1] = 1
					circuit.matrix_connections[bottom_item_num+1][i+1] = 1
				end
			end
		end
	end
	
	-- проверяем, подключен ли источник питания к элементу
	local battery_item_nums = circuit.params.battery.item_nums
	local battery_item_num = (battery_item_nums[2]-1) * cols + battery_item_nums[1]
	local battery_item = circuit.items[battery_item_num]
	local battery_item_connection = circuit.params.battery.item_connection
	if battery_item.connections[battery_item_connection] == 1 then
		circuit.matrix_connections[1][battery_item_num+1] = 1
		circuit.matrix_connections[battery_item_num+1][1] = 1
	end
end

function circuit.CrawlGraph(_i)	
	for j = 1, #circuit.matrix_connections[_i] do
		if circuit.matrix_connections[_i][j] == 1 and not circuit.crawl_graph.visited[j] then
			circuit.crawl_graph.visited[j] = 1
			circuit.CrawlGraph(j)
		end
	end
end

function circuit.UpdateItems(_owner)
    circuit.crawl_graph = {}
    circuit.crawl_graph.visited = {}
	circuit.CrawlGraph(1)
    
    circuit.all_bad_lamps_turn_off = true
    circuit.all_good_lamps_turn_on = true

    for i, item in pairs(circuit.items) do
        if item.type ~= circuit.enum.wire then
            if circuit.crawl_graph.visited[i+1] then  
                item.widgets[1].AddFlags(sf.gui.CBaseWidget.FlagHidden)
                item.widgets[2].RemFlags(sf.gui.CBaseWidget.FlagHidden)
                if item.type == circuit.enum.bad_lamp then
                    circuit.all_bad_lamps_turn_off = false        
                end
            else
                item.widgets[2].AddFlags(sf.gui.CBaseWidget.FlagHidden)
                item.widgets[1].RemFlags(sf.gui.CBaseWidget.FlagHidden)
                if item.type == circuit.enum.good_lamp then
                    circuit.all_good_lamps_turn_on = false        
                end
            end
        end
    end
end
