function config = rok_room_generate_rectangles (room, config_file, debug)
    % config = ROK_ROOM_GENERATE_RECTANLES (room, config, debug)
    %
    % Generates rectangles configuration for a room.
    %
    % Input:
    %  - room = room configuration structure, obtained by
    %    ROK_ROOM_CREATE(), or room configuration filename
    %  - config_file = rectangles configuration INI file
    %  - debug = whether to draw debug figures
    %
    % Output:
    %  - config = rectangles config file
    %
    % Copyright (C) 2011-2012, Rok Mandeljc <rok.mandeljc@fe.uni-lj.si>
    
    % Load room config, if given as a file
    if ischar(room),
        room = rok_room_create(room);
    end
    
    % Load rectangles config, if given as a file
    config = rok_parse_config_ini(config_file);    
    
    % Default value for debug
    if nargin < 3,
        debug = false;
    end

    % Grid
    [ X, Y ] = ndgrid(room.xVals, room.yVals);

    %% Generate rectangles for each camera
    % Preallocate
    numLocations = room.nx * room.ny;
    numCameras = numel(config.cameras);
    
    xMin = zeros(numLocations, numCameras);
    xMax = zeros(numLocations, numCameras);
    yMin = zeros(numLocations, numCameras);
    yMax = zeros(numLocations, numCameras);
    valid = false(numLocations, numCameras);
    area = zeros(numLocations, numCameras);
    originalHeight = zeros(numLocations, numCameras);
    originalWidth = zeros(numLocations, numCameras);
    frameWidth = zeros(1, numCameras);
    frameHeight = zeros(1, numCameras);
    
    % Generate
    for c = config.cameras,
        fprintf(1, 'View #%d\n', c);
        tic;
        [ xMin(:, c), xMax(:, c), yMin(:, c), yMax(:, c), valid(:, c), area(:,c), originalWidth(:,c), originalHeight(:,c), frameWidth(:, c), frameHeight(:, c) ] = rok_generate_rectangles_for_view(room, config, c, X, Y, debug);
        fprintf(1, 'Generated in %f seconds...\n', toc);
    end

    %% Draw coverage
    if debug,
        fig = figure;
        set(fig, 'Name', 'Cell coverage');
        hold on;
        grid on;
        
        surface( room.xVals, room.yVals, reshape( sum(valid, 2), room.nx, room.ny )' );
        mesh( room.xVals, room.yVals, zeros(room.ny, room.nx) );
        xlabel('X [m]');
        ylabel('Y [m]');
        set(gca, 'View', [90 90]);
    end


    %% Save
    config.frameWidth = frameWidth;
    config.frameHeight = frameHeight;
    
    config.rects.xMin = xMin;
    config.rects.xMax = xMax;
    config.rects.yMin = yMin;
    config.rects.yMax = yMax;
    config.rects.area = area;
    config.rects.valid = valid;
    config.rects.originalWidth = originalWidth;
    config.rects.originalHeight = originalHeight;

    config.room = room;

    %% Store config?
    if nargout < 1,
        [filename, pathname] = uiputfile('config-rectangles.mat', 'Save config as');
        if filename ~= 0,
            save(fullfile(pathname, filename), '-struct', 'config');
        end
    end
end




function [ xMin, xMax, yMin, yMax, valid, area, originalWidth, originalHeight, frameWidth, frameHeight ] = rok_generate_rectangles_for_view (room, config, c, X, Y, debug)
    % Load camera calibration
    calibrationData = load( rok_format_filename(config.calibration_data_format, 'camera', c) );
    
    % Load image
    I = imread( rok_format_filename(config.calibration_image_format, 'camera', c) );
    I = imresize(I, config.scaling);
    I = im2double( I );
    frameWidth = size(I, 2);
    frameHeight = size(I, 1);
    
    % Load mask
    Imask = im2bw( imread( rok_format_filename(config.mask_format, 'camera', c) ) );
    Imask = imresize(Imask, config.scaling);
    
    %% *** Project grid ***
    % NOTE: we are using the following point annotation
    % A-------B
    % |       |
    % |   E   |
    % |       |
    % D-------C
    
    % Project the bottom grid
    Z = config.height_bottom * ones(size(X));

    bottom.A = rok_camera_project_points( [ X(:)' - room.cell_size(1)/2; Y(:)' - room.cell_size(2)/2; Z(:)' ], calibrationData, config.scaling);
    bottom.B = rok_camera_project_points( [ X(:)' + room.cell_size(1)/2; Y(:)' - room.cell_size(2)/2; Z(:)' ], calibrationData, config.scaling);
    bottom.C = rok_camera_project_points( [ X(:)' + room.cell_size(1)/2; Y(:)' + room.cell_size(2)/2; Z(:)' ], calibrationData, config.scaling);
    bottom.D = rok_camera_project_points( [ X(:)' - room.cell_size(1)/2; Y(:)' + room.cell_size(2)/2; Z(:)' ], calibrationData, config.scaling);
    bottom.E = rok_camera_project_points( [ X(:)';                       Y(:)';                       Z(:)' ], calibrationData, config.scaling);

    % Project the top grid
    Z = config.height_top * ones(size(X));

    top.A = rok_camera_project_points( [ X(:)' - room.cell_size(1)/2; Y(:)' - room.cell_size(2)/2; Z(:)' ], calibrationData, config.scaling);
    top.B = rok_camera_project_points( [ X(:)' + room.cell_size(1)/2; Y(:)' - room.cell_size(2)/2; Z(:)' ], calibrationData, config.scaling);
    top.C = rok_camera_project_points( [ X(:)' + room.cell_size(1)/2; Y(:)' + room.cell_size(2)/2; Z(:)' ], calibrationData, config.scaling);
    top.D = rok_camera_project_points( [ X(:)' - room.cell_size(1)/2; Y(:)' + room.cell_size(2)/2; Z(:)' ], calibrationData, config.scaling);
    top.E = rok_camera_project_points( [ X(:)';                       Y(:)';                       Z(:)' ], calibrationData, config.scaling);

    
    %% *** Compute rectangles ***
    dB = sqrt( (bottom.A(1,:) - bottom.B(1,:)).^2 + (bottom.A(1,:) - bottom.D(1,:)).^2 ); % The whole span in X direction for bottom
    dT = sqrt( (top.A(1,:) - top.B(1,:)).^2 + (top.A(1,:) - top.D(1,:)).^2 ); % The whole span in X direction for top

    d = (dB + dT) / 2; % Average of two
    xc = (bottom.E(1,:) + top.E(1,:)) / 2; % Average of top and bottom center

    xMin = xc - d/2;
    xMax = xc + d/2;
    yMin = bottom.E(2,:);
    yMax = top.E(2,:);

    % Round the values
    xMin = fix(xMin);
    xMax = fix(xMax);
    yMin = fix(yMin);
    yMax = fix(yMax);

    valid = true(1, numel(X)); % Or numel(Y), it's all the same...
    area = zeros(1, numel(X));

    % Store original rectangles' width and height
    originalWidth = xMax - xMin + 1;
    originalHeight = yMin - yMax + 1;
    
    % Debug - connected corners
    if debug,
        fig = figure;
        imshow(I);
        set(fig, 'Name', sprintf('Camera #%d: connected corners', c));
        hold on;

        plot( [ bottom.A(1,:), bottom.B(1,:), bottom.D(1,:), bottom.C(1,:) ], [ bottom.A(2,:), bottom.B(2,:), bottom.D(2,:), bottom.C(2,:) ], 'go' );
        line( [ bottom.A(1,:), bottom.D(1,:); bottom.B(1,:), bottom.C(1,:); bottom.A(1,:), bottom.B(1,:); bottom.D(1,:), bottom.C(1,:) ], ...
              [ bottom.A(2,:), bottom.D(2,:); bottom.B(2,:), bottom.C(2,:); bottom.A(2,:), bottom.B(2,:); bottom.D(2,:), bottom.C(2,:) ], 'color', 'green');
    
        plot( [ top.A(1,:), top.B(1,:), top.D(1,:), top.C(1,:) ], [ top.A(2,:), top.B(2,:), top.D(2,:), top.C(2,:) ], 'ro' );
        line( [ top.A(1,:), top.D(1,:); top.B(1,:), top.C(1,:); top.A(1,:), top.B(1,:); top.D(1,:), top.C(1,:) ], ...
              [ top.A(2,:), top.D(2,:); top.B(2,:), top.C(2,:); top.A(2,:), top.B(2,:); top.D(2,:), top.C(2,:) ], 'color', 'red');
    end
    
    % Debug - connected centres
    if debug,
        fig = figure;
        imshow(I);
        set(fig, 'Name', sprintf('Camera #%d: connected centers', c));
        hold on;
        
        plot(bottom.E(1,:), bottom.E(2,:), 'ro');
        plot(top.E(1,:), top.E(2,:), 'go');

        line([ bottom.E(1,:); top.E(1,:) ], [ bottom.E(2,:); top.E(2, :) ], 'color', 'yellow');
    end
    
    
    %% *** Filter rectangles ***

    % Filter rectangles - invalid ones have both x corners outside the image...
    % we don't do this for y coordinates, though
    invalidIdx1 = (xMin < 0 | xMin > frameWidth) & (xMax < 0 | xMax > frameWidth);

    if config.only_full_rectangles,
        nonFull = xMin < 0 | xMin > frameWidth | xMax < 0 | xMax > frameWidth | yMin < 0 | yMin > frameHeight | yMax < 0 | yMax > frameHeight;
        invalidIdx1 = invalidIdx1 | nonFull;
    end
    
    % Clamp the values
    xMin(xMin < 1) = 1;
    xMin(xMin >= frameWidth) = frameWidth - 1;

    xMax(xMax < 1) = 1;
    xMax(xMax >= frameWidth) = frameWidth - 1;

    yMin(yMin < 1) = 1;
    yMin(yMin >= frameHeight) = frameHeight - 1;

    yMax(yMax < 1) = 1;
    yMax(yMax >= frameHeight) = frameHeight - 1;
    
    % The rectangles with any of the corner being NaN are also invisible
    invalidIdx2 = isnan(xMin) | isnan(xMax) | isnan(yMin) | isnan(yMax);
    idx = find( (invalidIdx1 | invalidIdx2) == false );
    
    area(idx) = arrayfun(@(i)( sum(sum( Imask(yMax(i):yMin(i),xMin(i):xMax(i)) )) ), idx, 'UniformOutput', true);
    
    valid = area >= config.min_area * config.scaling^2;
    
    %area(idx) = validArea; % Store area for all
    
    if debug,
         % Draw rectangles
        fig = figure;
        imshow(I);
        set(fig, 'Name', sprintf('Camera #%d: rectangles', c));
        hold on;

        % Draw the invisible rects
        mask = ~valid;
        line([ xMin(mask), xMin(mask); xMax(mask), xMax(mask); xMin(mask), xMax(mask); xMin(mask), xMax(mask) ], [ yMin(mask), yMax(mask); yMin(mask), yMax(mask); yMin(mask), yMin(mask); yMax(mask), yMax(mask)], 'Color', 'red');

        mask = valid;
        line([ xMin(mask), xMin(mask); xMax(mask), xMax(mask); xMin(mask), xMax(mask); xMin(mask), xMax(mask) ], [ yMin(mask), yMax(mask); yMin(mask), yMax(mask); yMin(mask), yMin(mask); yMax(mask), yMax(mask)], 'Color', 'cyan');
    end
    
    % Return
    xMin = xMin';
    xMax = xMax';
    
    % Flip yMin and yMax
    tmp = yMin';
    yMin = yMax';
    yMax = tmp;
    
    valid = valid';
    area = area';
    originalWidth = originalWidth';
    originalHeight = originalHeight';
end

