# Copyright (c) 2019 The Regents of the University of California
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met: redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer;
# redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution;
# neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import textwrap

class TerminalFormatter:

    def __init__(self, max_width=80):
        # text_width holds the actual width we'll be wrapping to.
        # This takes into account the current terminal size.
        self.__text_width = min(max_width, self.__terminal_size()[0])

    def __terminal_size(self):
        import fcntl, termios, struct
        h, w, hp, wp = struct.unpack('HHHH',
            fcntl.ioctl(0, termios.TIOCGWINSZ,
            struct.pack('HHHH', 0, 0, 0, 0)))
        return w, h

    def __get_paragraphs(self, text, flatten=False):

        """
        This function takes a text and returns a list of constituent
        paragraphs, defining a paragraph as a block of text separated from
        other text by a blank line (or one containing only whitespace). If the
        "flatten" argument is set to true, all line breaks within paragraphs
        will be removed.

        E.g.:

        text = '''Hello, this is
        paragraph number 1.

        This is paragraph number 2.

        And this is
        paragraph number 3.
        '''

        __get_paragraphs(text, False)
        ["Hello, this is\nparagraph number 1", "This is paragraph number 2.",
            "And this is\npagraph number 3."]

        __get_paragraphs(text, True)
        ["Hello, this is paragraph number 1", "This is paragraph number 2.",
            "And this is pagraph number 3."]
        """

        paragraphs = []
        cur_paragraph = []

        for line in text.splitlines():
            stripped = line.strip()
            if not stripped: #I.e. a blank line.
                paragraphs.append(
                        {False: "\n", True: " "}[flatten].join(cur_paragraph))
                cur_paragraph = []
            else:
                cur_paragraph.append(stripped)

        paragraphs.append(
                {False: "\n", True: " "}[flatten].join(cur_paragraph))

        return paragraphs

    def format_output(self, text, label="", indent=0):
        """
        This function aids in the formatting of outputs. When obtaining
        the list of sim object we desire the output in the following
        format:

        desc: Address to mask loading binaries with, if 0,
              system auto-calculates the mask to be the most
              restrictive, otherwise it obeys a custom mask.

        We must take into account the width of the text, and wrap
        accordingly. We also must display the label.

        Keyword arguments:

        text --- The description text.
        label --- The label of the output (e.g. "desc: ").
        indent --- The white space width before each line.
        """

        if not text.strip():
            return ""

        # The text  may be over multiple lines (as when using triple
        # double quotes). First, we split the text into its constituent
        # paragraphs and remove new line characters from each.
        paragraphs = self.__get_paragraphs(text, True)

        # Wrap and Indent the paragraphs
        wrapper = textwrap.TextWrapper(width =
                max((self.__text_width - indent),1))
        # The first paragraph is special case due to the inclusion of the label
        formatted_paragraphs = [' ' * max((indent - len(label)),0) \
                + label + wrapper.wrap(paragraphs[0])[0]]
        for paragraph in paragraphs:
            for line in wrapper.wrap(paragraph[1:])[1:]:
                formatted_paragraphs.append(' ' * indent + line)
            formatted_paragraphs.append('\n')

        # Remove the last line break
        return '\n'.join(formatted_paragraphs[:-1])
