#include "net.h"
#include "net_unit.h"


/****************************/
/* Implement the axon Class */
/****************************/

axon_class::axon_class(unsigned int the_id, double theweight)
{
    source = NULL;
    source_id = the_id;
    weight = theweight;
}

/*************************/
/* End of the axon Class */
/*************************/





/******************************/
/* Implement the Neuron Class */
/******************************/

/* the neuron constructor */
neuron_class::neuron_class( int the_x_pos, int the_y_pos, neuron_type the_type, unsigned int the_id )
{
    x_pos = the_x_pos;
    y_pos = the_y_pos;
    type = the_type;

    selected = 0;
    output = 0;
    level = 0;
    id = the_id;

    display();
}


void neuron_class::display ( void )
{
    int base_x = Form1->netarea->Left;
    int base_y = Form1->netarea->Top;

    Graphics::TBitmap *image;

    if (type == _in)
    {
        if (output == 0)
            image = Form1->in_off->Picture->Bitmap; //input_off_image;
        else
            image = Form1->in_on->Picture->Bitmap;
    }
    else
    if (type == _out)
    {
        if (output == 0)
            image = Form1->out_off->Picture->Bitmap;
        else
            image = Form1->out_on->Picture->Bitmap;
    }
    else
    if (type == _or) image =  Form1->or->Picture->Bitmap; else
    if (type == _and) image =  Form1->and->Picture->Bitmap; else
    if (type == _not) image =  Form1->not->Picture->Bitmap; else
    return; // cant draw undefined types

    // draw a box arouns it
    if (selected)
    {
        Form1->Canvas->Pen->Color = clBlue;

        Form1->Canvas->Rectangle(base_x - 2 + x_pos,
                                 base_y - 2 + y_pos,
                                 base_x + neuron_x_size + 2 + x_pos,
                                 base_y + neuron_y_size + 2 + y_pos);

        Form1->Canvas->Pen->Color = clBlack;
    }

    // draw the image
    Form1->Canvas->Draw(base_x + x_pos, base_y + y_pos, image);

    // draw all of the axons

    for (unsigned int i = 0; i < axons.size(); i++)
    if (axons[i].source != NULL)
    {
        if (axons[i].source->output == 0)
            Form1->Canvas->Pen->Color = clBlue;
        else
            Form1->Canvas->Pen->Color = clRed;

        Form1->Canvas->MoveTo(base_x + x_pos, base_y + y_pos + (neuron_x_size / 2));
        Form1->Canvas->LineTo(axons[i].source->x_pos + base_x + neuron_x_size,
                              axons[i].source->y_pos + base_y + (neuron_y_size/ 2));
    }

    // reset the pen color the black (normal)
    Form1->Canvas->Pen->Color = clBlack;

//    Form1->Canvas->TextOut(base_x + x_pos, base_y + y_pos, (AnsiString)level);
}


void neuron_class::process( void )
{
    if (type == _out)
    {
        if (axons.size() >= 1)
            if (axons[0].source != NULL)
                output = axons[0].source->output * axons[0].weight;
    }
    else
    if (type == _or)
    {
        output = 0;

        // sum the input together
        for (unsigned int i = 0; i < axons.size(); i++)
        {
            if (axons[i].source != NULL)
                output += axons[i].source->output * axons[i].weight;
        }

        // apply thresh hold
        if (output > 1)
            output = 1;
    }
    else
    if (type == _and)
    {
        /* or all of the imputs together */
        output = 1;

        for (unsigned int i = 0; i < axons.size(); i++)
           if (axons[i].source != NULL)
            output *= axons[i].source->output * axons[i].weight;
    }
    else
    if (type == _not)
    {
        if (axons.size() >= 1)
            if (axons[0].source != NULL)
            output = !(axons[0].source->output * axons[0].weight);
    }
    else
    if (type == _in)
    {
//        output = random(2);
    }
    else
        output = 0;

    display();

}


void neuron_class::connect(unsigned int the_id, double theweight)
{
     axons.push_back(axon_class(the_id, theweight));

    // display();
}



void neuron_class::remove( void )
{
    //remove all of the axons that it is getting information from
   Form1->net.num_axons -= axons.size();
    axons.clear();

   // Form1->bug->Lines->Add("destruct neuron");


    for (unsigned int i = 0; i < Form1->net.neurons.size(); i++)
    {
        for (unsigned int t = 0; t < Form1->net.neurons[i].axons.size(); t++)
            if (Form1->net.neurons[i].axons[t].source_id == id)
            {
                Form1->net.neurons[i].axons.erase(Form1->net.neurons[i].axons.begin() + t);
                Form1->net.num_axons--;
            }
    }
}






void neuron_class::move (int the_x_pos, int the_y_pos )
{
    erase();

    x_pos = the_x_pos;
    y_pos = the_y_pos;

    Form1->net.display();
}


void neuron_class::erase(void)
{
    int base_x = Form1->netarea->Left + x_pos;
    int base_y = Form1->netarea->Top + y_pos;

    Form1->Canvas->Pen->Color = clWhite;

    Form1->Canvas->Rectangle(base_x, base_y,
                             base_x + neuron_x_size,
                             base_y + neuron_x_size);

    Form1->Canvas->Pen->Color = clBlack;
}

void neuron_class::toggle(void)
{
    if (type == _in)
    {
        if (output > 0)
            output = 0;
        else
            output = 1;

        Form1->net.process();
    }

}


int calc_level(neuron_class *n, vector<unsigned int>visit, int start_level)
{
    int highest = 0;

    // catch circular race condition
//    if ((s == n) && (start_level != 0)) return(0);

    visit.push_back(n->id);

    if (start_level != 0)
    {
        for (unsigned int i = 0; i < visit.size(); i++)
        if (visit[i] == n->id)
        return(start_level);
    }


    if (n->type == _in) return (start_level);


    for (unsigned int i = 0; i < n->axons.size(); i++)
    {
        int tmp;

        if (n->axons[i].source == NULL)
            tmp = 0;
        else
            tmp = calc_level(n->axons[i].source, visit, start_level+1);

        if (tmp > highest)
            highest = tmp;
    }

    return(highest);
}





/***************************/
/* End of the Neuron Class */
/***************************/








/**************************************/
/* Implement the Neural Network Class */
/**************************************/

neural_network_class::neural_network_class( void )
{
    reset();
}


void neural_network_class::process( void )
{
    unsigned int level_max = 0;

   // cross reference the source_iss to actual pointers;
    resolve_axons();

    // determine the propigation level of each neuron
    for (unsigned int i = 0; i < neurons.size(); i++)
    {

        vector<unsigned int> tmp;


        neurons[i].level = calc_level(&neurons[i], tmp, 0);

        if (neurons[i].level > level_max)
            level_max = neurons[i].level;
    }

    vector<unsigned int> index_list;

    // create sorted the index list
    for (unsigned int lev = 0; lev < level_max + 1; lev++)
       for (unsigned int i = 0; i < neurons.size(); i++)
           if (neurons[i].level == lev)
               index_list.push_back(i);

    for (unsigned int i = 0; i < neurons.size(); i++)
//        neurons[i].process();
        neurons[index_list[i]].process();
}

void neural_network_class::display( void )
{
    unsigned int base_x = Form1->netarea->Left;
    unsigned int base_y = Form1->netarea->Top;

    Form1->Canvas->Rectangle(base_x, base_y,
                             base_x + Form1->netarea->Width,
                             base_y + Form1->netarea->Height);


    for (unsigned int i = 0; i < neurons.size(); i++)
        neurons[i].display();
}



void neural_network_class::add( int the_x_pos, int the_y_pos, neuron_type the_type, unsigned int id )
{
    // determine the range for the positions
    int max_x = Form1->netarea->Left + Form1->netarea->Width - neuron_x_size;
    int max_y = Form1->netarea->Top + Form1->netarea->Height - neuron_y_size;

    // range check the positions
    if (the_x_pos > max_x) the_x_pos = max_x;
    if (the_y_pos > max_y) the_y_pos = max_y;

    // add the neuron to the network
    neurons.push_back(neuron_class(the_x_pos, the_y_pos, the_type, id));

    process();
}



void neural_network_class::remove ( void )
{
    for (unsigned int i = 0; (i < neurons.size()) && (neurons.size() > 0); i++)
    {
        if (neurons[i].selected)
        {
            neurons[i].remove();
            neurons.erase(neurons.begin() + i);
            i--; // the next one may need to be removed as well
            num_neurons--;
            num_selected--;
        }
    }

    process();

    display();
}








void neural_network_class::reset( void )
{
    neurons.clear();    // erase all of the neurons

    next_id = 0;

    num_neurons=0;
    num_axons=0;
    num_selected = 0;
    display();          // display the empty net
}



void neural_network_class::select_object(int the_x_pos, int the_y_pos)
{
    neuron_class *tmp_neuron = get_neuron_mouse_over(the_x_pos, the_y_pos);

    if (tmp_neuron == NULL) return;

    // reverse the selection
    if (!tmp_neuron->selected)
    {
        tmp_neuron->selected = 1;
        num_selected++;
    }
    else
    {
        tmp_neuron->selected = 0;
        num_selected--;
    }

    // draw the neuron
    tmp_neuron->display();

//    Form1->bug->Lines->Add("ID: " + (AnsiString)tmp_neuron->id);
}

neuron_class * neural_network_class::get_neuron_mouse_over(int the_x_pos, int the_y_pos)
{
    neuron_class *tmp_neuron;

    for (unsigned int i = 0; i < neurons.size(); i++)
    {
        tmp_neuron = &neurons[i];

        if ((the_x_pos >= tmp_neuron->x_pos) &&
            (the_y_pos >= tmp_neuron->y_pos) &&
            (the_x_pos <= tmp_neuron->x_pos + neuron_x_size) &&
            (the_y_pos <= tmp_neuron->y_pos + neuron_y_size))
            {
                move_x_ofs = the_x_pos - tmp_neuron->x_pos;
                move_y_ofs = the_y_pos - tmp_neuron->y_pos;

                return(tmp_neuron);
            }
    }

    return(NULL);
}


void neural_network_class::unselect_all(void)
{
    for (unsigned int i = 0; i < neurons.size(); i++)
        neurons[i].selected = 0;

    num_selected = 0;

    display();
}




neuron_class *neural_network_class::get_neuron_pointer(unsigned  the_id)
{
    for (unsigned int i = 0; i < neurons.size(); i++)
    {
        if (neurons[i].id == the_id)
            return(&neurons[i]);
    }

    return(NULL);
}






void neural_network_class::resolve_axons( void )
{
    for (unsigned int n = 0; n < neurons.size(); n++)
        for (unsigned int a = 0; a < neurons[n].axons.size(); a++)
            neurons[n].axons[a].source =
                get_neuron_pointer(neurons[n].axons[a].source_id);
}


/***********************************/
/* End of the Neural Network Class */
/***********************************/



