/* === === === matrix.c === === === */

#include "include.h"
#include "matrixstruct.h"
#include "bitstruct.h"
#include "io.h"

extern MATRIX_LIST matrix_l;

void create_matrix(int rows,int cols) {
    MATRIX* m = (MATRIX*)malloc(sizeof(MATRIX));
    m->rows = rows;
    m->cols = cols;
    m->led = (RGB_LED***)malloc(rows * sizeof(RGB_LED**));

    for (int i = 0; i < rows; i++){
        m->led[i] = (RGB_LED**)malloc(cols * sizeof(RGB_LED*));
        for (int j = 0; j < cols; j++)
            m->led[i][j] = create_led(0,0,0,0,0);
    }

    matrix_l.list = (MATRIX**)realloc(matrix_l.list,(matrix_l.n+1)*sizeof(MATRIX*));
    matrix_l.list[matrix_l.n] = m;
    matrix_l.n++;
}

void input(LED_WICH grp,unsigned char n){
    int maxrow = matrix_l.list[n]->rows;
    int maxcol = matrix_l.list[n]->cols;
    RGB_LED*** led = matrix_l.list[n]->led;

    for(int r = 0;r<maxrow;r++){
        for(int c = 0; c < maxcol;c++){
            int num = get_num();
            modify_led(led[r][c], grp, num);
        }
    }
}

void input_matrix(unsigned char n){
    input(LED_R ,n);
    input(LED_G ,n);
    input(LED_B ,n);
    input(LED_TEMP ,n);
    input(LED_BR ,n);
    input(LED_TYPE ,n);
    input(LED_MODE ,n);
}

void free_matrix(unsigned char n){
    if(n >= matrix_l.n) return;
    MATRIX* m = matrix_l.list[n];
    for (int i = 0; i < m->rows; i++){
        for (int j = 0; j < m->cols; j++)
            free(m->led[i][j]);
        free(m->led[i]);
    }
    free(m->led);
    free(matrix_l.list[n]);

    for(int i = n; i < matrix_l.n-1; i++)
        matrix_l.list[i] = matrix_l.list[i+1];

    matrix_l.n--;
    if (matrix_l.n == 0) {
        free(matrix_l.list);
        matrix_l.list = NULL;
    } else {
        matrix_l.list = realloc(matrix_l.list, matrix_l.n * sizeof(MATRIX*));
    }
}

static uint64_t extract(uint64_t l, int padd, int size){
    return (l >> padd) & ((1ULL << size) - 1);
}

void get_matrix(unsigned char n){
    int rows = matrix_l.list[n]->rows;
    int cols = matrix_l.list[n]->cols;
    RGB_LED*** led = matrix_l.list[n]->led;

    for(int r = 0;r<rows;r++){
        for(int c = 0;c<cols;c++){
            RGB_LED* l=led[r][c];
            printf("%03llu,%03llu  ",
                extract(l->l, LED_PADD_R, LED_SIZE_R),
                extract(l->l, LED_PADD_TEMP, LED_SIZE_TEMP));
        }
        putchar('\n');

        for(int c = 0;c<cols;c++){
            RGB_LED* l=led[r][c];
            printf("%03llu,%03llu  ",
                extract(l->l, LED_PADD_G, LED_SIZE_G),
                extract(l->l, LED_PADD_BR, LED_SIZE_BR));
        }
        putchar('\n');

        for(int c = 0;c<cols;c++){
            RGB_LED* l=led[r][c];
            printf("%03llu,%02llu%llu  ",
                extract(l->l, LED_PADD_B, LED_SIZE_B),
                extract(l->l, LED_PADD_TYPE, LED_SIZE_TYPE),
                extract(l->l, LED_PADD_MODE, LED_SIZE_MODE));
        }
        putchar('\n');
        putchar('\n');
    }
}
char edit_is_cmd(char _buff[BUFF_MAX],char inbuff,unsigned char n,int* r,int* c,char* mode,char *wich){
    int rows = matrix_l.list[n]->rows;
    int cols = matrix_l.list[n]->cols;

    create_cmd_list(CMD_EDIT,(char*[]){"point","add","sub","exit"},4);
    char loc_cmd = cmd_buff(_buff,CMD_EDIT);

    if (loc_cmd == 1) {
        buff_input(&inbuff, _buff);
        long pr = 0, pc = 0;
        int i = 0;

        for (; i < inbuff && (_buff[i] != ':' && _buff[i] != ',' && _buff[i] != '.'); i++)
            if (_buff[i] >= '0' && _buff[i] <= '9')
                pr = pr * 10 + (_buff[i] - '0');

        for (i++; i < inbuff; i++)
            if (_buff[i] >= '0' && _buff[i] <= '9')
                pc = pc * 10 + (_buff[i] - '0');

        if (pr < rows && pc < cols) {
            *r = (int)pr;
            *c = (int)pc;
        }
        return 1;
    }

    if (loc_cmd == 2) { *mode = *mode==1?0:1; return 1; }
    if (loc_cmd == 3) { *mode = *mode==2?0:2; return 1; }
    if (loc_cmd == 4) { *mode = *mode==3?0:3; return 1; }

    create_cmd_list(CMD_LED,(char*[]){"temp","brg","r","g","b","type","mode","speed","sscale"},9);
    char wich_cmd = cmd_buff(_buff,CMD_LED) - 1;

    if (wich_cmd >= 0)
        *wich = (*wich != wich_cmd && wich_cmd+1) ? wich_cmd : *wich;

    return 0;
}
void edit_matrix(unsigned char n) {
    int rows = matrix_l.list[n]->rows;
    int cols = matrix_l.list[n]->cols;
    char inbuff;
    char _buff[BUFF_MAX];

    RGB_LED*** led = matrix_l.list[n]->led;
    
    int r = 0, c = 0;
    char mode = 0;
    char wich = 0;

    while (r < rows) {
        char input = buff_input(&inbuff,_buff);
        if (mode == 3) break;
        if (input == -1) break;

        if(edit_is_cmd(_buff,inbuff,n,&r,&c,&mode,&wich)) continue;
                
        long num = 0;
        int i = 0, sign = 1;

        if (inbuff > 0 && _buff[0] == '-') { sign = -1; i++; }
        for (; i < inbuff; i++) {
            if (_buff[i] >= '0' && _buff[i] <= '9')
                num = num * 10 + (_buff[i] - '0');
        }
        num *= sign;

        uint64_t old = 0;
        switch(wich){
            case LED_TEMP:
                old = (led[r][c]->l >> LED_PADD_TEMP) & 
                    ((1ULL<<LED_SIZE_TEMP)-1); break;
            case LED_BR:
                old = (led[r][c]->l >> LED_PADD_BR)   & 
                    ((1ULL<<LED_SIZE_BR)-1); break;
            case LED_R:      
                old = (led[r][c]->l >> LED_PADD_R)    & 
                    ((1ULL<<LED_SIZE_R)-1); break;
            case LED_G:      
                old = (led[r][c]->l >> LED_PADD_G)    & 
                    ((1ULL<<LED_SIZE_G)-1); break;
            case LED_B:      
                old = (led[r][c]->l >> LED_PADD_B)    & 
                    ((1ULL<<LED_SIZE_B)-1); break;
            case LED_TYPE:   
                old = (led[r][c]->l >> LED_PADD_TYPE) & 
                    ((1ULL<<LED_SIZE_TYPE)-1); break;
            case LED_MODE:   
                old = (led[r][c]->l >> LED_PADD_MODE) & 
                    ((1ULL<<LED_SIZE_MODE)-1); break;
            case LED_SPEED:  
                old = (led[r][c]->l >> LED_PADD_SPEED)& 
                    ((1ULL<<LED_SIZE_SPEED)-1); break;
            case LED_SSCALE: 
                old = (led[r][c]->l >> LED_PADD_SSCALE)& 
                    ((1ULL<<LED_SIZE_SSCALE)-1); break;
        }

        long val = old;

        switch(mode){
            case 0: val = num; break;
            case 1: val += num; break;
            case 2: val -= num; break;
        }

        modify_led(led[r][c], wich, val);

        if (++c >= cols) {
            c = 0;
            r++;
        }
    }
}

long long int get_matrix_cost(unsigned char n){
    int rows = matrix_l.list[n]->rows;
    int cols = matrix_l.list[n]->cols;
    RGB_LED*** led = matrix_l.list[n]->led;

    long long int cost=0;

    for(int r = 0;r<rows;r++){
        for(int c = 0;c<cols;c++){
            RGB_LED* l = led[r][c];

            uint64_t R = extract(l->l, LED_PADD_R, LED_SIZE_R);
            uint64_t G = extract(l->l, LED_PADD_G, LED_SIZE_G);
            uint64_t B = extract(l->l, LED_PADD_B, LED_SIZE_B);
            uint64_t TEMP = extract(l->l, LED_PADD_TEMP, LED_SIZE_TEMP);
            uint64_t BR = extract(l->l, LED_PADD_BR, LED_SIZE_BR);
            uint64_t MODE = extract(l->l, LED_PADD_MODE, LED_SIZE_MODE);

            cost += ((R + G + B)
                - (TEMP * (((long double)BR)/50)))
                * MODE;
        }
    }

    return cost;
}

void logic_matrix(unsigned char n1,unsigned char n2){
    char inbuff;
    char _buff[BUFF_MAX];
    
    char cmd = 0;
    {
        buff_input(&inbuff,_buff);
        char* cmds[] = {"==","!=",">=","<=",">","<"};
        create_cmd_list(CMD_LOGIC,cmds,6);

        cmd = cmd_buff(_buff,CMD_LOGIC);
        del_cmd_list(CMD_LOGIC);
    }

    long long int cost1 = get_matrix_cost(n1);
    long long int cost2 = get_matrix_cost(n2);

    switch(cmd){
        case 0: putchar('-');putchar('1'); break;
        case 1: putchar(cost1==cost2?'1':'0'); break;
        case 2: putchar(cost1!=cost2?'1':'0'); break;
        case 3: putchar(cost1>=cost2?'1':'0'); break;
        case 4: putchar(cost1<=cost2?'1':'0'); break;
        case 5: putchar(cost1>cost2?'1':'0'); break;
        case 6: putchar(cost1<cost2?'1':'0'); break;
    }
}

int get_rand(int min, int max) {
    return (double)rand() / (RAND_MAX + 1.0) * (max - min) + min;
}

void rand_matrix(unsigned char n){
    unsigned int lo, hi;
    __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
    srand(((unsigned long long)hi << 32) | lo);

    int rows = matrix_l.list[n]->rows;
    int cols = matrix_l.list[n]->cols;
    RGB_LED*** led = matrix_l.list[n]->led;

    for (int r = 0; r < rows; r++){
        for(int c = 0; c < cols; c++){
            modify_led(led[r][c], LED_R, get_rand(0,255));
            modify_led(led[r][c], LED_G, get_rand(0,255));
            modify_led(led[r][c], LED_B, get_rand(0,255));
            modify_led(led[r][c], LED_TEMP, get_rand(2000,6000));
            modify_led(led[r][c], LED_BR, get_rand(0,255));
            modify_led(led[r][c], LED_TYPE, get_rand(0,63));
            modify_led(led[r][c], LED_MODE, get_rand(0,3));
        }
    }
}

void copy_matrix(unsigned char n1, unsigned char n2){
    int rows = matrix_l.list[n1]->rows;
    int cols = matrix_l.list[n1]->cols;
    RGB_LED*** led = matrix_l.list[n1]->led;

    create_matrix(rows,cols);

    RGB_LED*** led2 = matrix_l.list[matrix_l.n-1]->led;

    for(int r = 0; r<rows;r++){
        for(int c = 0; c<cols;c++){
            led2[r][c]->l = led[r][c]->l;
        }
    }

    if(n2 < matrix_l.n){
    MATRIX* dst = matrix_l.list[n2];
    MATRIX* src = matrix_l.list[matrix_l.n-1];

    for(int r=0;r<rows;r++)
        for(int c=0;c<cols;c++)
            dst->led[r][c]->l = src->led[r][c]->l;

    free_matrix(matrix_l.n-1);
    }
}

unsigned char is_Matrix_Exist(unsigned char n){

    if(matrix_l.n == 0){
        create_matrix(4,4);
        rand_matrix(0);
        return 0;
    }

    if(n >= matrix_l.n){
        return matrix_l.n - 1;
    }

    return n;
}

