#!/usr/bin/env python
#
# Copyright 2011-2011 Ettus Research LLC
#
# 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, either 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 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/>.
#

TMPL_TEXT = """
#import time
/***********************************************************************
 * This file was generated by $file on $time.strftime("%c")
 **********************************************************************/
\#include <uhd/exception.hpp>
\#include <boost/tokenizer.hpp>
\#include <boost/lexical_cast.hpp>
\#include <boost/detail/endian.hpp>
\#include <boost/cstdint.hpp>
\#include <string>
\#include <vector>

typedef size_t pred_type;
typedef std::vector<pred_type> pred_vector_type;

enum dir_type{
    DIR_OTW_TO_CPU = 0,
    DIR_CPU_TO_OTW = 1
};

struct pred_error : uhd::value_error{
    pred_error(const std::string &what):
        uhd::value_error("convert::make_pred: " + what)
    {
        /* NOP */
    }
};

pred_type make_pred(const std::string &markup, dir_type &dir){
    pred_type pred = 0;

    try{
        boost::tokenizer<boost::char_separator<char> > tokenizer(markup, boost::char_separator<char>("_"));
        std::vector<std::string> tokens(tokenizer.begin(), tokenizer.end());
        //token 0 is <convert>
        std::string inp_type = tokens.at(1);
        std::string num_inps = tokens.at(2);
        //token 3 is <to>
        std::string out_type = tokens.at(4);
        std::string num_outs = tokens.at(5);
        std::string swap_type = tokens.at(6);

        std::string cpu_type, otw_type;
        if (inp_type.find("item") == std::string::npos){
            cpu_type = inp_type;
            otw_type = out_type;
            dir = DIR_CPU_TO_OTW;
        }
        else{
            cpu_type = out_type;
            otw_type = inp_type;
            dir = DIR_OTW_TO_CPU;
        }

        if      (cpu_type == "fc64") pred |= $ph.fc64_p;
        else if (cpu_type == "fc32") pred |= $ph.fc32_p;
        else if (cpu_type == "sc16") pred |= $ph.sc16_p;
        else if (cpu_type == "sc8")  pred |= $ph.sc8_p;
        else throw pred_error("unhandled io type " + cpu_type);

        if (otw_type == "item32") pred |= $ph.item32_p;
        else throw pred_error("unhandled otw type " + otw_type);

        int num_inputs = boost::lexical_cast<int>(num_inps);
        int num_outputs = boost::lexical_cast<int>(num_outs);

        switch(num_inputs*num_outputs){ //FIXME treated as one value
        case 1: pred |= $ph.chan1_p; break;
        case 2: pred |= $ph.chan2_p; break;
        case 3: pred |= $ph.chan3_p; break;
        case 4: pred |= $ph.chan4_p; break;
        default: throw pred_error("unhandled number of channels");
        }

        if      (swap_type == "bswap") pred |= $ph.bswap_p;
        else if (swap_type == "nswap") pred |= $ph.nswap_p;
        else throw pred_error("unhandled swap type");

    }
    catch(...){
        throw pred_error("could not parse markup: " + markup);
    }

    return pred;
}

#define pred_table_wildcard pred_type(~0)
#define pred_table_max_size size_t(128)
#define pred_table_index(e) (pred_type(e) & 0x7f)

static pred_vector_type get_pred_byte_order_table(void){
    pred_vector_type table(pred_table_max_size, pred_table_wildcard);
    \#ifdef BOOST_BIG_ENDIAN
    table[pred_table_index(otw_type_t::BO_BIG_ENDIAN)]    = $ph.nswap_p;
    table[pred_table_index(otw_type_t::BO_LITTLE_ENDIAN)] = $ph.bswap_p;
    \#else
    table[pred_table_index(otw_type_t::BO_BIG_ENDIAN)]    = $ph.bswap_p;
    table[pred_table_index(otw_type_t::BO_LITTLE_ENDIAN)] = $ph.nswap_p;
    \#endif
    table[pred_table_index(otw_type_t::BO_NATIVE)]        = $ph.nswap_p;
    return table;
}

static pred_vector_type get_pred_io_type_table(void){
    pred_vector_type table(pred_table_max_size, pred_table_wildcard);
    table[pred_table_index(io_type_t::COMPLEX_FLOAT64)]    = $ph.fc64_p;
    table[pred_table_index(io_type_t::COMPLEX_FLOAT32)]    = $ph.fc32_p;
    table[pred_table_index(io_type_t::COMPLEX_INT16)]      = $ph.sc16_p;
    return table;
}

static pred_vector_type get_pred_num_io_table(void){
    pred_vector_type table(pred_table_max_size, pred_table_wildcard);
    table[1] = $ph.chan1_p;
    table[2] = $ph.chan2_p;
    table[3] = $ph.chan3_p;
    table[4] = $ph.chan4_p;
    return table;
}

UHD_INLINE pred_type make_pred(
    const io_type_t &io_type,
    const otw_type_t &otw_type,
    size_t num_inputs,
    size_t num_outputs
){
    pred_type pred = $ph.item32_p; //only item32 supported as of now

    static const pred_vector_type pred_byte_order_table(get_pred_byte_order_table());
    pred |= pred_byte_order_table[pred_table_index(otw_type.byteorder)];

    static const pred_vector_type pred_io_type_table(get_pred_io_type_table());
    pred |= pred_io_type_table[pred_table_index(io_type.tid)];

    static const pred_vector_type pred_num_io_table(get_pred_num_io_table());
    pred |= pred_num_io_table[pred_table_index(num_inputs*num_outputs)];

    if (pred == pred_table_wildcard) throw pred_error(
        "unhanded input combination for make_pred()"
    );

    return pred;
}
"""

def parse_tmpl(_tmpl_text, **kwargs):
    from Cheetah.Template import Template
    return str(Template(_tmpl_text, kwargs))

class ph:
    bswap_p  = 0b00001
    nswap_p  = 0b00000
    item32_p = 0b00000
    sc8_p    = 0b00000
    sc16_p   = 0b00010
    fc32_p   = 0b00100
    fc64_p   = 0b00110
    chan1_p  = 0b00000
    chan2_p  = 0b01000
    chan3_p  = 0b10000
    chan4_p  = 0b11000

if __name__ == '__main__':
    import sys, os
    file = os.path.basename(__file__)
    open(sys.argv[1], 'w').write(parse_tmpl(TMPL_TEXT, file=file, ph=ph))