#!/usr/bin/env python
# coding=utf-8
"""Examples of using the cmd2 table creation API"""

import functools
import sys
from typing import (
    Any,
    List,
)

from cmd2 import (
    EightBitBg,
    EightBitFg,
    Fg,
    ansi,
)
from cmd2.table_creator import (
    AlternatingTable,
    BorderedTable,
    Column,
    HorizontalAlignment,
    SimpleTable,
)

# Text styles used in the tables
bold_yellow = functools.partial(ansi.style, fg=Fg.LIGHT_YELLOW, bold=True)
blue = functools.partial(ansi.style, fg=Fg.LIGHT_BLUE)
green = functools.partial(ansi.style, fg=Fg.GREEN)


class DollarFormatter:
    """Example class to show that any object type can be passed as data to TableCreator and converted to a string"""

    def __init__(self, val: float) -> None:
        self.val = val

    def __str__(self) -> str:
        """Returns the value in dollar currency form (e.g. $100.22)"""
        return "${:,.2f}".format(self.val)


class Relative:
    """Class used for example data"""

    def __init__(self, name: str, relationship: str) -> None:
        self.name = name
        self.relationship = relationship


class Book:
    """Class used for example data"""

    def __init__(self, title: str, year_published: str) -> None:
        self.title = title
        self.year_published = year_published


class Author:
    """Class used for example data"""

    def __init__(self, name: str, birthday: str, place_of_birth: str) -> None:
        self.name = name
        self.birthday = birthday
        self.place_of_birth = place_of_birth
        self.books: List[Book] = []
        self.relatives: List[Relative] = []


def ansi_print(text):
    """Wraps style_aware_write so style can be stripped if needed"""
    ansi.style_aware_write(sys.stdout, text + '\n\n')


def basic_tables():
    """Demonstrates basic examples of the table classes"""

    # Table data which demonstrates handling of wrapping and text styles
    data_list: List[List[Any]] = list()
    data_list.append(["Billy Smith", "123 Sesame St.\nFake Town, USA 33445", DollarFormatter(100333.03)])
    data_list.append(
        [
            "William Longfellow Marmaduke III",
            "984 Really Long Street Name Which Will Wrap Nicely\nApt 22G\nPensacola, FL 32501",
            DollarFormatter(55135.22),
        ]
    )
    data_list.append(
        [
            "James " + blue("Bluestone"),
            bold_yellow("This address has line feeds,\ntext styles, and wrapping. ")
            + blue("Style is preserved across lines."),
            DollarFormatter(300876.10),
        ]
    )
    data_list.append(["John Jones", "9235 Highway 32\n" + green("Greenville") + ", SC 29604", DollarFormatter(82987.71)])

    # Table Columns (width does not account for any borders or padding which may be added)
    columns: List[Column] = list()
    columns.append(Column("Name", width=20))
    columns.append(Column("Address", width=38))
    columns.append(
        Column("Income", width=14, header_horiz_align=HorizontalAlignment.RIGHT, data_horiz_align=HorizontalAlignment.RIGHT)
    )

    st = SimpleTable(columns)
    table = st.generate_table(data_list)
    ansi_print(table)

    bt = BorderedTable(columns)
    table = bt.generate_table(data_list)
    ansi_print(table)

    at = AlternatingTable(columns)
    table = at.generate_table(data_list)
    ansi_print(table)


def nested_tables():
    """
    Demonstrates how to nest tables with styles which conflict with the parent table by setting style_data_text to False.
    It also demonstrates coloring various aspects of tables.
    """

    # Create data for this example
    author_data: List[Author] = []
    author_1 = Author("Frank Herbert", "10/08/1920", "Tacoma, Washington")
    author_1.books.append(Book("Dune", "1965"))
    author_1.books.append(Book("Dune Messiah", "1969"))
    author_1.books.append(Book("Children of Dune", "1976"))
    author_1.books.append(Book("God Emperor of Dune", "1981"))
    author_1.books.append(Book("Heretics of Dune", "1984"))
    author_1.books.append(Book("Chapterhouse: Dune", "1985"))
    author_1.relatives.append(Relative("Flora Lillian Parkinson", "First Wife"))
    author_1.relatives.append(Relative("Beverly Ann Stuart", "Second Wife"))
    author_1.relatives.append(Relative("Theresa Diane Shackelford", "Third Wife"))
    author_1.relatives.append(Relative("Penelope Herbert", "Daughter"))
    author_1.relatives.append(Relative("Brian Patrick Herbert", "Son"))
    author_1.relatives.append(Relative("Bruce Calvin Herbert", "Son"))

    author_2 = Author("Jane Austen", "12/16/1775", "Steventon, Hampshire, England")
    author_2.books.append(Book("Sense and Sensibility", "1811"))
    author_2.books.append(Book("Pride and Prejudice", "1813"))
    author_2.books.append(Book("Mansfield Park ", "1814"))
    author_2.books.append(Book("Emma", "1815"))
    author_2.books.append(Book("Northanger Abbey", "1818"))
    author_2.books.append(Book("Persuasion", "1818"))
    author_2.books.append(Book("Lady Susan", "1871"))
    author_2.relatives.append(Relative("James Austen", "Brother"))
    author_2.relatives.append(Relative("George Austen", "Brother"))
    author_2.relatives.append(Relative("Edward Austen", "Brother"))
    author_2.relatives.append(Relative("Henry Thomas Austen", "Brother"))
    author_2.relatives.append(Relative("Cassandra Elizabeth Austen", "Sister"))
    author_2.relatives.append(Relative("Francis William Austen", "Brother"))
    author_2.relatives.append(Relative("Charles John Austen", "Brother"))

    author_data.append(author_1)
    author_data.append(author_2)

    # Define table which presents Author data fields vertically with no header.
    # This will be nested in the parent table's first column.
    author_columns: List[Column] = list()
    author_columns.append(Column("", width=14))
    author_columns.append(Column("", width=20))

    # The text labels in this table will be bold text. They will also be aligned by the table code.
    # When styled text is aligned, a TextStyle.RESET_ALL sequence is inserted between the aligned text
    # and the fill characters. Therefore, the Author table will contain TextStyle.RESET_ALL sequences,
    # which would interfere with the background color applied by the parent table. To account for this,
    # we will manually color the Author tables to match the background colors of the parent AlternatingTable's
    # rows and set style_data_text to False in the Author column.
    odd_author_tbl = SimpleTable(author_columns, data_bg=EightBitBg.GRAY_0)
    even_author_tbl = SimpleTable(author_columns, data_bg=EightBitBg.GRAY_15)

    # Define AlternatingTable for books checked out by people in the first table.
    # This will be nested in the parent table's second column.
    books_columns: List[Column] = list()
    books_columns.append(Column(ansi.style("Title", bold=True), width=25))
    books_columns.append(
        Column(
            ansi.style("Published", bold=True),
            width=9,
            header_horiz_align=HorizontalAlignment.RIGHT,
            data_horiz_align=HorizontalAlignment.RIGHT,
        )
    )

    books_tbl = AlternatingTable(
        books_columns,
        column_borders=False,
        border_fg=EightBitFg.GRAY_15,
        header_bg=EightBitBg.GRAY_0,
        odd_bg=EightBitBg.GRAY_0,
        even_bg=EightBitBg.GRAY_15,
    )

    # Define BorderedTable for relatives of the author
    # This will be nested in the parent table's third column.
    relative_columns: List[Column] = list()
    relative_columns.append(Column(ansi.style("Name", bold=True), width=25))
    relative_columns.append(Column(ansi.style("Relationship", bold=True), width=12))

    # Since the header labels are bold, we have the same issue as the Author table. Therefore, we will manually
    # color Relatives tables to match the background colors of the parent AlternatingTable's rows and set style_data_text
    # to False in the Relatives column.
    odd_relatives_tbl = BorderedTable(
        relative_columns,
        border_fg=EightBitFg.GRAY_15,
        border_bg=EightBitBg.GRAY_0,
        header_bg=EightBitBg.GRAY_0,
        data_bg=EightBitBg.GRAY_0,
    )

    even_relatives_tbl = BorderedTable(
        relative_columns,
        border_fg=EightBitFg.GRAY_0,
        border_bg=EightBitBg.GRAY_15,
        header_bg=EightBitBg.GRAY_15,
        data_bg=EightBitBg.GRAY_15,
    )

    # Define parent AlternatingTable which contains Author and Book tables
    parent_tbl_columns: List[Column] = list()

    # All of the nested tables already have background colors. Set style_data_text
    # to False so the parent AlternatingTable does not apply background color to them.
    parent_tbl_columns.append(
        Column(ansi.style("Author", bold=True), width=odd_author_tbl.total_width(), style_data_text=False)
    )
    parent_tbl_columns.append(Column(ansi.style("Books", bold=True), width=books_tbl.total_width(), style_data_text=False))
    parent_tbl_columns.append(
        Column(ansi.style("Relatives", bold=True), width=odd_relatives_tbl.total_width(), style_data_text=False)
    )

    parent_tbl = AlternatingTable(
        parent_tbl_columns,
        column_borders=False,
        border_fg=EightBitFg.GRAY_93,
        header_bg=EightBitBg.GRAY_0,
        odd_bg=EightBitBg.GRAY_0,
        even_bg=EightBitBg.GRAY_15,
    )

    # Construct the tables
    parent_table_data: List[List[Any]] = []
    for row, author in enumerate(author_data, start=1):
        # First build the author table and color it based on row number
        author_tbl = even_author_tbl if row % 2 == 0 else odd_author_tbl

        # This table has three rows and two columns
        table_data = [
            [ansi.style("Name", bold=True), author.name],
            [ansi.style("Birthday", bold=True), author.birthday],
            [ansi.style("Place of Birth", bold=True), author.place_of_birth],
        ]

        # Build the author table string
        author_tbl_str = author_tbl.generate_table(table_data, include_header=False, row_spacing=0)

        # Now build this author's book table
        table_data = [[book.title, book.year_published] for book in author.books]
        book_tbl_str = books_tbl.generate_table(table_data)

        # Lastly build the relatives table and color it based on row number
        relatives_tbl = even_relatives_tbl if row % 2 == 0 else odd_relatives_tbl
        table_data = [[relative.name, relative.relationship] for relative in author.relatives]
        relatives_tbl_str = relatives_tbl.generate_table(table_data)

        # Add these tables to the parent table's data
        parent_table_data.append(['\n' + author_tbl_str, '\n' + book_tbl_str + '\n\n', '\n' + relatives_tbl_str + '\n\n'])

    # Build the parent table
    top_table_str = parent_tbl.generate_table(parent_table_data)
    ansi_print(top_table_str)


if __name__ == '__main__':
    # Default to terminal mode so redirecting to a file won't include the ANSI style sequences
    ansi.allow_style = ansi.AllowStyle.TERMINAL
    basic_tables()
    nested_tables()
