///////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 Jon Beniston, M7RCE <jon@beniston.com>                         //
//                                                                                   //
// This program is free software; you can redistribute it and/or modify              //
// it under the terms of the GNU General Public License as published by              //
// the Free Software Foundation as version 3 of the License, or                      //
// (at your option) any later version.                                               //
//                                                                                   //
// This program is distributed in the hope that it will be useful,                   //
// but WITHOUT ANY WARRANTY; without even the implied warranty of                    //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                      //
// GNU General Public License V3 for more details.                                   //
//                                                                                   //
// You should have received a copy of the GNU General Public License                 //
// along with this program. If not, see <http://www.gnu.org/licenses/>.              //
///////////////////////////////////////////////////////////////////////////////////////
#include "DVB2.h"


//
// Update working parameters for the next frame
// This prevents parameters changing during a frame
//
void DVB2::base_end_of_frame_actions(void)
{
    if( m_params_changed )
    {
        m_format[0] = m_format[1];
        ldpc_lookup_generate();
        m_params_changed = 0;
    }
    // reset the pointer
    m_frame_offset_bits = 0;
}

//
// This configures the system and calculates
// any required intermediate values
//
int DVB2::set_configure( DVB2FrameFormat *f )
{
    int bch_bits = 0;
    int error = 0;

    if( f->broadcasting )
    {
        // Set standard parameters for broadcasting
        f->frame_type        = FRAME_NORMAL;
        f->bb_header.ts_gs   = TS_GS_TRANSPORT;
        f->bb_header.sis_mis = SIS_MIS_SINGLE;
        f->bb_header.ccm_acm = CCM;
        f->bb_header.issyi   = 0;
        f->bb_header.npd     = 0;
        f->bb_header.upl     = 188*8;
        f->bb_header.sync    = 0x47;
    }
    f->bb_header.ro = f->roll_off;
    // Fill in the mode specific values and bit lengths
    if( f->frame_type == FRAME_NORMAL )
    {
        f->nldpc = 64800;
        bch_bits = 192;
        f->bch_code = BCH_CODE_N12;
        // Apply code rate
        switch(f->code_rate )
        {
            case CR_1_4:
                f->q_val = 135;
                f->kbch  = 16008;
                f->bch_code = BCH_CODE_N12;
                break;
            case CR_1_3:
                f->q_val = 120;
                f->kbch  = 21408;
                f->bch_code = BCH_CODE_N12;
                break;
            case CR_2_5:
                f->q_val = 108;
                f->kbch  = 25728;
                f->bch_code = BCH_CODE_N12;
                break;
            case CR_1_2:
                f->q_val = 90;
                f->kbch  = 32208;
                f->bch_code = BCH_CODE_N12;
                break;
            case CR_3_5:
                f->q_val = 72;
                f->kbch  = 38688;
                f->bch_code = BCH_CODE_N12;
                break;
            case CR_2_3:
                bch_bits = 160;
                f->q_val = 60;
                f->kbch  = 43040;
                f->bch_code = BCH_CODE_N10;
                break;
            case CR_3_4:
                f->q_val = 45;
                f->kbch  = 48408;
                f->bch_code = BCH_CODE_N12;
                break;
            case CR_4_5:
                f->q_val = 36;
                f->kbch  = 51648;
                f->bch_code = BCH_CODE_N12;
                break;
            case CR_5_6:
                bch_bits = 160;
                f->q_val = 30;
                f->kbch  = 53840;
                f->bch_code = BCH_CODE_N10;
                break;
            case CR_8_9:
                bch_bits = 128;
                f->q_val = 20;
                f->kbch  = 57472;
                f->bch_code = BCH_CODE_N8;
                break;
            case CR_9_10:
                bch_bits = 128;
                f->q_val = 18;
                f->kbch  = 58192;
                f->bch_code = BCH_CODE_N8;
                break;
            default:
//                loggerf("Configuration error DVB2\n");
                error = -1;
                break;
        }
    }

    if( f->frame_type == FRAME_SHORT )
    {
        f->nldpc = 16200;
        bch_bits = 168;
        f->bch_code = BCH_CODE_S12;
        // Apply code rate
        switch(f->code_rate )
        {
            case CR_1_4:
                f->q_val = 36;
                f->kbch  = 3072;
                break;
            case CR_1_3:
                f->q_val = 30;
                f->kbch  = 5232;
                break;
            case CR_2_5:
                f->q_val = 27;
                f->kbch  = 6312;
                break;
            case CR_1_2:
                f->q_val = 25;
                f->kbch  = 7032;
                break;
            case CR_3_5:
                f->q_val = 18;
                f->kbch  = 9552;
                break;
            case CR_2_3:
                f->q_val = 15;
                f->kbch  = 10632;
                break;
            case CR_3_4:
                f->q_val = 12;
                f->kbch  = 11712;
                break;
            case CR_4_5:
                f->q_val = 10;
                f->kbch  = 12432;
                break;
            case CR_5_6:
                f->q_val = 8;
                f->kbch  = 13152;
                break;
            case CR_8_9:
                f->q_val = 5;
                f->kbch  = 14232;
                break;
            case CR_9_10:
                error = 1;
                f->kbch  = 0;
                break;
            default:
//                loggerf("Configuration error DVB2\n");
                error = -1;
                break;
        }
    }
    if( error == 0 )
    {
        // Length of the user packets
        f->bb_header.upl  = 188*8;
        // Payload length
        f->bb_header.dfl = f->kbch - 80;
        // Transport packet sync
        f->bb_header.sync = 0x47;
        // Start of LDPC bits
        f->kldpc = f->kbch + bch_bits;
        // Number of padding bits required (not used)
        f->padding_bits = 0;
        // Number of usable data bits (not used)
        f->useable_data_bits = f->kbch - 80;
        // Save the configuration, will be updated on next frame
        m_format[1] = *f;
        // reset various pointers
        m_dnp   = 0;// No deleted null packets
        // Signal we need to update on the next frame.
        if( m_params_changed )
            base_end_of_frame_actions();
        else
            m_params_changed = 1;
    }
    return error;
}
void DVB2::get_configure( DVB2FrameFormat *f )
{
    *f = m_format[1];
}

// Points to first byte in transport packet

int DVB2::add_ts_frame_base( u8 *ts )
{
    if( m_frame_offset_bits == 0 )
    {
         // New frame needs to be sent
        add_bbheader(); // Add the header
    }
    // Add a new transport packet
    unpack_transport_packet_add_crc( ts );
    // Have we reached the end?
    if( m_frame_offset_bits == m_format[0].kbch )
    {
        // Yes so now Scramble the BB frame
        bb_randomise();
        // BCH encode the BB Frame
        bch_encode();
        // LDPC encode the BB frame and BCHFEC bits
        ldpc_encode();
        // Signal to the modulation specific class we have something to send
        base_end_of_frame_actions();
        return 1;
    }
    return 0;
}
//
// Dump NULL packets appends a counter to the end of each UP
// it is not implemented at the moment.
//
int DVB2::next_ts_frame_base( u8 *ts )
{
    int res = 0;
    // See if we need to dump null packets
    if( m_format[0].null_deletion == 1 )
    {
        if(((ts[0]&0x1F) == 0x1F)&&(ts[1] == 0xFF ))
        {
            // Null packet detected
            if( m_dnp < 0xFF )
            {
                m_dnp++;// Increment the number of null packets
                return 0;
            }
        }
    }
    // Need to send a new transport packet
    res = add_ts_frame_base( ts );
    if( res ) m_dnp = 0;// Clear the DNP counter
    // return whether it is time to transmit a new frame
    return res;
}
DVB2::DVB2(void)
{
    init_bb_randomiser();
    bch_poly_build_tables();
    build_crc8_table();
    m_dnp   = 0;// No deleted null packets
    m_frame_offset_bits = 0;
    m_params_changed = 1;
}
DVB2::~DVB2(void)
{
}
