#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>


typedef struct{
    char **words;
    size_t currentSize;
    size_t capacity;
}Words;


typedef struct {
    bool up,down,right,left,upRight,upLeft,downRight,downLeft;
}Direction;

void startALloc (Words *words){
    words->words = (char**) malloc(10 * sizeof(char*));
    words->capacity = 10;
    words->currentSize = 0;
}

void resize (Words *words){
    if (words->currentSize == words->capacity){
        words->capacity = words->capacity * 2 + 4;
        words->words = (char**) realloc(words->words,words->capacity * sizeof (char*));
    }
}

void removeWhitespace(char *str) {
    size_t i = 1, j = 0;

    while (str[i] != '\0' ) {
        if (!isspace(str[i])) {
            str[j++] = str[i];
        }
        i++;
    }
    str[j] = '\0';
}


void makeString (Words words, size_t lenOfWords,int** usableWords){
    size_t count = 1;
    printf("Tajenka:\n");

    for (size_t i = 0; i < words.currentSize; ++i) {

        for (size_t j = 0; j < lenOfWords-1; ++j) {

            if (words.words[i][j] != '.' && usableWords[i][j] != 1){
                printf("%c",words.words[i][j]);
                if (count == 60){
                    printf("\n");
                    count = 0;
                }
                count++;
            }
        }
    }
    if (count != 1)
        printf("\n");
}
void removeUp (size_t len, size_t row , size_t col, int** usableWords) {
    for (size_t i = 0; i < len; i++) {
        size_t r = row - i;

        usableWords[r][col] = 1;
    }
}
void removeDown (size_t len, size_t row , size_t col, int** usableWords) {
    for (size_t i = 0; i < len; i++) {
        size_t r = row + i;

        usableWords[r][col] = 1;
    }
}
void removeLeft (size_t len, size_t row , size_t col, int** usableWords) {
    for (size_t i = 0; i < len; i++) {
        size_t c = col - i;

        usableWords[row][c] = 1;
    }
}
void removeRight (size_t len, size_t row , size_t col, int** usableWords) {
    for (size_t i = 0; i < len; i++) {
        size_t c = col + i;

        usableWords[row][c] = 1;
    }
}
void removeUpLeft (size_t len, size_t row , size_t col, int** usableWords) {
    for (size_t i = 0; i < len; i++) {
        size_t r = row - i;
        size_t c = col - i;

        usableWords[r][c] = 1;
    }
}
void removeUpRight (size_t len, size_t row , size_t col, int** usableWords) {
    for (size_t i = 0; i < len; i++) {
        size_t r = row - i;
        size_t c = col + i;

        usableWords[r][c] = 1;
    }
}
void removeDownLeft (size_t len, size_t row , size_t col, int** usableWords) {
    for (size_t i = 0; i < len; i++) {
        size_t r = row + i;
        size_t c = col - i;

        usableWords[r][c] = 1;
    }
}
void removeDownRight (size_t len, size_t row , size_t col, int** usableWords) {
    for (size_t i = 0; i < len; i++) {
        size_t r = row + i;
        size_t c = col + i;

        usableWords[r][c] = 1;
    }
}

bool checkUp (Words words , char *subStr , size_t row , size_t col){
    size_t len = strlen(subStr);
    for (size_t i = 0; i < len; i++) {
        size_t r = row - i;

        if (words.words[r][col] != subStr[i]) {
            return false;
        }
    }
    return true;
}

bool checkDown (Words words , char *subStr , size_t row , size_t col){
    size_t len = strlen(subStr);
    for (size_t i = 0; i < len; i++) {
        size_t r = row + i;

        if (words.words[r][col] != subStr[i]) {
            return false;
        }
    }
    return true;
}
bool checkRight (Words words , char *subStr , size_t row , size_t col){
    size_t len = strlen(subStr);
    for (size_t i = 0; i < len; i++) {
        size_t c = col + i;

        if (words.words[row][c] != subStr[i]) {
            return false;
        }
    }
    return true;
}
bool checkLeft (Words words , char *subStr , size_t row , size_t col){
    size_t len = strlen(subStr);
    for (size_t i = 0; i < len; i++) {
        size_t c = col - i;

        if (words.words[row][c] != subStr[i]) {
            return false;
        }
    }
    return true;
}
bool checkUpRight (Words words , char *subStr , size_t row , size_t col){
    size_t len = strlen(subStr);
    for (size_t i = 0; i < len; i++) {
        size_t r = row - i;
        size_t c = col + i;

        if (words.words[r][c] != subStr[i]) {
            return false;
        }
    }
    return true;
}
bool checkUpLeft (Words words , char *subStr , size_t row , size_t col){
    size_t len = strlen(subStr);
    for (size_t i = 0; i < len; i++) {
        size_t r = row - i;
        size_t c = col - i;

        if (words.words[r][c] != subStr[i]) {
            return false;
        }
    }
    return true;
}
bool checkDownRight (Words words , char *subStr , size_t row , size_t col){
    size_t len = strlen(subStr);
    for (size_t i = 0; i < len; i++) {
        size_t r = row + i;
        size_t c = col + i;

        if (words.words[r][c] != subStr[i]) {
            return false;
        }
    }
    return true;
}
bool checkDownLeft (Words words , char *subStr , size_t row , size_t col){
    size_t len = strlen(subStr);
    for (size_t i = 0; i < len; i++) {
        size_t r = row + i;
        size_t c = col - i;

        if (words.words[r][c] != subStr[i]) {
            return false;
        }
    }
    return true;
}


void findSubStr(Words words, char *subStr, int** usableWords , size_t lenOfWords, bool remove){
    size_t len,count;
    Direction dir;
    len = strlen(subStr);
    char leadingAlpha = subStr[0];
    count = 0;
    for (size_t row = 0; row < words.currentSize; ++row) {
        for (size_t col = 0; col < lenOfWords; ++col) {
            if (words.words[row][col] == leadingAlpha){
                dir.up = row+1 >= len ? true : false;
                dir.down = words.currentSize-row >= len ? true : false;
                dir.right = lenOfWords-col >= len ? true : false;
                dir.left = col+1 >= len ? true : false;
                dir.upLeft = dir.up && dir.left ? true : false;
                dir.upRight = dir.up && dir.right ? true : false;
                dir.downLeft = dir.down && dir.left ? true : false;
                dir.downRight = dir.down && dir.right ? true : false;

                if (dir.up && checkUp(words,subStr,row,col)){
                    if (remove){
                        removeUp(strlen(subStr),row,col,usableWords);
                    }
                    count++;
                }
                if (dir.down && checkDown(words,subStr,row,col)) {
                    if (remove){
                        removeDown(strlen(subStr),row,col,usableWords);
                    }
                    count++;
                };
                if (dir.right && checkRight(words,subStr,row,col)) {
                    if (remove){
                        removeRight(strlen(subStr),row,col,usableWords);
                    }
                    count++;
                };
                if (dir.left && checkLeft(words,subStr,row,col)){
                    if (remove){
                        removeLeft(strlen(subStr),row,col,usableWords);
                    }
                    count++;
                };
                if (dir.upLeft && checkUpLeft(words,subStr,row,col)){
                    if (remove){
                        removeUpLeft(strlen(subStr),row,col,usableWords);
                    }
                    count++;
                };
                if (dir.upRight && checkUpRight(words,subStr,row,col)){
                    if (remove){
                        removeUpRight(strlen(subStr),row,col,usableWords);
                    }
                    count++;
                };
                if (dir.downLeft && checkDownLeft(words,subStr,row,col)) {
                    if (remove){
                        removeDownLeft(strlen(subStr),row,col,usableWords);
                    }
                    count++;
                };
                if (dir.downRight && checkDownRight(words,subStr,row,col)) {
                    if (remove){
                        removeDownRight(strlen(subStr),row,col,usableWords);
                    }
                    count++;
                };
            }
        }
    }
    printf("%s: %zux\n",subStr,count);
}


int main (void){

    Words words;
    startALloc(&words);
    words.currentSize = 0;

    size_t buffer;
    size_t lenOfWords = 0;
    size_t currentLen;

    printf("Osmismerka:\n");

    while ((currentLen = getline(&words.words[words.currentSize],&buffer,stdin)) != (size_t)EOF){

        if (words.currentSize == 0){
            lenOfWords = currentLen;
        }

        if (words.words[0][0] == '\n'){
            printf("Nespravny vstup.\n");
            free(words.words);
            return 0;
        }

        if (words.currentSize > 0 && currentLen == 1 && words.words[words.currentSize][0] == '\n')
            break;

        if (currentLen != lenOfWords){
            printf("Nespravny vstup.\n");
            free(words.words);
            return 0;
        }
        for (size_t i = 0; i < lenOfWords-1; ++i) {
            if ((words.words[words.currentSize][i] < 'a' || words.words[words.currentSize][i] > 'z') && words.words[words.currentSize][i] != '.'){
                printf("Nespravny vstup.\n");
                free(words.words);
                return 0;
            }
        }
        words.currentSize++;
        resize(&words);
    }
    int **usableWords = (int **) calloc(words.currentSize+10,sizeof (int*));
    for (size_t i = 0; i < words.currentSize; ++i) {
        usableWords[i] = (int *)calloc(lenOfWords+10,sizeof (int));
    }
    char* subStr = NULL;
    size_t mem;
    while (getline(&subStr,&mem,stdin) != EOF){
        if (subStr[0] == '?'){
            if (strlen(subStr) != 2){
                printf("Nespravny vstup.\n");
                free(words.words);
                return 0;
            }
            makeString(words,lenOfWords,usableWords);
        }
        else if (subStr[0] == '#'){
            bool remove = false;
            removeWhitespace(subStr);
            size_t lenOfSubStr = strlen(subStr);
            if (lenOfSubStr < 2){
                printf("Nespravny vstup.\n");
                free(words.words);
                return 0;
            }
            for (size_t i = 0; i < lenOfSubStr; ++i) {
                if (subStr[i] < 'a' || subStr[i] > 'z'){
                    printf("Nespravny vstup.\n");
                    free(words.words);
                    return 0;
                }
            }
            findSubStr(words,subStr,usableWords,lenOfWords,remove);
        }
        else if (subStr[0] == '-'){
            bool remove = true;
            removeWhitespace(subStr);
            size_t lenOfSubStr = strlen(subStr);
            if (lenOfSubStr < 2){
                printf("Nespravny vstup.\n");
                free(words.words);
                return 0;
            }
            for (size_t i = 0; i < lenOfSubStr; ++i) {
                if (subStr[i] < 'a' || subStr[i] > 'z'){
                    printf("Nespravny vstup.\n");
                    free(words.words);
                    return 0;
                }
            }
            findSubStr(words,subStr,usableWords,lenOfWords,remove);
        }
        else{
            printf("Nespravny vstup.\n");
            free(words.words);
            return 0;
        }

    }
    free(words.words);
    return 0;
}
