  // Windows - 1251 (ANSI)

#include "AE_bfield.h"



bfield* bfield_create(uint32_t width, uint32_t height) {

        #ifdef _AE_bfield_debug_
        printf("  AE_bfield: bfield_create >>>\n");
        #endif

    uint64_t size = (uint64_t)width * (uint64_t)height;

    if (size % 8) size = (size >> 3) + 1;
    else size >>= 3;

    size += sizeof(bfield);

    if (size > UINT32_MAX) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_create: size > UINT32_MAX");
            #endif

        return 0;
    }

        #ifdef _AE_bfield_debug_
        printf("AE_bfield: bfield_create: size = %u\n", (uint32_t)size);
        #endif

    bfield* tbfield = malloc((uint32_t)size);

    if (!tbfield) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_create: malloc\n");
            #endif

        return 0;
    }

    tbfield->size = (uint32_t)size;
    tbfield->width = width;
    tbfield->height = height;

        #ifdef _AE_bfield_debug_
        printf("AE_bfield: bfield_create: address = %p\n", tbfield);
        printf("AE_bfield: bfield_create: width = %u\n", tbfield->width);
        printf("AE_bfield: bfield_create: height = %u\n", tbfield->height);
        printf("  >>> AE_bfield: bfield_create.\n");
        #endif

    return tbfield;
}



void bfield_destroy(bfield** ttbfield) {

        #ifdef _AE_bfield_debug_
        printf("  AE_bfield: bfield_destroy >>>\n");
        #endif

    if (*ttbfield == 0) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_destroy: *ttbfield == 0\n");
            #endif

        return;
    }

    free(*ttbfield);

    *ttbfield = 0;

        #ifdef _AE_bfield_debug_
        printf("  >>> AE_bfield: bfield_destroy.\n");
        #endif
}



uint32_t bfield_resize(bfield** ttbfield, uint32_t new_width, uint32_t new_height) {

        #ifdef _AE_bfield_debug_
        printf("  AE_bfield: bfield_resize >>>\n");
        #endif

    if (!(*ttbfield)) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_resize: *ttbfield == 0\n");
            #endif

        return UINT32_MAX;
    }

    if (((*ttbfield)->width == new_width) && ((*ttbfield)->height == new_height)) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_resize: new == old\n");
            #endif

        return 0;
    }

    bfield* tbfield = bfield_create(new_width, new_height);

    if (!tbfield) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_resize: bfield_create\n");
            #endif

        return UINT32_MAX;
    }

    for (uint32_t y = 0; y < new_height; y++) {

        for (uint32_t x = 0; x < new_width; x++) {

            if ((x < (*ttbfield)->width) && (y < (*ttbfield)->height)) {

                (bfield_get(*ttbfield, x, y)) ? bfield_set(tbfield, x, y) : \
                                                bfield_clear(tbfield, x, y);

            } else bfield_clear(tbfield, x, y);
        }
    }

    bfield_destroy(ttbfield);
    *ttbfield = tbfield;

        #ifdef _AE_bfield_debug_
        printf("  >>> AE_bfield: bfield_resize.\n");
        #endif

    return 0;
}



bfield* bfield_clone(bfield* tbfield) {

        #ifdef _AE_bfield_debug_
        printf("  AE_bfield: bfield_clone >>>\n");
        #endif

    if (!tbfield) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_clone: tbfield == 0\n");
            #endif

        return 0;
    }

    bfield* clone = bfield_create(tbfield->width, tbfield->height);

    if (!clone) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_clone: bfield_create\n");
            #endif

        return 0;
    }

    memcpy(clone, tbfield, tbfield->size);

        #ifdef _AE_bfield_debug_
        printf("  >>> AE_bfield: bfield_clone.\n");
        #endif

    return clone;
}



void bfield_set(bfield* tbfield, unsigned int x, unsigned int y) {

        #ifdef _AE_bfield_debug_
        printf("  AE_bfield: bfield_set >>>\n");
        #endif

    if (!tbfield) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_set: !tbfield == 0\n");
            #endif

        return;
    }

    if ((x >= tbfield->width) || (y >= tbfield->height)) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_set: (x >= width) or (y >=height)\n");
            #endif
        
        return;
    }

    uint64_t pos = y * tbfield->width + x;

    switch (pos%8) {

        case 7:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] |= 0x80; // 1000 0000
            break;

        case 6:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] |= 0x40; // 0100 0000
            break;

        case 5:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] |= 0x20; // 0010 0000
            break;

        case 4:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] |= 0x10; // 0001 0000
            break;

        case 3:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] |= 0x08; // 0000 1000
            break;

        case 2:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] |= 0x04; // 0000 0100
            break;

        case 1:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] |= 0x02; // 0000 0010
            break;

        case 0:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] |= 0x01; // 0000 0001
            break;
    }

        #ifdef _AE_bfield_debug_
        printf("  >>> AE_bfield: bfield_set.\n");
        #endif
}



void bfield_clear(bfield* tbfield, unsigned int x, unsigned int y) {

        #ifdef _AE_bfield_debug_
        printf("  AE_bfield: bfield_clear >>>\n");
        #endif

    if (!tbfield) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_clear: tbfield == 0\n");
            #endif

        return;
    }

    if ((x >= tbfield->width) || (y >= tbfield->height)) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_clear: (x >= width) or (y >= height)\n");
            #endif
        
        return;
    }

    uint64_t pos = y * tbfield->width + x;

    switch (pos%8) {

        case 7:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] &= 0x7F; // 0111 1111
            break;

        case 6:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] &= 0xBF; // 1011 1111
            break;

        case 5:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] &= 0xDF; // 1101 1111
            break;

        case 4:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] &= 0xEF; // 1110 1111
            break;

        case 3:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] &= 0xF7; // 1111 0111
            break;

        case 2:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] &= 0xFB; // 1111 1011
            break;

        case 1:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] &= 0xFD; // 1111 1101
            break;

        case 0:
            pos >>= 3;
            tbfield->data[(uint32_t)pos] &= 0xFE; // 1111 1110
            break;
    }

        #ifdef _AE_bfield_debug_
        printf("  >>> AE_bfield: bfield_clear.\n");
        #endif
}



uint32_t bfield_get(bfield* tbfield, unsigned int x, unsigned int y)  {

        #ifdef _AE_bfield_debug_
        printf("  AE_bfield: bfield_get >>>\n");
        #endif

    if (!tbfield) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_get: tbfield == 0\n");
            #endif

        return 0;
    }

    if ((x >= tbfield->width) || (y >= tbfield->height)) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_get: (x >= width) or (y >= height)\n");
            #endif
        
        return 0;
    }

    uint64_t pos = y * tbfield->width + x;
    uint32_t ret;

    switch (pos%8) {

        case 7:
            pos >>= 3;
            ret = tbfield->data[(uint32_t)pos] & 0x80; // 1000 0000
            break;

        case 6:
            pos >>= 3;
            ret = tbfield->data[(uint32_t)pos] & 0x40; // 0100 0000
            break;

        case 5:
            pos >>= 3;
            ret = tbfield->data[(uint32_t)pos] & 0x20; // 0010 0000
            break;

        case 4:
            pos >>= 3;
            ret = tbfield->data[(uint32_t)pos] & 0x10; // 0001 0000
            break;

        case 3:
            pos >>= 3;
            ret = tbfield->data[(uint32_t)pos] & 0x08; // 0000 1000
            break;

        case 2:
            pos >>= 3;
            ret = tbfield->data[(uint32_t)pos] & 0x04; // 0000 0100
            break;

        case 1:
            pos >>= 3;
            ret = tbfield->data[(uint32_t)pos] & 0x02; // 0000 0010
            break;

        case 0:
            pos >>= 3;
            ret = tbfield->data[(uint32_t)pos] & 0x01; // 0000 0001
            break;
    }

        #ifdef _AE_bfield_debug_
        printf("  >>> AE_bfield: bfield_get.\n");
        #endif

    return (ret) ? 1 : 0;
}



void bfield_not(bfield* tbfield, uint32_t x, uint32_t y) {

        #ifdef _AE_bfield_debug_
        printf("  AE_bfield: bfield_not >>>\n");
        #endif

    if (!tbfield) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_not: tbfield == 0\n");
            #endif

        return;
    }

    if ((x >= tbfield->width) || (y >= tbfield->height)) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_not: (x >= width) or (y >= height)\n");
            #endif
        
        return;
    }

    uint64_t pos = y * tbfield->width + x;

    switch (pos%8) {

        case 7:
            pos >>= 3;

            if (tbfield->data[(uint32_t)pos] & 0x80) tbfield->data[(uint32_t)pos] &= 0x7F;
            else tbfield->data[(uint32_t)pos] |= 0x80;

            break;

        case 6:
            pos >>= 3;

            if (tbfield->data[(uint32_t)pos] & 0x40) tbfield->data[(uint32_t)pos] &= 0xBF;
            else tbfield->data[(uint32_t)pos] |= 0x40;

            break;

        case 5:
            pos >>= 3;

            if (tbfield->data[(uint32_t)pos] & 0x20) tbfield->data[(uint32_t)pos] &= 0xDF;
            else tbfield->data[(uint32_t)pos] |= 0x20;

            break;

        case 4:
            pos >>= 3;

            if (tbfield->data[(uint32_t)pos] & 0x10) tbfield->data[(uint32_t)pos] &= 0xEF;
            else tbfield->data[(uint32_t)pos] |= 0x10;

            break;

        case 3:
            pos >>= 3;

            if (tbfield->data[(uint32_t)pos] & 0x08) tbfield->data[(uint32_t)pos] &= 0xF7;
            else tbfield->data[(uint32_t)pos] |= 0x08;

            break;

        case 2:
            pos >>= 3;

            if (tbfield->data[(uint32_t)pos] & 0x04) tbfield->data[(uint32_t)pos] &= 0xFB;
            else tbfield->data[(uint32_t)pos] |= 0x04;

            break;

        case 1:
            pos >>= 3;

            if (tbfield->data[(uint32_t)pos] & 0x02) tbfield->data[(uint32_t)pos] &= 0xFD;
            else tbfield->data[(uint32_t)pos] |= 0x02;

            break;

        case 0:
            pos >>= 3;

            if (tbfield->data[(uint32_t)pos] & 0x01) tbfield->data[(uint32_t)pos] &= 0x7E;
            else tbfield->data[(uint32_t)pos] |= 0x01;

            break;
    }

        #ifdef _AE_bfield_debug_
        printf("  >>> AE_bfield: bfield_not.\n");
        #endif
}



uint32_t bfield_save(bfield* tbfield, char* name) {

        #ifdef _AE_bfield_debug_
        printf("  AE_bfield: bfield_save >>>\n");
        #endif

    if ((!tbfield) || (!name)) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_save: (tbfield == 0) or (name == 0)\n");
            #endif

        return UINT32_MAX;
    }

    FILE* f = fopen(name, "wb");

    if (!f) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_save: fopen\n");
            #endif

        return UINT32_MAX;
    }

        #ifdef _AE_bfield_debug_
        printf("AE_bfield: bfield_save: data size = %u\n", tbfield->size);
        #endif

    if (fwrite(tbfield, 1, tbfield->size, f) != tbfield->size) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_save: fwrite\n");
            #endif

        fclose(f);
        return UINT32_MAX;
    }

    fclose(f);

        #ifdef _AE_bfield_debug_
        printf("  >>> AE_bfield: bfield_save.\n");
        #endif

    return 0;
}



bfield* bfield_load(char* name) {

        #ifdef _AE_bfield_debug_
        printf("  AE_bfield: bfield_load >>>\n");
        #endif

    if (!name) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_load: name == 0\n");
            #endif

        return 0;
    }

    bfield* tbfield = bfield_create(0, 0);

    if (!tbfield) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_load: tbfield == 0\n");
            #endif

        return 0;
    }

    FILE* f = fopen(name, "rb");

    if (!f) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_load: fopen\n");
            #endif

        return 0;
    }

    if (fread(tbfield, 1, sizeof(bfield), f) != sizeof(bfield)) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_load: fread(1)\n");
            #endif

        bfield_destroy(&tbfield);
        return 0;
    }

    bfield* tbfield_2 = bfield_create(tbfield->width, tbfield->height);

    if (!tbfield_2) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_load: tbfield_2 == 0\n");
            #endif

        bfield_destroy(&tbfield);
        return 0;
    }

    bfield_destroy(&tbfield);

    if (fread(tbfield_2->data, 1, tbfield_2->size - sizeof(bfield), f) != \
        (tbfield_2->size - sizeof(bfield))) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_load: fread (2)\n");
            #endif

        bfield_destroy(&tbfield_2);

        return 0;
    }

    fclose(f);

        #ifdef _AE_bfield_debug_
        printf("  >>> AE_bfield: bfield_load.\n");
        #endif

    return tbfield_2;
}


void bfield_info(bfield* tbfield, char* name) {

        #ifdef _AE_bfield_debug_
        printf("  AE_bfield: bfield_info >>>\n");
        #endif

    if ((!tbfield) || (!name)) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_info: (tbfield == 0) or (name == 0)\n");
            #endif

        return;
    }

    FILE* f = fopen(name, "w");

    if (!f) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_info: fopen\n");
            #endif

        return;
    }

    fprintf(f, "    ... AE_bfield info file ...\n");
    fprintf(f, "    Address    : %p\n", tbfield);
    fprintf(f, "    Size       : %u\n", tbfield->size);
    fprintf(f, "    Width      : %u\n", tbfield->width);
    fprintf(f, "    Height     : %u\n\n", tbfield->height);
    fprintf(f, "    ... DATA ...\n");

    for (uint32_t y = 0; y < tbfield->height; y++) {
        fprintf(f,"    ");

        for (uint32_t x = 0; x < tbfield->width; x++) {
            (bfield_get(tbfield, x, y)) ? fprintf(f, "+") : fprintf(f, "O");
        }

        fprintf(f,"\n");
    }

    fclose(f);

        #ifdef _AE_bfield_debug_
        printf("  >>> AE_bfield: bfield_info.\n");
        #endif
}



void bfield_zero(bfield* tbfield) {

        #ifdef _AE_bfield_debug_
        printf("  AE_bfield: bfield_zero >>>\n");
        #endif

    if (!tbfield) {

            #ifdef _AE_bfield_debug_
            printf("AE_bfield: bfield_zero: tbfield == 0\n");
            #endif

        return;
    }

    for (uint32_t i = 0; i < tbfield->size - sizeof(bfield); i++) tbfield->data[i] = 0;

        #ifdef _AE_bfield_debug_
        printf("  >>> AE_bfield: bfield_zero.\n");
        #endif
}