function txObj = WIFI_TX(wifi_params,txObj,i_mcs)
% WIFI transmitter function


%% DEFINE MODEL PARAMETERS

% define parameters of used MCS
MCS = wifi_params.MCS(i_mcs);

% define modulation parameters
hMod = wifi_params.modulation(log2(MCS.M)).hMod;
modNorm = wifi_params.modulation(log2(MCS.M)).hMod_factor;

% define channel coding parameters


n_fft = wifi_params.mapping.n_fft;
n_tot = wifi_params.mapping.n_tot;

LENGTH = wifi_params.general.LENGTH;
service_length = wifi_params.mapping.service_length;

N_sym = wifi_params.coding.N_sym;

%% GENERATE PSDU INFORMATION

% PSDU_tx = randi([0 1],LENGTH*8,1);
PSDU_tx = ones(LENGTH*8,1);
% PSDU_tx_plus_service = [zeros(service_length,1); PSDU_tx];
PSDU_tx_plus_service = [ones(service_length,1); PSDU_tx];
%% SCRAMBLING
switch wifi_params.coding.type
    case 'BCC'
        if MCS.N_es == 1
            DEC_offset = wifi_params.coding.tail_length;
        elseif MCS.N_es == 2
            DEC_offset = 2*wifi_params.coding.tail_length;
        else
            error('Wrong number of BCC encoders');
        end
        scramblerIn = [PSDU_tx_plus_service; zeros(wifi_params.coding.N_pad_bits,1); zeros(DEC_offset,1)];
        scramblerIn = [PSDU_tx_plus_service; ones(wifi_params.coding.N_pad_bits,1); zeros(DEC_offset,1)];
        
        scr_seed = wifi_params.scrambling.scr_seed;
        scramblingSeq = BCC_ScramblingSeqGen(scramblerIn, scr_seed);
        scramblerOut = xor(scramblerIn, scramblingSeq);
        
        % tail of encoderIn must be zero (6 or 12 bits)
        scramblerOut(end-DEC_offset+1:end) = zeros(DEC_offset,1);
%         scramblerOut = [PSDU_tx_plus_service; ones(wifi_params.coding.N_pad_bits,1); zeros(DEC_offset,1)];
    case 'LDPC'
        
end

%% CHANNEL CODING
if strcmp(wifi_params.coding.type,'BCC')
    encoderIn = scramblerOut;
    switch MCS.CR
        case 1/2
            puncpat = wifi_params.coding.puncturing(1).pattern;
        case 2/3
            puncpat = wifi_params.coding.puncturing(2).pattern;
        case 3/4
            puncpat = wifi_params.coding.puncturing(3).pattern;
        case 5/6
            puncpat = wifi_params.coding.puncturing(4).pattern;
    end
    trellis = wifi_params.coding.trellis;
    if ~istrellis(trellis)
        error('Wrong trellis structure');
    end
    hEnc = comm.ConvolutionalEncoder(trellis,...
        'PuncturePatternSource','Property',...
        'PuncturePattern',puncpat.',...
        'FinalStateOutputPort',true); %);
    
    [encoderOut, CONV_FSTATE]= step(hEnc,encoderIn);
    if CONV_FSTATE ~= 0
        error('BCC is not performed correctly - final state of the encoder is nonzero');
    end
    encoderIn = encoderIn(1:end-DEC_offset); % save to results structure without zeros tail bits
    
else
    % generate encoder structure
    PSDU_tx_plus_service_tmp = PSDU_tx_plus_service;
    
    
    hEnc = comm.LDPCEncoder(wifi_params.coding.UsedLDPC_matrix);
    LDPC_N = size(wifi_params.coding.UsedLDPC_matrix,2);
    LDPC_K = LDPC_N - size(wifi_params.coding.UsedLDPC_matrix,1);
    % mux PSDU plus SERVICE into codewords
    PSDU_tx_plus_service_cw = cell(wifi_params.coding.N_cw,1);
    
    for i_cw = 1:wifi_params.coding.N_cw
        PSDU_tx_plus_service_cw{i_cw} = PSDU_tx_plus_service_tmp(1:wifi_params.coding.shortening.n_bitwoshrt_cw(i_cw),1);
        if i_cw ~= wifi_params.coding.N_cw
            PSDU_tx_plus_service_tmp = PSDU_tx_plus_service_tmp(wifi_params.coding.shortening.n_bitwoshrt_cw(i_cw)+1:end,1);
        end
    end
    
    % inserting shortening bits according to N_shrt and N_cw parameter
    shortenedCodeword = zeros(wifi_params.coding.N_bpcw,1);
    for i_cw = 1:wifi_params.coding.N_cw
        actualCodeword = PSDU_tx_plus_service_cw{i_cw};
        actualCodeword = [actualCodeword; zeros(wifi_params.coding.shortening.n_shrt_cw(i_cw),1)];
        PSDU_tx_plus_service_cw{i_cw} = actualCodeword;
    end
    
    % LDPC encoding process for each codeword
    encoded_PSDU_tx = cell(wifi_params.coding.N_cw,1);
    encoded_PSDU_tx_NoRepeated = cell(wifi_params.coding.N_cw,1); % #1
    
    for i_cw = 1:wifi_params.coding.N_cw
        
        inputEncoderCodeword = PSDU_tx_plus_service_cw{i_cw};
        %         dataIn_cw{i_cw} = inputEncoderCodeword;
        LDPC_encoderOut = hEnc.step(inputEncoderCodeword);
        % demux encoded data and parity bits
        punctured_parityBits = LDPC_encoderOut(wifi_params.coding.puncturing.parity_ind(i_cw,:)); % include puncturing
        % discard shortening bits
        shortenedEncoderCodeword = LDPC_encoderOut(wifi_params.coding.shortening.bitwoshrt_ind(i_cw,:));
        
        % mux encodedData and punctured parity bits
        encodedDataPlusParity = [shortenedEncoderCodeword; punctured_parityBits];
        encodedDataPlusParity_NoRepeated = encodedDataPlusParity; % #1
        % repeating bits
        if wifi_params.coding.repeating.n_rep_cw(i_cw) ~= 0
            %             disp('Data should be repeated')
            %             disp(['CW#',num2str(i_cw),'Bitu po kan kodovani ', num2str(wifi_params.coding.shortening.n_bitwoshrt_cw(i_cw)+sum(wifi_params.coding.puncturing.parity_ind(i_cw,:))),' | Bitu k dispozici ',num2str(wifi_params.coding.N_bpcw+sum(wifi_params.coding.puncturing.parity_ind(i_cw,:)))])
            %
            if wifi_params.coding.puncturing.N_punc ~= 0
                %                 disp(['NOTE: In this case repetition should not be performed'])
            else
                encodedDataPlusParity = [encodedDataPlusParity; encodedDataPlusParity(1:wifi_params.coding.repeating.n_rep_cw(i_cw))];
            end
        end
        
        encoded_PSDU_tx{i_cw} = encodedDataPlusParity;
        encoded_PSDU_tx_NoRepeated{i_cw} = encodedDataPlusParity_NoRepeated;  % #1
    end
    
    if length(vertcat(encoded_PSDU_tx{:})) ~= wifi_params.coding.N_avbits
        error('Error in puncturing operation --> LDPC encoder')
    end
    
    %     dataIn = vertcat(PSDU_tx_plus_service_cw{:});
    encoderIn = PSDU_tx_plus_service;
    
    % LDPC encoding parser
    encoderOut = vertcat(encoded_PSDU_tx{:});
    
end

%% PARSING (only if 2 BCC are used)

%% INTERLEAVING
switch wifi_params.coding.type
    case 'BCC'
        interleaverIn = reshape(encoderOut,wifi_params.interleaving.N_cbpss,[]);
        % first permutation
        permOut = interleaverIn(wifi_params.interleaving.i{1}+1,:);
        % second permutation
        permOut = permOut(wifi_params.interleaving.j{1}+1,:);
        % third permutation (only if Nss > 1)
        if wifi_params.MCS(i_mcs).N_ss > 1
            permOut = permOut(wifi_params.interleaving.r{1}+1,:);
        end
        interleaverOut = reshape(permOut,[],1);
        
%         interleaverOut = encoderOut;
    case 'LDPC'
        interleaverOut = encoderOut;
end
%% MODULATION MAPPING
modulatedDataSymbol = step(hMod,interleaverOut)*modNorm;

if strcmp(wifi_params.coding.type,'LDPC')
    encoderOut = vertcat(encoded_PSDU_tx_NoRepeated{:});
end
%% generate preambles and concatenate to frame
% switch txObj.mode
%     case 'Greenfield (HT)'
%         % preamble
%         % HT-STF
%
%         % HT-LTF1
%
%         % HT-SIG
%
%         % HT-LTFn
%
%     case 'Mixed (Non-HT)'
%         error('chyba');
%     otherwise
%         error('Wrong operation mode');
% end

% mapping to frame (w/o pilots, etc.)
x = zeros(wifi_params.mapping.n_data+wifi_params.mapping.n_pilot+numel(wifi_params.mapping.i_DC),wifi_params.mapping.n_data_symbols_per_frame,1);
% map payload
% xRGrid(wifi_params.mapping.map_data) = modSignal_data; % asi zbytecne...

%% create ofdm signal

% add pilots
% mod_pilots = ones(4,n_sf);
modulatedPilotSymbols = randsrc(N_sym*wifi_params.mapping.n_pilot,1,[-1 1]); % dodelat dle normy! BPSK = {-1,+1}

% x = zeros(length(wifi_params.mapping.map_data),Nsym)
mapData = wifi_params.mapping.map_data;
x(mapData) = modulatedDataSymbol;
% x(mapData) = ones(sum(sum(wifi_params.mapping.map_data)),1)*999;

% phase rotation
phase_rot_temp = ones(size(x));
phase_rot_temp(wifi_params.mapping.map_phase_rotation) = wifi_params.mapping.phase_rotation_factor;
x = x.*phase_rot_temp;


mapPilots = wifi_params.mapping.map_pilots;
x(mapPilots) = modulatedPilotSymbols;
mapDC = wifi_params.mapping.map_DC;
x(mapDC) = zeros(numel(wifi_params.mapping.i_DC)*N_sym,1);
% [mod_data(1:7,:); mod_pilots(1,:); mod_data(8:21,:); mod_pilots(2,:); mod_data(22:27,:); zeros(1,n_sf); mod_data(28:33,:); mod_pilots(3,:); mod_data(34:47,:); mod_pilots(4,:); mod_data(48:54,:)];

x_dc_padded = [x; zeros(n_fft-n_tot-numel(wifi_params.mapping.i_DC),N_sym)];
x_shifted = circshift(x_dc_padded,ceil((n_fft-n_tot-numel(wifi_params.mapping.i_DC))/2));

% image(abs(real(y_shifted))*20)
x_ifft = (n_fft/sqrt(n_tot))*ifft(x_shifted,n_fft);

cyclic_pref = x_ifft(wifi_params.cprefix.indices,:);

x_cp_ifft = [cyclic_pref; x_ifft];
x_s = reshape(x_cp_ifft,1,[]);
% x_s = [x_s, zeros(1,wifi_params.coexistence.N_wifi_all_symbols-length(x_s))];

% save outputs
txObj.output.PSDU = PSDU_tx; % user traffic data without overhead
txObj.output.PSDU_plus_service = PSDU_tx_plus_service; % user traffic data + SERVICE zeros field but without overhead

txObj.output.coded_data = encoderIn; % encoder input - user traffic data + SERVICE zeros field with overhead
txObj.output.uncoded_data = interleaverOut; % encoder output (raw data)

% save('tx_test.mat','PSDU_tx','PSDU_tx_plus_service','encoderIn','encoderOut');

txObj.output.x_s = x_s;
end