function bldg_struct = readBLDGfile( bldg_full_filename )

% if input argument is not specified, prompt user to select:
if nargin == 0
    % Load pathname for building input file used in last analysis, if available, otherwise create empty string:
    if exist('tmp_bldg_path.mat','file'), load('tmp_bldg_path'); end
    if ~exist('bldg_path','var') || ~isstr(bldg_path), bldg_path = ''; end
    % Prompt user to select building input file and extract information from file:
    [bldg_filename bldg_path]= uigetfile({'*.csv','CSV (Comma delimited)(*.csv)'},'Select building input file',bldg_path);
    if any(bldg_filename~=0)
        bldg_full_filename = fullfile( bldg_path, bldg_filename );
        % Save pathname to be used as default values in next analysis:
        save('tmp_bldg_path','bldg_path');
    else
        err = errordlg('Building input file selection cancelled by user', 'Analysis cancelled');
        uiwait(err); bldg_struct.error = 1; return;
    end
end

bldg_struct.inf_coeff = []; % initialize data structure for building information

err_dlgname = 'Error in building input file';

% open specified building input file:
fid = fopen( bldg_full_filename );

% read each row in the input file as a separate string:
file_contents = textscan( fid, '%s  ', 'delimiter', '\n', 'commentStyle', '%');

% extract a cell array from the textscan output:
file_contents = file_contents{1};

% identify the rows containing keyword entries
key_ind = strmatch('*',file_contents);

if isempty(key_ind)
    err = errordlg('No keywords (indicated by asterisks) were found in the input file', err_dlgname);
    uiwait(err); bldg_struct.error = 1; return;
end    


% total lines in file:
file_lines = length(file_contents);

% loop through keyword entries and extract specified values:
for i = 1:length(key_ind)
    
    keyword_line = file_contents{key_ind(i)};
    keyword = strtok( keyword_line(2:end), ', ');
    keyword_list{i} = keyword;
    
    if strcmp( keyword, 'UNITS' )
        % One header row (ignored)
        % Three entries expected on one line (length_units, force_units, ws_units):
        units_string = file_contents{key_ind(i)+2};
        units_string_spc = strrep( units_string, ',', ' '); % replace commas with spaces
        units = strread( strtrim(units_string_spc), '%s', 'delimiter', ' ');
        allowed_length_unit = {'ft'};
        allowed_force_unit = {'lb'};
        allowed_ws_unit = {'ft/s'};
        if length(units)~=3
            err = errordlg(['The *UNITS section contains ' num2str(length(units)) ' entries (3 expected). ' ...
                'Quoting from input file:  >> '  units_string ' <<'], err_dlgname);
            uiwait(err); bldg_struct.error = 1; return;
        elseif ~ismember(units{1}, allowed_length_unit) 
            err = errordlg(['The first entry in the *UNITS section ("' units{1} ...
                '") is not allowed: only "ft" is currently allowed for length units.'], err_dlgname);
            uiwait(err); bldg_struct.error = 1; return;
        elseif ~ismember(units{2}, allowed_force_unit)
            err = errordlg(['The second entry in the *UNITS section ("' units{2} ...
                '") is not allowed: only "lb" is currently allowed for force units.'], err_dlgname);
            uiwait(err); bldg_struct.error = 1; return;
        elseif ~ismember(units{3}, allowed_ws_unit)
            err = errordlg(['The third entry in the *UNITS section ("' units{3} ...
                '") is not allowed: only "ft/s" is currently allowed for wind speed units.'], err_dlgname);
            uiwait(err); bldg_struct.error = 1; return;
        else
            bldg_struct.length_units = units{1};
            bldg_struct.force_units = units{2};
            bldg_struct.ws_units = units{3};
        end
    elseif strcmp( keyword, 'BUILDING_DIMENSIONS' )
        % Four entries expected on one line (W, L, H, R):
        dimensions_string = file_contents{key_ind(i)+2};
        dimensions_string_spc = strrep( dimensions_string, ',', ' '); % replace commas with spaces
        dimensions = strread(strtrim(dimensions_string_spc)); % trim trailing spaces, read numerical values
        if length(dimensions)~=4
            err = errordlg(['The *BUILDING_DIMENSIONS section does not contain the expected number (4) of entries. ' ...
                'Quoting from input file:  >> '  dimensions_string ' <<'], err_dlgname );
            uiwait(err); bldg_struct.error = 1; return;
        else
            bldg_struct.d0 = dimensions; % (W, L, H, R)
        end

    elseif strcmp( keyword, 'FRAME_LOCATIONS' )
        % At least one line expected with four entries per line (frame #, previous y, current y, next y):
        if i == length(key_ind)
            frame_loc_str = file_contents(key_ind(i)+2:file_lines);
        else
            frame_loc_str = file_contents(key_ind(i)+2:key_ind(i+1)-1);
        end
        frame_loc_str_spc = strrep( frame_loc_str, ',', ' ' ); % replace commas with spaces
        n_f = size(frame_loc_str,1);
        frame_coords = zeros(n_f,3); % initialize matrix for storing numerical values
        for j = 1:n_f
            % trim trailing spaces, read numerical values, and place in matrix:
            row_j = strread(strtrim(frame_loc_str_spc{j,:}));
            if length(row_j)==4
                if row_j(1)~=j
                    err = errordlg(['The frame number (' num2str(row_j(1)) ') in row '  num2str(j) ' of the *FRAME_LOCATIONS section ' ...
                        ' is not allowed. Frame numbers must start at 1 and increment by 1.'], err_dlgname );
                    uiwait(err); bldg_struct.error = 1; return;
                else
                    frame_coords(j,:) = row_j(2:4);
                end
            else
                err = errordlg(['Row ' num2str(j) ' of the *FRAME_LOCATIONS section contains ' num2str(length(row_j))  ' entries (4 expected). ' ...
                    'Quoting from input file:  >> '  frame_loc_str{j} ' <<' ], err_dlgname );
                uiwait(err); bldg_struct.error = 1; return;
            end
        end
        bldg_struct.frame_coords = frame_coords;
        
    elseif strcmp( keyword, 'TERRAIN' )
        % One or three lines expected
        if i == length(key_ind)
            terrain_lines = file_contents(key_ind(i)+1:file_lines);
        else
            terrain_lines = file_contents(key_ind(i)+1:key_ind(i+1)-1);
        end
        terrain_string = terrain_lines{1};
        terrain_string_spc = strrep( terrain_string, ',', ' '); % replace commas with spaces
        terrain = strread( strtrim(terrain_string_spc), '%s', 'delimiter', ' ');
        if length(terrain)~=1
            err = errordlg(['The *TERRAIN section does not contain the expected number (1) of entries. ' ...
                'Quoting from input file:  >> ' terrain_string ' <<'], err_dlgname);
            uiwait(err); bldg_struct.error = 1; return;
        elseif ~ismember(terrain,{'Open_Country','Suburban','Directional'})
            err = errordlg(['The entry in the *TERRAIN section ("' terrain{1} ...
                '") is not permitted: only "Open_Country", "Suburban", "Directional" are allowed.'], err_dlgname);
            uiwait(err); bldg_struct.error = 1; return;
        end
        bldg_struct.terrain = terrain{1};
        if strcmp(terrain,'Open_Country') & size(terrain_lines,1)==1
            z0 = 0.03*3.2808; % roughness length (ft)
        elseif strcmp(terrain,'Suburban') & size(terrain_lines,1)==1
            z0 = 0.3*3.2808; % roughness length (ft)
        elseif strcmp(terrain,'Directional') & size(terrain_lines,1)==3
            z0_str = terrain_lines{3};
            z0_str_spc = strrep( z0_str, ',', ' '); % replace commas with spaces
            z0 = strread(strtrim(z0_str_spc)); % trim trailing spaces, read numerical values
            if length(z0)~=8
                err = errordlg({['The third line of the *TERRAIN section contains '  num2str(length(z0)) ...
                    ' entries (8 expected). Quoting from input file:'], [' >> '  z0_str ' <<']}, err_dlgname );
                uiwait(err); bldg_struct.error = 1; return;
            end
        else
            err = errordlg(['Unexpected number of lines (' num2str(size(terrain_lines,1))  ') in *TERRAIN section. ' ...
                'Three lines expected only if "Directional" terrain is specified; ' ...
                'one line expected for "Open_Country" or "Suburban" terrain'], err_dlgname);
            uiwait(err); bldg_struct.error = 1; return;
        end
        bldg_struct.z0 = z0;

    elseif strcmp( keyword, 'RESPONSE_NAMES' )
        % At least one line expected with two entries per line (response #, name):
        if i == length(key_ind)
            resp_names_str = file_contents(key_ind(i)+2:file_lines);
        else
            resp_names_str = file_contents(key_ind(i)+2:key_ind(i+1)-1);
        end
        n_r1 = size(resp_names_str,1);
        for j = 1:n_r1
            % trim trailing spaces, read numerical values, and place in matrix:
            row_j = resp_names_str{j};
            [num_str, rem] = strtok( row_j, ',');
            [resp_str, rem] = strtok( rem, ',');
            [runit_str, rem] = strtok( rem, ',' );
            if isempty(num_str) || str2num(num_str)~=j
                err = errordlg(['The response number (' num_str ') in row '  num2str(j) ' of the *RESPONSE_NAMES section ' ...
                    ' is not allowed. Response numbers must start at 1 and increment by 1.'], err_dlgname );
                uiwait(err); bldg_struct.error = 1; return;
            elseif isempty(resp_str) | isempty(runit_str)
                err = errordlg({['Row ' num2str(row_j(1)) ' of the *RESPONSE_NAMES section contains fewer than the expected ' ...
                    'number of entries (3). Quoting from input file:'],['     ' row_j ]}, err_dlgname );
                uiwait(err); bldg_struct.error = 1; return;
            elseif ~isempty(setdiff(rem,', '))
                err = errordlg({['Row ' num2str(row_j(1)) ' of the *RESPONSE_NAMES section contains more than the expected ' ...
                    'number of entries (3). Quoting from input file:'],['     ' row_j ]}, err_dlgname );
                uiwait(err); bldg_struct.error = 1; return;
            else
                resp_names{j} = resp_str;
                resp_units{j} = runit_str;
            end
        end
        bldg_struct.resp_names = resp_names;
        bldg_struct.resp_units = resp_units;
        
    elseif strcmp( keyword, 'ATTACHMENT_LOCATIONS' )
        % At least four rows expected, with three entries per row: (index, face #, s-coordinate)
        if i == length(key_ind)
            attach_str = file_contents(key_ind(i)+2:file_lines);
        else
            attach_str = file_contents(key_ind(i)+2:key_ind(i+1)-1);
        end
        attach_str_spc = strrep( attach_str, ',', ' ' ); % replace commas with spaces
        n_a1 = size(attach_str_spc,1); % number of attachment points
        % check number of entries in first row:
        for j = 1:n_a1
            row_j = strread(strtrim(attach_str_spc{j,:}));
            if row_j(1)~=j
                err = errordlg(['The index number (' num2str(row_j(1)) ') in row '  num2str(j) ' of the *ATTACHMENT_LOCATIONS section ' ...
                    'is not allowed. Index numbers must start at 1 and increment by 1.'], err_dlgname );
                uiwait(err); bldg_struct.error = 1; return;
            elseif length(row_j)~=3
                err = errordlg({['Row ' num2str(j) ' of the *ATTACHMENT_LOCATIONS section does not contain '...
                    'the expected number of entries (3). Quoting from input file:'],...
                    ['     >> '  inf_coeff_str{j} ' <<' ]}, err_dlgname );
                uiwait(err); bldg_struct.error = 1; return;
            end
            attach_pts(j,:) = row_j(2:3);
        end
        for j = 1:4
            if any(attach_pts(find(attach_pts(:,1)==j),2)~=sort(attach_pts(find(attach_pts(:,1)==j),2)))
                err = errordlg(['The s-coordinates for face ' num2str(j) ' in the *ATTACHMENT_LOCATIONS section are not ' ...
                    'sorted in ascending order, as is required.'], err_dlgname );
                uiwait(err); bldg_struct.error = 1; return;
            end
        end
        bldg_struct.attach_pts = attach_pts;
        
    elseif strcmp( keyword, 'INFLUENCE_COEFFICIENTS' )
        % At least four rows expected, with at least two entries per row:
        if i == length(key_ind)
            inf_coeff_str = file_contents(key_ind(i)+2:file_lines);
        else
            inf_coeff_str = file_contents(key_ind(i)+2:key_ind(i+1)-1);
        end
        inf_coeff_str_spc = strrep( inf_coeff_str, ',', ' ' ); % replace commas with spaces
        n_a2 = length(inf_coeff_str_spc); % number of attachment points
        % check number of entries in first row:
        n_cols = length(strread(strtrim( inf_coeff_str_spc{1,:} ))); % total number of columns
        n_r2 = n_cols-1; % number of influence coefficient vectors defined (first columns is index number)
        inf_coeff = zeros(size(inf_coeff_str_spc,1),n_r2); % initialize matrix for storing numerical values
        for j = 1:size(inf_coeff,1)
            % trim trailing spaces, read numerical values, and place in matrix:
            row_j = strread(strtrim(inf_coeff_str_spc{j,:}));
            if row_j(1)~=j
                err = errordlg(['The index number (' num2str(row_j(1)) ') in row '  num2str(j) ' of the *INFLUENCE_COEFFICIENTS section ' ...
                    'is not allowed. Index numbers must start at 1 and increment by 1.'], err_dlgname );
                uiwait(err); bldg_struct.error = 1; return;
            elseif length(row_j)~=n_cols
                err = errordlg({['Row ' num2str(j) ' of the *INFLUENCE_COEFFICIENTS section contains a different number of entries than row 1. ' ...
                    'Quoting from input file:'],['   Line 1:  >> '  inf_coeff_str{1} ' <<'], ...
                    ['   Line ' num2str(j) ':  >> '  inf_coeff_str{j} ' <<' ]}, err_dlgname );
                uiwait(err); bldg_struct.error = 1; return;
            end
            inf_coeff(j,:) = row_j(2:end);
        end
        bldg_struct.inf_coeff = inf_coeff;
    
    
    else
        err = errordlg(['The keyword ' keyword ' is unrecognized.'], err_dlgname);
        uiwait(err); bldg_struct.error = 1; return;
    end
    
end


% required keyword entries:
required_keywords = {'UNITS','BUILDING_DIMENSIONS','TERRAIN','FRAME_LOCATIONS','RESPONSE_NAMES','ATTACHMENT_LOCATIONS','INFLUENCE_COEFFICIENTS'};

% check that all required keywords are included:
missing_keywords = setdiff( required_keywords, keyword_list );
missing_key_char = [];
if ~isempty(missing_keywords)
    for i = 1:length(missing_keywords)
        missing_key_char = [missing_key_char missing_keywords{i}];
        if i ~= length(missing_keywords)
            missing_key_char = [missing_key_char ', '];
        end
    end
    err = errordlg({'The following required keywords are missing from the input file:',['     ' missing_key_char ]}, err_dlgname);
    uiwait(err); bldg_struct.error = 1; return;
end

if n_a1~=n_a2
    errordlg(['The number of columns in the *ATTACHMENT_LOCATIONS section (' num2str(n_a1) ') ' ...
        'does not match the number of columns in the *INFLUENCE_COEFFICIENTS section (' num2str(n_a2) ').'], err_dlgname );
    bldg_struct.error = 1; return;
elseif n_r1~=n_r2
    errordlg(['The number of responses defined in the *RESPONSE_NAMES section (' num2str(n_r1) ') ' ...
        'does not match the number of responses in the *INFLUENCE_COEFFICIENTS section (' num2str(n_r2) ').'], err_dlgname );
    bldg_struct.error = 1; return;
end
n_a = n_a1;
n_r = n_r1;
    
if frame_coords(1,1)==frame_coords(1,2) & frame_coords(1,1)>(frame_coords(1,3)-frame_coords(1,2))/5
    button = questdlg(['The frame at y = ' num2str(frame_coords(1,2)) ' has been selected for analysis, but no previous frame has been specified. ' ...
        'Therefore, pressures acting on the building surface for y < '  num2str(frame_coords(1,2)) ' will be neglected in computing the ' ...
        'response of this frame. Press "Continue" to neglect these pressures. To define a previous frame, press "Cancel" to abort the ' ...
        'analysis; then modify the building input file and restart the analysis.'],...
        'Warning: Questionable frame definition', 'Continue', 'Cancel', 'Cancel');
    uiwait(button);
    if strcmp(button,'Cancel')
        bldg_struct.error = 1; return;
    end
end
if frame_coords(end,2)==frame_coords(end,3) & L-frame_coords(end,3)>(frame_coords(end,2)-frame_coords(end,1))/5
    button = questdlg(['The frame at y = ' num2str(frame_coords(end,2)) ' has been selected for analysis, but no subsequent frame has been specified. ' ...
        'Therefore, pressures acting on the building surface for y > '  num2str(frame_coords(end,2)) ' will be neglected in computing the ' ...
        'response of this frame. Press "Continue" to neglect these pressures. To define a subsequent frame, press "Cancel" to abort the ' ...
        'analysis; then modify the building input file and restart the analysis.'],...
        'Warning: Questionable frame definition', 'Continue', 'Cancel', 'Cancel');
    uiwait(button);
    if strcmp(button,'Cancel')
        bldg_struct.error = 1; return;
    end
end

