/**
 * \file
 * \brief Implementation of the Driver for the AD5791
 *   Digital to Analog Converter Board
 *
 *
 * The board inverts all digital inputs.
 */

#include "AD5791.h"
#include "../../core/states.h"
#include <cstdio>
#include <cmath>
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>

using std::reverse;
using std::cout;
using std::vector;
#ifndef TIMING
#define TIMING 9e-8
#endif

// The bit depth of the DAC
#define DAC_DATA_BITS 20
#define DAC_CONTROL_BITS 4

// The channel configuration
#define DATA_BIT 18//18	
#define CLK_BIT 16//16


// this bit needs to be set to write the DAC register
#define WRITE_DAC 1<<20


// software control register
#define RESET 1
#define CLR 2
#define LDAC 4

// control register
// Linearity error compensation for varying reference input spans.
#define LINCOMP_10V 0*1<<9 + 0*1<<8 + 0*1<<7 + 0*1<<6
#define LINCOMP_12V 1*1<<9 + 0*1<<8 + 0*1<<7 + 1*1<<6
#define LINCOMP_16V 1*1<<9 + 0*1<<8 + 1*1<<7 + 0*1<<6
#define LINCOMP_19V 1*1<<9 + 0*1<<8 + 1*1<<7 + 1*1<<6
#define LINCOMP_20V 1*1<<9 + 1*1<<8 + 0*1<<7 + 0*1<<6

// SDO pin enable/disable control.
#define SDODIS 32
// DAC register coding select.
#define DAC_CODING_SELECT 16
// DAC tristate control.
#define DACTRI 8
// Output ground clamp control. (set by default)
#define OPGND 4
//  Output amplifier configuration control. (set by default)
#define RBUF 2


AD5791::AD5791(int myid) : id(myid) {
    dac_value = 0;
    set_latch_bit(17);
}

AD5791::~AD5791() {}
// This sets the dac_value
void AD5791::set_dac(signed dw) {
    dac_value = dw;
}

void AD5791::set_latch_bit(int le_bit)
{
    latch_bit = le_bit;
}
// This sets the DAC
void AD5791::set_dac(state &experiment) {
    state_sequent *exp_sequence = dynamic_cast<state_sequent *>(&experiment);
    if (exp_sequence == NULL)
        // is a very state on top level, todo: change interface
        throw pfg_exception("cannot work on a single state, sorry (todo: change interface)");
    else {
        for (state_sequent::iterator child_state = exp_sequence->begin();
             child_state != exp_sequence->end(); ++child_state) {
            set_dac_recursive(*exp_sequence, child_state);
	    }
	    std::cout << "first state"<< std::endl;

        // Set DAC to 0 at start of experiment
        set_dac_to_zero(exp_sequence, exp_sequence->begin());
        // but first initialize DAC
        init_dac(exp_sequence, exp_sequence->begin());
        // And at the end of the experiment set DAC to zero
        set_dac_to_zero(exp_sequence, exp_sequence->end());
    }
}

state_sequent* AD5791::tx_bit(unsigned int bit) {
    state_sequent* tx = new state_sequent();
    ttlout* ttl_state = new ttlout();
    state s(TIMING);
    s.push_back(ttl_state);

    ttl_state->ttls = bit*(1<<DATA_BIT) + (1 << CLK_BIT);
    tx->push_back(s.copy_new());
    ttl_state->ttls = (1<<DATA_BIT);
    tx->push_back(s.copy_new());
    return  tx;
}

void AD5791::set_dac_to_zero(state_sequent* exp_sequence, state::iterator where) {
    // 0001 0000 0000 0000 0000 0000
    state s(TIMING);
    ttlout* ttl_state = new ttlout();
    ttl_state->id = 0;
    s.push_front(ttl_state);

    // test exp_sequence->insert(where, tx_bit(1));
    exp_sequence->insert(where, tx_bit(1));
    // 0
    ttl_state->ttls = 0 + (1 << CLK_BIT);
    exp_sequence->insert(where, s.copy_new());
    ttl_state->ttls = 0;
    exp_sequence->insert(where, s.copy_new());
    //  0
    ttl_state->ttls = 0 + (1 << CLK_BIT);
    exp_sequence->insert(where, s.copy_new());
    ttl_state->ttls = 0;
    exp_sequence->insert(where, s.copy_new());
    //   0
    ttl_state->ttls = 0 + (1 << CLK_BIT);
    exp_sequence->insert(where, s.copy_new());
    ttl_state->ttls = 0;
    exp_sequence->insert(where, s.copy_new());
    //    1
    ttl_state->ttls = (1<<DATA_BIT) + (1 << CLK_BIT);
    exp_sequence->insert(where, s.copy_new());
    ttl_state->ttls = (1<<DATA_BIT);
    exp_sequence->insert(where, s.copy_new());

    // the rest
    state_sequent *rep_sequence = new state_sequent();
    rep_sequence->repeat = DAC_DATA_BITS;
    ttl_state->ttls = 0 + (1 << CLK_BIT);
    rep_sequence->push_back(s.copy_new());
    ttl_state->ttls = 0;
    rep_sequence->push_back(s.copy_new());
    exp_sequence->insert(where, rep_sequence);
    //read in the word
    ttl_state->ttls = 0;
    exp_sequence->insert(where, s.copy_new());
    ttl_state->ttls = (1 << latch_bit);
    exp_sequence->insert(where, s.copy_new());
}

void AD5791::init_dac(state_sequent *exp_sequence, state::iterator where) {
    state s(TIMING);
    ttlout *ttl_state = new ttlout();
    ttl_state->id = 0;
    s.push_front(ttl_state);
    // 0010 0000 0000 0000 0000 0000
    // 0
    ttl_state->ttls = 0 + (1 << CLK_BIT);
    exp_sequence->insert(where, s.copy_new());
    ttl_state->ttls = 0;
    exp_sequence->insert(where,s.copy_new());
    //  0
    ttl_state->ttls = 0 + (1 << CLK_BIT);
    exp_sequence->insert(where, s.copy_new());
    ttl_state->ttls = 0;
    exp_sequence->insert(where, s.copy_new());
    //   1
    ttl_state->ttls = (1<<DATA_BIT) + (1 << CLK_BIT);
    exp_sequence->insert(where,s.copy_new());
    ttl_state->ttls = (1<<DATA_BIT);
    exp_sequence->insert(where,s.copy_new());
    //    0
    ttl_state->ttls = 0 + (1 << CLK_BIT);
    exp_sequence->insert(where, s.copy_new());
    ttl_state->ttls = 0;
    exp_sequence->insert(where,s.copy_new());

    // the rest
    state_sequent *rep_sequence = new state_sequent();
    rep_sequence->repeat = DAC_DATA_BITS;
    ttl_state->ttls = 0 + (1 << CLK_BIT);
    rep_sequence->push_back(s.copy_new());
    ttl_state->ttls = 0;
    rep_sequence->push_back(s.copy_new());
    exp_sequence->insert(where, rep_sequence);

    //read in the word
    ttl_state->ttls = 0;
    exp_sequence->insert(where, s.copy_new());
    ttl_state->ttls = (1 << latch_bit) + (1<<CLK_BIT);
    s.length = 180e-9;
    exp_sequence->insert(where, s.copy_new());
}




// This loops recursive through the state tree
void AD5791::set_dac_recursive(state_sequent &the_sequence, state::iterator &the_state) {

    state_sequent *a_sequence = dynamic_cast<state_sequent *>(*the_state);
    // Am I a sequence? Yes? Go one sequence further
    if (a_sequence != NULL) {
        for (state_sequent::iterator child_state = a_sequence->begin(); child_state != a_sequence->end(); ++child_state)
            set_dac_recursive(*a_sequence, child_state);
    }
        // I am not a sequence, but a state
    else {
        state *this_state = dynamic_cast<state *>(*the_state);
        if (this_state == NULL)
            throw pfg_exception("state_atom in state_sequent not expected");
        analogout *dac_analog_out = NULL;
        // find an analogout section with suitable id
        state::iterator pos = this_state->begin();
        while (pos != this_state->end()) {  // state members loop
            analogout *aout = dynamic_cast<analogout *>(*pos); // initialize new analogout from state
            if (aout != NULL)
                std::cout << aout->id << std::endl;
            // This is for me, analogout is != NULL (there is an analogout) and has my ID
            if (aout != NULL && aout->id == id) {
                if (dac_analog_out == NULL) {
                    // save the information of this aout
                    dac_analog_out = aout;
                }
                    // there is no place for me here
                else {
                    throw pfg_exception("found another DAC section, ignoring");
                    delete aout;
                }
                // remove the analog out section
                this_state->erase(pos++);
            } else {
                std::cout << "nope" << std::endl;
                ++pos;
            }
        } // state members loop

        if (dac_analog_out != NULL) { // state modifications

            //std::cout<<"found a analog out section, value="<<dac_analog_out->dac_value<<std::endl;
            // check the length of the state
            if (this_state->length < TIMING * (DAC_CONTROL_BITS + DAC_DATA_BITS) * 2 + 1)
                throw pfg_exception("AD5791: time is too short to save DAC information");
            else {
                // copy of original state
                state *register_state = new state(*this_state);
                ttlout *register_ttls = new ttlout();
                register_ttls->id = 0;
                register_state->length = TIMING;
                register_state->push_back(register_ttls);
                if (dac_analog_out->dac_value > (pow(2.0, int(DAC_DATA_BITS - 1)) - 1))
                    throw pfg_exception("dac_value too high");
                if (abs(dac_analog_out->dac_value) > pow(2.0, int(DAC_DATA_BITS - 1)))
                    throw pfg_exception("dac_value too low");

                // set dac write register 1<<20
                dac_analog_out->dac_value += WRITE_DAC;
                vector<int> dac_word;
                // generate the bit pattern, this is MSB last
                for (int j = 0; j < (DAC_DATA_BITS + DAC_CONTROL_BITS); j++) {
                    int bit = dac_analog_out->dac_value & 1;
                    dac_word.push_back(bit);
                    dac_analog_out->dac_value >>= 1;
                    std::cout << bit;
                }
                // reverse the bit pattern (MSB first)
                reverse(dac_word.begin(), dac_word.end());

                // need one clock cycle to read in bit
                // latch enable (LE) should always be high while doing so
                // except for the last bit

                // do run length encoding, grouping same bit values in loops
                int last_seen_bit = dac_word[0];
                int last_seen_bit_count = 1;
                for (int i = 1; i < (DAC_DATA_BITS + DAC_CONTROL_BITS) + 1; i++) {
                    if (i == (DAC_DATA_BITS + DAC_CONTROL_BITS) || last_seen_bit != dac_word[i]) {
                        // so we have to write the bits
                        // either because the previous were different or we are finished
                        if (last_seen_bit_count > 1) {
                            // insert a loop
                            state_sequent *rep_sequence = new state_sequent();
                            rep_sequence->repeat = last_seen_bit_count;
                            register_ttls->ttls = (1 << DATA_BIT) * last_seen_bit + (1 << CLK_BIT) + (1 << latch_bit);
                            rep_sequence->push_back(register_state->copy_new());
                            register_ttls->ttls = (1 << DATA_BIT) * last_seen_bit + (1 << latch_bit);
                            rep_sequence->push_back(register_state->copy_new());
                            the_sequence.insert(the_state, rep_sequence);
                        } else {
                            // no loop necessary, insert two states
                            register_ttls->ttls = (1 << DATA_BIT) * last_seen_bit + (1 << CLK_BIT) + (1 << latch_bit);
                            the_sequence.insert(the_state, register_state->copy_new());
                            register_ttls->ttls = (1 << DATA_BIT) * last_seen_bit + (1 << latch_bit);
                            the_sequence.insert(the_state, register_state->copy_new());
                        }
                        // reset counter and bits if we are not finished
                        if (i < (DAC_DATA_BITS + DAC_CONTROL_BITS)) {
                            last_seen_bit = dac_word[i];
                            last_seen_bit_count = 1;
                        }
                    } // finished writing
                    else
                        last_seen_bit_count += 1; // same bit value, so continue
                } // end of bit loop
                register_ttls->ttls = 0;
                the_sequence.insert(the_state, register_state->copy_new());

                // shorten the remaining state
                // and add LE high to this state
                ttlout *ttls = new ttlout();
                // 2 clocks per bit + 1 for sync
                this_state->length -= TIMING * 2 * (DAC_DATA_BITS + DAC_CONTROL_BITS) + 1;
                ttls->ttls = 1 << latch_bit;
                this_state->push_front(ttls);

                // cleanup
                delete register_state;
                delete dac_analog_out;
            } // state was long enough to work on
        } else {
            ttlout *le_ttls = new ttlout();
            // le_ttls->ttls = 1 << latch_bit;
            this_state->push_back(le_ttls);
        }
        // end of state modifications
    } // I was a state
}
