
import sre_parse
import pprint
import random


class RandomRegexStringGenerator:
    def __init__(self):
        self._words = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
        self._digits = '0123456789'
        self._puncts = '!"#$%&()*+,-./:;<=>?@[\]^_`{|}~'
        self._whitespaces = ' \t'  #  ' \t\n\r\v\f' 에서 조정
        self._max_repeat = 5 # 무한 수량자가 나왔을때 최대 반복 리밋
        self._result = ''

    def _gen_char(self, value):
        return chr(value)

    def _gen_range(self, min_value, max_value):
        # random 선택
        idx = random.randint(min_value, max_value)
        return chr(idx)

    def _gen_in_word(self):
        return self._words[random.randint(0, len(self._words)-1)]

    def _gen_in_digit(self):
        return self._digits[random.randint(0, len(self._digits)-1)]

    def _gen_in_whitespace(self):
        return self._whitespaces[random.randint(0, len(self._whitespaces)-1)]

    def _gen_in_punct(self):
        return self._puncts[random.randint(0, len(self._puncts)-1)]

    def _gen_class(self, cls_itm):
        if cls_itm[0] == sre_parse.LITERAL:
            self._result += self._gen_char(cls_itm[1])

        elif cls_itm[0] == sre_parse.RANGE:
            self._result += self._gen_range(cls_itm[1][0], cls_itm[1][1])
            
        elif cls_itm[0] == sre_parse.CATEGORY:
            if cls_itm[1] == sre_parse.CATEGORY_DIGIT:
                self._result += self._gen_in_digit()
                
            elif cls_itm[1] == sre_parse.CATEGORY_WORD:
                self._result += self._gen_in_word()
                
            elif cls_itm[1] == sre_parse.CATEGORY_SPACE:
                self._result += self._gen_in_whitespace()
            
            elif cls_itm[1] == sre_parse.CATEGORY_NOT_DIGIT:
                self._result += self._gen_in_punct()  # NOT-DIGIT => PUNCT
                
            elif cls_itm[1] == sre_parse.CATEGORY_NOT_WORD:
                self._result += self._gen_in_punct() # NOT-WORD => PUNCT
                
            elif cls_itm[1] == sre_parse.CATEGORY_NOT_SPACE:
                self._result += self._gen_in_word()  # NOT-SPACE => WORD

    def _gen_negate_class(self, body):
        # 문자클래스에 속하지 않는 문자 랜덤 지정
        # 완전 랜덤은 아니며 min, max  근방의 값으로 함
        min_value = 9999999
        max_value = 32
        for cls_itm in body:
            if cls_itm[0] == sre_parse.LITERAL:
                min_value = min(cls_itm[1], min_value)
                max_value = max(cls_itm[1], max_value)
            elif cls_itm[0] == sre_parse.RANGE:
                min_value = min(cls_itm[1][0], min_value)
                max_value = max(cls_itm[1][1], max_value)

        if min_value-1 >= 32:
            # random 선택
            self._result += chr( random.randint(32, min_value-1) )
        else:
            if max_value + 1 <= 126:
                self._result += chr( random.randint(max_value + 1, 126) )
            else:
                # ascii 범위 벗어남, 그냥 표정
                self._result += chr( random.randint(0x1F600, 0x1F609) )




    def _proc_cmd(self, itm):
        # 단문자
        if itm[0] == sre_parse.LITERAL:
            self._result += self._gen_char(itm[1])

        # 문자클래스
        elif itm[0] == sre_parse.IN:
            start_num = 0
            body = itm[1]
            if body[0][0] == sre_parse.NEGATE: # 제외조건 TODO:
                start_num = 1
                self._gen_negate_class(body[1:])
            else:
                # random 선택
                idx = random.randint(start_num, len(body)-1)
                cls_itm = body[idx]
                self._gen_class(cls_itm)
                    
        # 수량자
        elif itm[0] in (sre_parse.MAX_REPEAT, sre_parse.MIN_REPEAT):
            # 패턴 => XXX_REPEAT, (min, max, p)                 
            min_repeat = itm[1][0] 
            max_repeat = itm[1][1]
            # set limit
            if itm[1][1] == sre_parse.MAXREPEAT:
                max_repeat = self._max_repeat  # 지정한 max값으로 한정
            
            # random 선택
            rand_repeat = random.randint(min_repeat, max_repeat)

            # random 만큼 반복
            for i in range(rand_repeat):
                self._read_body(itm[1][2])
        
        # Union
        elif itm[0] == sre_parse.BRANCH:
            body = itm[1][1]
            # random 선택
            idx = random.randint(0, len(body)-1)
            self._read_body(body[idx])

        else:            
            if isinstance(itm[1], tuple):
                for titm in itm[1]:
                    if isinstance(titm, sre_parse.SubPattern):
                        #print('CMD: %s in body '%(itm[0]))
                        self._read_body(titm)

    # [ (cmd), (cmd), ...] 이런 형태에 대해 cmd 마다 처리
    def _read_body(self, body):
        for itm in body:
            self._proc_cmd(itm)


    def generate(self, regex_pattern):

        p = sre_parse.parse( regex_pattern )
        #pprint.pprint(p)

        self._result = ''
        self._read_body( p )

        return self._result


pats = [
    r'01[069]-\d{3}\d?-\d{4}', # 전화번호
    r'안녕하세요 제 이름은 [김이박최황안][가-힣][가-힣] (입니다|일까요\?)', # 유니코드
    r'aa(bcd|\w{7}\d{3}|[a-z0-9]+)cc\W{2}\S{2}\D{2}', # union 수량자
    r'(void|char|int|double) [A-Za-z_]\w{8} \((int|char) [A-Za-z_]\w{3}(, (int|char) [A-Za-z_]\w{3}){1,2}\);', # function style
    r'[^ -~a-z]+', # ascii printable 범위 제외 문자클래스 => 그냥 유니코드 표정
]



g = RandomRegexStringGenerator()

for pat in pats:
    print('pattern=>', pat)
    for i in range(3):
        print( g.generate( pat ) )
    print('--------')

