//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop


using namespace std;

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


//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
    cur_mode = _select;
    mouse_state = up;
    mouse_on_neuron = NULL;
    cur_add_type = _in;
}

//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
    net.display();
}


void __fastcall TForm1::netareaMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
    has_moved = 0;
    mouse_state = down;

    mouse_on_neuron = net.get_neuron_mouse_over(X, Y);
}


void __fastcall TForm1::netareaMouseUp(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
   mouse_state = up;

   if (!has_moved)
   {
       if (Button == mbRight)
       {
           if (cur_mode == _add)
           {
             cur_mode = _select;
           }
           else
           {
               if (net.num_selected > 0)
                   do_link();
               else
                   if (mouse_on_neuron != NULL)
                       mouse_on_neuron->toggle();
           }

       }
       else


       if (cur_mode == _add)
       {
           net.add(X, Y, cur_add_type, net.next_id++);
           net.num_neurons++;
           Form1->Status1->SimpleText = "Neurons: " + (AnsiString)net.num_neurons + "  Axons: " + (AnsiString)net.num_axons;
       }
       else
       if (cur_mode == _select)
           net.select_object(X, Y);

   }
   net.display();
}

void __fastcall TForm1::netareaMouseMove(TObject *Sender,
      TShiftState Shift, int X, int Y)
{

    if (mouse_state == down)
    if (mouse_on_neuron)
    {
        mouse_on_neuron->move(X - net.move_x_ofs, Y - net.move_y_ofs);
        has_moved = 1;
    }
}


void __fastcall TForm1::in_offClick(TObject *Sender)
{
    cur_mode = _add;
    cur_add_type = _in;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::out_offClick(TObject *Sender)
{
    cur_mode = _add;
    cur_add_type = _out;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::orClick(TObject *Sender)
{
    cur_mode = _add;
    cur_add_type = _or;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::andClick(TObject *Sender)
{
    cur_mode = _add;
    cur_add_type = _and;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::notClick(TObject *Sender)
{
    cur_mode = _add;
    cur_add_type = _not;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::selectClick(TObject *Sender)
{
    cur_mode = _select;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::toggleClick(TObject *Sender)
{
//    cur_mode = _toggle;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::do_link(void)//TComponent* Owner)
{
    if (mouse_on_neuron == NULL) return;

    // inputs cant have inputs
    if (mouse_on_neuron->type == _in) return;

    // outputs cant cant more than one input
    if ((mouse_on_neuron->type == _out) && (mouse_on_neuron->axons.size() == 1)) return;

    // outputs cant cant more than one input
    if ((mouse_on_neuron->type == _out) && (net.num_selected > 1)) return;

    if (net.num_selected == 0) return;


    for (unsigned int i = 0; i < net.neurons.size(); i++)
        if ((net.neurons[i].selected) && (&net.neurons[i] != mouse_on_neuron))
        {
            mouse_on_neuron->connect(net.neurons[i].id, 1);
            net.num_axons++;
        }

    Form1->Status1->SimpleText = "Neurons: " + (AnsiString)net.num_neurons + "  Axons: " + (AnsiString)net.num_axons;

    net.process();

    mouse_on_neuron = NULL;         // clear the last selected neuron

    net.unselect_all();             // unselect all of the neurons

}

void __fastcall TForm1::linkClick(TObject *Sender)
{
//do_link();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::removeClick(TObject *Sender)
{
    net.remove();
    Form1->Status1->SimpleText = "Neurons: " + (AnsiString)net.num_neurons +
                                 "  Axons: " + (AnsiString)net.num_axons;    
//    net.reset();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
    net.process();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormKeyPress(TObject *Sender, char &Key)
{
//    if (Key == VK_SPACE)
//    do_link();
}
//---------------------------------------------------------------------------



void __fastcall TForm1::unlinkClick(TObject *Sender)
{
   // unlink the selected neurons

   vector<unsigned int> index_list;

   for (unsigned int i = 0; i < net.neurons.size(); i++)
       if (net.neurons[i].selected)
           index_list.push_back(i);


    for (unsigned int s1 = 0; s1 < index_list.size(); s1++)
    {
        for (unsigned int s2= 0; s2 < index_list.size(); s2++)
        {
//            if (s1 != s2)
            {
                for (unsigned int a = 0; a < net.neurons[index_list[s2]].axons.size(); a++)
                    if (net.neurons[index_list[s2]].axons[a].source_id == net.neurons[index_list[s1]].id)
                        net.neurons[index_list[s2]].axons.erase(net.neurons[index_list[s2]].axons.begin() + a);
            }
        }
    }

    net.process();
    net.display();
}
//---------------------------------------------------------------------------



void __fastcall TForm1::Save1Click(TObject *Sender)
{
    if (SaveDialog1->Execute())
    {
        int outfile = FileCreate(SaveDialog1->FileName);

        char buff[255];

        // number of neurons
        sprintf(buff, "%d\n", net.neurons.size());
        FileWrite(outfile, buff, strlen(buff));

        for (unsigned int i = 0; i < net.neurons.size(); i++)
        {
            // ID
            sprintf(buff, "%d\n", net.neurons[i].id);
            FileWrite(outfile, buff, strlen(buff));

            // type
            if (net.neurons[i].type == _in) sprintf(buff, "in\n"); else
            if (net.neurons[i].type == _out) sprintf(buff, "out\n"); else
            if (net.neurons[i].type == _or) sprintf(buff, "or\n"); else
            if (net.neurons[i].type == _and) sprintf(buff, "and\n"); else
            if (net.neurons[i].type == _not) sprintf(buff, "not\n"); else
                sprintf(buff, "invalid\n");

            FileWrite(outfile, buff, strlen(buff));

            // x_pos
            sprintf(buff, "%d\n", net.neurons[i].x_pos);
            FileWrite(outfile, buff, strlen(buff));

            // y_pos
            sprintf(buff, "%d\n", net.neurons[i].y_pos);
            FileWrite(outfile, buff, strlen(buff));

            // num axons
            sprintf(buff, "%d\n", net.neurons[i].axons.size());
            FileWrite(outfile, buff, strlen(buff));

            for (unsigned int a = 0; a < net.neurons[i].axons.size(); a++)
            {
                // source id
                sprintf(buff, "%d\n", net.neurons[i].axons[a].source_id);
                FileWrite(outfile, buff, strlen(buff));

                // weight
                sprintf(buff, "%f\n", net.neurons[i].axons[a].weight);
                FileWrite(outfile, buff, strlen(buff));
            }
        }

        FileClose(outfile);
    }
}

//---------------------------------------------------------------------------


void __fastcall TForm1::Load1Click(TObject *Sender)
{
    if (OpenDialog1->Execute())
    {
        FILE *infile = fopen(OpenDialog1->FileName.c_str(), "rt");

        if (infile != NULL)
        {
            net.reset();

            unsigned int num_neurons  = 0;
            unsigned int max_id = 0;

            fscanf(infile, "%d\n", &num_neurons);


            for (unsigned int i = 0; i < num_neurons; i++)
            {
                char buffer[255];

                unsigned int id = 0;
                neuron_type type = _in;
                unsigned int x_pos = 0;
                unsigned int y_pos = 0;
                unsigned int num_axons = 0;

                // ID
                fscanf(infile, "%d\n", &id);

                if (id > max_id)  max_id = id;

                // type
                fscanf(infile, "%s\n", buffer);

                if (!strcmp(buffer, "in")) type = _in; else
                if (!strcmp(buffer, "out")) type = _out; else
                if (!strcmp(buffer, "or")) type = _or; else
                if (!strcmp(buffer, "and")) type = _and; else
                if (!strcmp(buffer, "not")) type = _not; else
                    type = _in;

                // x_pos
                fscanf(infile, "%d\n", &x_pos);

                // y_pos
                fscanf(infile, "%d\n", &y_pos);

                // num axons
                fscanf(infile, "%d\n", &num_axons);

                // add the neuron to the net
                net.add(x_pos, y_pos, type, id );

                net.num_neurons++;

                for (unsigned int a = 0; a < num_axons; a++)
                {
                    unsigned int source_id;
                    float weight;

                    // source id
                    fscanf(infile, "%d\n", &source_id);

                    // weight
                    fscanf(infile, "%f\n", &weight);

                    // add the axon
                    net.neurons[i].connect(source_id, weight);
                }
            }
            fclose(infile);

            net.next_id = max_id + 1;

            net.process();
            net.display();
        }
        else
            Status1->SimpleText = "Unable to load File";
    }


}
//---------------------------------------------------------------------------


void __fastcall TForm1::Exit1Click(TObject *Sender)
{
    Application->Terminate();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Rest1Click(TObject *Sender)
{
    net.reset();
}
//---------------------------------------------------------------------------

