#include<stdio.h>
#include<string.h>
#include<limits.h>
#include<stdint.h>

#define BUFSIZE 48
#define NUMENCDATA 4

void printb(unsigned int v) {
  unsigned int mask = (int)1 << (sizeof(v) * CHAR_BIT - 1);
  do putchar(mask & v ? '1' : '0');
  while (mask >>= 1);
}

void putb(unsigned int v) {
  putchar('0'), putchar('b'), printb(v), putchar('\n');
}

static const size_t sixbase = 0b111111;
static const size_t eigbase = 0b11111111;
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

enum State { First = 1, Second = 2, Third = 3, Finish = 4 };
typedef struct {
    size_t bytes;
    size_t eqNum;
    uint8_t org[3];
    enum State state;
} FSM;

static FSM initState = { 0, 0, {0}, First };


void FSM_init(FSM *self) {
    *self = initState;
}

void FSM_stack(FSM *self, uint8_t c) {
    size_t stack = (size_t)c;
    switch (self->state) {
        case First:
            self->bytes = (stack << 16);
            self->state = Second;
            self->org[0] = c;
            break;
        case Second:
            self->bytes = self->bytes | (stack << 8) ;
            self->state = Third;
            self->org[1] = c;
            break;
        case Third:
            self->bytes = self->bytes | stack;
            self->state = Finish;
            self->org[2] = c;
            break;
        case Finish:
            fprintf(stderr,"スタックがいっぱいです。\n");
            break;
    }
}

void FSM_encode_flush(FSM *self) {
    uint8_t res[NUMENCDATA];
    if(self->state == Finish) { 
        for(size_t it = 0; it != NUMENCDATA; ++it) {
            res[it] = table[(self->bytes & (sixbase << (18 - 6 * it))) >> (18 - 6 * it)];
        }
        fwrite(res, sizeof(uint8_t), 2, stdout);
        if(self->eqNum == 2) { 
            printf("==");
        }
        if(self->eqNum == 1) {
            fwrite(res+2, sizeof(uint8_t), 1, stdout);
            printf("=");
        }
        if(self->eqNum == 0) {
            fwrite(res+2, sizeof(uint8_t), 2, stdout);
        }
//        printf(" : ");
//        printf("%c%c%c :",self->org[0], self->org[1], self->org[2]);
//        putb(self->bytes);
        *self = initState;
    }
}

void FSM_pre_terminal_proc(FSM *self) {
    switch (self->state) {
        case First:
            break;
        case Second:
            self->eqNum = 2;
            FSM_stack(self,0);
            FSM_stack(self,0);
            break;
        case Third:
            self->eqNum = 1;
            FSM_stack(self,0);
            break;
        case Finish:
            break;
    }
    return;
}


void b64enc(FILE *fp) {
    FSM fsm;
    FSM_init(&fsm);
    uint8_t buf[BUFSIZE];
    size_t nread;
    while((nread = fread(buf, sizeof(uint8_t), BUFSIZE, fp)) > 0) {
        for(size_t it = 0; it != nread; ++it) {
            FSM_stack(&fsm, buf[it]);
            FSM_encode_flush(&fsm);
        }
        if(feof(fp)) {
            FSM_pre_terminal_proc(&fsm);
            FSM_encode_flush(&fsm);
        }
        printf("\n");
    }
        
}


int main(void) {
    FILE *fp;
    if( fopen_s(&fp,"base64s.c", "rb") < 0) {
        fprintf(stderr, "ファイルを開けませんでした!\n");
        return 1;
    }
    b64enc(fp);
    fclose(fp);
    return 0;
}
//