import pathlib

class render:
    '''
    This class is responsible for rendering files, such as HTML files to
    result. It could replace given formats with given values.
    '''

    def __init__(self, file: pathlib.Path):
        '''
        This is default render builder. It require handle to file which 
        would be rendered.

        Parameters:
            file (Path): This is handle to file

        Returns:
            (render): New render object
        '''

        self.__list = dict()
        self.__cache = None

        self.__start_tag = ">>"
        self.__stop_tag = "<<"

        try:
            with file.open() as handle:
                self.__content = handle.read()
        
        except:
            Exception("Can not open file to render.")

    def add(self, name: str, content: str) -> None:
        '''
        This add new replace to the render. That mean all places, where 
        special tags contains name, would be replaced with content.

        Parameters:
            name (str): Name of the parameter
            content (str): Content to replace when render

        Returns:
            (None)
        '''

        if name in self.__list:
            raise Exception("Parameter " + name + " had been already set.")

        self.__list[name] = content
        self.__clean_cache()

    def __clean_cache(self):
        ''' This clean render cache. '''

        self.__cache = None

    def validate(self, name: str) -> bool:
        '''
        This check that given name is valid, that mean not contain any 
        blocked chars, and white chars.

        Parameters:
            name (str): Name to validate

        Returns:
            (bool): True if valid, or False when not
        '''

        blocked = ["'", "\\", "\""]

        for char in blocked:
            if char in name:
                return False

        for letter in name:
            if letter.isspace():
                return False

        return True

    @property
    def start_tag(self):
        ''' This return tag which start replace pattern. '''

        return self.__start_tag

    @property
    def stop_tag(self):
        ''' This return stop tag which end pattern. '''

        return self.__stop_tag

    @start_tag.setter
    def start_tag(self, tag: str) -> None:
        ''' This set new start tag. '''

        if not self.validate(tag):
            raise Exception("Start tag contain bad chars.")

        self.__start_tag = tag

    @stop_tag.setter
    def stop_tag(self, tag: str) -> None:
        ''' This set new stop tag. '''

        if not self.validate(tag):
            raise Exception("Stop tag can not contain blocked chars.")

        self.__stop_tag = tag

    def get(self, name: str) -> str:
        ''' 
        This retunr content set to given parameter.

        Parameters:
            name (str): Name of the parameter

        Returns:
            (str): Content of the parameter or blank string
        '''

        if name in self.__list:
            return self.__list[name]

        return str()

    def finalize(self) -> str:
        '''
        This function render given documents, using parameters set before, 
        and return finalized.       

        Returns:
            (str): Finalized rendered document 
        '''

        if self.__cache is not None:
            return self.__cache

        start_length = len(self.start_tag)
        stop_length = len(self.stop_tag)
        
        result = self.__content
        last = 0

        while True:
            start = result.find(self.start_tag, last)
            stop = result.find(self.stop_tag, last)

            if start == -1:
                break

            if stop == -1:
                break

            name = result[start + start_length + 1 : stop]
            name = name.strip()

            last = start
            content = self.get(name)

            result \
            = result[: start] \
            + content \
            + result[stop + stop_length :]

        self.__cache = result

        return self.__cache

    def __str__(self) -> str:
        ''' This is string converter, and alias of finalize. '''

        return self.finalize()