#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "parse.h"

InputData parse_text_of_inputs(char *text)
{
    InputData input_data;
    char **array_of_lines;
    char **array_of_parts;
    int input_number;
    int input_value;
    int number_of_lines;
    int number_of_parts;
    int i, j;

    array_of_lines = split_text(text, '\n');
    number_of_lines = get_number_of_parts(text, '\n');
    number_of_parts = get_number_of_parts(array_of_lines[0], ' ');
    input_data.array_of_inputs = (int ***)malloc(number_of_lines * sizeof(int ***));
    input_data.number_of_parts = number_of_parts - 1;
    input_data.number_of_lines = number_of_lines;
    input_data.number_of_tacts = (int *)malloc(number_of_lines * sizeof(int *));
    input_data.control_variable = 1;

    for (i = 0; i < number_of_lines; i++)
    {
        array_of_parts = split_text(array_of_lines[i], ' ');
        number_of_parts = get_number_of_parts(array_of_lines[i], ' ');
        input_data.array_of_inputs[i] = (int **)malloc(number_of_parts * sizeof(int **));
        for (j = 0; j < number_of_parts - 1; j++)
        {
            if (sscanf(array_of_parts[j], "%d:%d", &input_number, &input_value) == 2 &&
                (input_value == 1 || input_value == 0))
            {
                input_data.array_of_inputs[i][j] = (int *)malloc(2 * sizeof(int *));
                input_data.array_of_inputs[i][j][0] = input_number;
                input_data.array_of_inputs[i][j][1] = input_value;
            }
            else
            {
                printf("Blad w pliku ze stanami wejsc w linii: %d", i + 1);
                deallocate_memory(input_data.array_of_inputs, i, j);
                input_data.control_variable = 0;
                return input_data;
            }
        }
        if (sscanf(array_of_parts[j], "%d:%d", &input_data.number_of_tacts[i]) != 1)
        {
            printf("Blad w pliku ze stanami wejsc w linii: %d", i + 1);
            deallocate_memory(input_data.array_of_inputs, i, j);
            input_data.control_variable = 0;
            return input_data;
        }
    }
    return input_data;
}

char *set_output_text_line(Node *array_of_nodes, int tact_number)
{
    char *output_text = NULL;
    char *tmp_output_text = NULL;
    int output_index;
    char tmp_string[15];
    int index = 0;
    int size = 5;
    int first_time = 0;
    int i;

    output_text = (char *)malloc(size * sizeof(char *));
    output_text[0] = '\0';
    strcat(output_text, "IN: ");
    while (array_of_nodes[index].control_variable == 1)
    {
        if (array_of_nodes[index].gate_type == IN || array_of_nodes[index].gate_type == OUT)
        {
            if (array_of_nodes[index].gate_type == OUT && first_time == 0)
            {
                first_time = 1;
                size += 15;
            }
            else
            {
                size += 5;
            }

            tmp_output_text = output_text;
            output_text = (char *)malloc(size * sizeof(char *));
            for (i = 0; i < strlen(tmp_output_text); i++)
            {
                output_text[i] = tmp_output_text[i];
            }
            output_text[i] = '\0';
            free(tmp_output_text);
            if (first_time == 1)
            {
                sprintf(tmp_string, "T: %d  OUT: ", tact_number);
                strcat(output_text, tmp_string);
                first_time = 2;
            }
            if (array_of_nodes[index].gate_type == IN)
            {
                sprintf(tmp_string, "%d:%d ", array_of_nodes[index].node_id, array_of_nodes[index].output);
                strcat(output_text, tmp_string);
            }
            else if (array_of_nodes[index].gate_type == OUT)
            {
                if ((output_index = find_node_index(array_of_nodes, array_of_nodes[index].input_1)) != -1)
                {
                    if (array_of_nodes[output_index].output != 3)
                    {
                        sprintf(tmp_string, "%d:%d ", array_of_nodes[index].input_1,
                                array_of_nodes[output_index].output);
                    }
                    else
                    {
                        sprintf(tmp_string, "%d:x ", array_of_nodes[index].input_1);
                    }
                    strcat(output_text, tmp_string);
                }
            }
        }
        index++;
    }

    return output_text;
}

Node *parse_text_of_schema(char *text)
{
    Node *array_of_nodes = NULL;
    Node *tmp_array_of_nodes = NULL;
    int number_of_lines = 0;
    int i;
    char **array_of_lines;

    array_of_lines = split_text(text, '\n');
    number_of_lines = get_number_of_parts(text, '\n');
    if (text != NULL)
        free(text);
    for (i = 0; i < 2; i++)
    {
        if ((tmp_array_of_nodes = add_input_output_to_array_of_structs(array_of_lines[i], i + 1)) == NULL)
        {
            printf("Blad podczas wczytywania schematu w linii: %d", i + 1);
            free(array_of_nodes);
            return NULL;
        }
        array_of_nodes = merge_arrays(array_of_nodes, tmp_array_of_nodes);
    }
    tmp_array_of_nodes = (Node *)malloc((number_of_lines - 2) * sizeof(Node));
    for (i = 0; i < number_of_lines - 2; i++)
    {
        if ((tmp_array_of_nodes[i] = add_line_to_struct(array_of_lines[i + 2])).node_id == -1)
        {
            printf("Blad podczas wczytywania schematu w linii: %d", i + 3);
            free(tmp_array_of_nodes);
            free(array_of_nodes);
            return NULL;
        }
    }
    for (i = 0; i < number_of_lines; i++)
    {
        free(array_of_lines[i]);
    }
    free(array_of_lines);
    array_of_nodes = merge_arrays(array_of_nodes, tmp_array_of_nodes);
    return array_of_nodes;
}

Node add_line_to_struct(char *line)
{
    Node node;
    char gate_type[5];
    int id_node;
    int input_1;
    int input_2;

    sscanf(line, "%s", gate_type);
    if ((node.gate_type = convert_char_to_enum(gate_type)) == 0)
    {
        node.node_id = -1;
        return node;
    }
    if (node.gate_type == NEG && sscanf(line, "%s %d %d", gate_type, &input_1, &id_node) == 3)
    {
        node.node_id = id_node;
        node.output = 3;
        node.input_1 = input_1;
        node.input_2 = input_1;
        node.control_variable = 1;
    }
    else if (node.gate_type != NEG && sscanf(line, "%s %d %d %d", gate_type, &input_1, &input_2, &id_node) == 4)
    {
        node.node_id = id_node;
        node.output = 3;
        node.input_1 = input_1;
        node.input_2 = input_2;
        node.control_variable = 1;
    }
    else
    {
        node.node_id = -1;
    }
    return node;
}

Gate convert_char_to_enum(char *gate_type)
{
    if (!stricmp(gate_type, "AND"))
        return AND;
    else if (!stricmp(gate_type, "OR"))
        return OR;
    else if (!stricmp(gate_type, "NAND"))
        return NAND;
    else if (!stricmp(gate_type, "NOR"))
        return NOR;
    else if (!stricmp(gate_type, "XOR"))
        return XOR;
    else if (!stricmp(gate_type, "XNOR"))
        return XNOR;
    else if (!stricmp(gate_type, "NEG"))
        return NEG;
    else
        return 0;
}

Node *add_input_output_to_array_of_structs(char *line, int type)
{
    Node *tmp_array_of_nodes = NULL;
    Node *array_of_nodes = NULL;
    int number_of_parts;
    char **array_of_parts = NULL;
    int i, j;

    number_of_parts = get_number_of_parts(line, ' ');
    array_of_parts = split_text(line, ' ');
    if (stricmp(array_of_parts[0], "IN:") && (type == 1))
    {
        return NULL;
    }
    else if (stricmp(array_of_parts[0], "OUT:") && (type == 2))
    {
        return NULL;
    }
    else
    {
        for (i = 0; i < number_of_parts - 1; i++)
        {
            tmp_array_of_nodes = array_of_nodes;
            array_of_nodes = (Node *)malloc((i + 1) * sizeof(Node));

            for (j = 0; j < i; j++)
            {
                array_of_nodes[j] = tmp_array_of_nodes[j];
            }
            if (tmp_array_of_nodes != NULL)
                free(tmp_array_of_nodes);

            if (type == 1)
            {
                array_of_nodes[i].node_id = atoi(array_of_parts[i + 1]);
                array_of_nodes[i].output = 3;
                array_of_nodes[i].input_1 = 0;
                array_of_nodes[i].input_2 = 0;
                array_of_nodes[i].gate_type = IN;
                array_of_nodes[i].control_variable = 1;
            }
            else if (type == 2)
            {
                array_of_nodes[i].node_id = 0;
                array_of_nodes[i].output = 3;
                array_of_nodes[i].input_1 = atoi(array_of_parts[i + 1]);
                array_of_nodes[i].input_2 = atoi(array_of_parts[i + 1]);
                array_of_nodes[i].gate_type = OUT;
                array_of_nodes[i].control_variable = 1;
            }
        }
    }
    return array_of_nodes;
}

char **split_text(char *text, char delimeter)
{
    char **array_of_parts = NULL;
    int count_parts = 0;
    int size = 0;
    int number_of_parts = 0;
    int part_size = 0;
    int prv_i = 0;
    int i, j;

    number_of_parts = get_number_of_parts(text, delimeter);
    array_of_parts = (char **)malloc(number_of_parts * sizeof(char **));
    for (i = 0; i <= strlen(text); i++)
    {
        part_size = i - prv_i;

        if ((text[i] == delimeter || text[i] == '\0') && text[i - 1] != delimeter)
        {
            size = 0;
            array_of_parts[count_parts] = (char *)malloc(part_size + 1 * sizeof(char *));

            for (j = 0; j < part_size; j++)
            {
                if (text[prv_i + j] != delimeter)
                {
                    array_of_parts[count_parts][size] = text[prv_i + j];
                    size++;
                }
            }
            array_of_parts[count_parts][size] = '\0';
            count_parts++;
            prv_i = i;
        }
    }

    return array_of_parts;
}

Node *merge_arrays(Node *array_1, Node *array_2)
{
    Node *tmp_node_array = NULL;
    int size = 0;
    int array1_size = 0;
    int i;

    while (array_1 != NULL && array_1[array1_size].output == 3)
    {
        array1_size++;
    }
    while (array_2 != NULL && array_2[size].output == 3)
    {
        tmp_node_array = array_1;
        array_1 = (Node *)malloc((array1_size + size + 1) * sizeof(Node));

        for (i = 0; i < size + array1_size; i++)
        {
            array_1[i] = tmp_node_array[i];
        }
        if (tmp_node_array != NULL)
        {
            free(tmp_node_array);
        }

        array_1[size + array1_size] = array_2[size];
        size++;
    }
    if (array_2 != NULL)
    {
        free(array_2);
    }
    return array_1;
}

int get_number_of_parts(char *text, char delimeter)
{
    int parts = 0;
    int i;

    for (i = 0; i <= strlen(text); i++)
    {
        if ((text[i] == delimeter || text[i] == '\0') && text[i - 1] != delimeter)
        {
            parts++;
        }
    }
    return parts;
}

void deallocate_memory(int ***array, int i, int j)
{
    int k, l;

    for (k = i; k < 0; k--)
    {
        for (l = i; l < 0; l--)
        {
            free(array[k][l]);
        }
        free(array[k]);
    }
    free(array);
}
