import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from io import BytesIO
from reportlab.platypus import PageBreak, SimpleDocTemplate, Image, Paragraph, Spacer, Table, TableStyle
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors

def test():
    return True

def sn_ratio(y, typ):
    y = np.array(y)
    if typ == 'smaller':  # Smaller-the-better
        return -10 * np.log10(np.mean(y**2))
    elif typ == 'larger':  # Larger-the-better
        return -10 * np.log10(np.mean(1 / y**2))
    elif typ == 'nominal':  # Nominal-the-best
        return 10 * np.log10((np.mean(y)**2) / np.var(y))
    else:
        raise ValueError("Type must be 'smaller', 'larger', or 'nominal'")


def report():
    ##def snr(data, char="larger"):
    ##    """
    ##    Calculate Signal-to-Noise ratio for Taguchi method
    ##
    ##    Parameters:
    ##    data : list or array
    ##        Experimental results (replications)
    ##    characteristic : str
    ##        'larger'   -> Larger-the-Better
    ##        'smaller'  -> Smaller-the-Better
    ##        'nominal'  -> Nominal-the-Best
    ##
    ##    Returns:
    ##    float
    ##        S/N ratio in dB
    ##    """
    ##
    ##    data = np.array(data, dtype=float)
    ##    n = len(data)
    ##
    ##    if char.lower() == "larger":
    ##        sn = -10 * np.log10(np.mean(1 / data**2))
    ##
    ##    elif char.lower() == "smaller":
    ##        sn = -10 * np.log10(np.mean(data**2))
    ##
    ##    elif char.lower() == "nominal":
    ##        mean = np.mean(data)
    ##        std = np.std(data, ddof=1)
    ##        sn = 10 * np.log10((mean**2) / (std**2))
    ##
    ##    else:
    ##        raise ValueError("Choose 'larger', 'smaller', or 'nominal'")
    ##
    ##    return sn

    ##results = [52, 55, 50]
    ##sn = snr(results, "larger")
    ##print("S/N Ratio (Larger-the-Better):", sn)
    ##
    ##results = [2.1, 2.3, 2.0]
    ##sn = snr(results, "smaller")
    ##print("S/N Ratio (Smaller-the-Better):", sn)
    ##
    ##results = [9.9, 10.1, 10.0]
    ##sn = snr(results, "nominal")
    ##print("S/N Ratio (Nominal-the-Best):", sn)


    ##experiments = [
    ##    [102,   5,      51,     11],
    ##    [102,   127,    35,	    87],
    ##    [29,    86,     107,    103],
    ##    [96,    40,	    111,    108],
    ##    [50,    90,	    77,	    123],
    ##    [82,    69,	    50,	    133],
    ##    [144,   138,    98,	    47]]
    ##
    ##sn_values = [snr(exp, "larger") for exp in experiments]
    ##
    ##print(sn_values)



    ##df = pd.read_excel("Results.xlsx")
    ##df = pd.read_excel("Results.xlsx", sheet_name="Sheet1")
    ##df = pd.read_excel("Results.xlsx", usecols=["++", "+-", "-+", "--"])
    ##df = pd.read_excel("Results.xlsx", usecols="A:D")
    df = pd.read_excel("Result.xlsx")
    colm = df.columns.tolist()
    resp = colm[-1]
    colm.pop()
    ##results = df.values.tolist()
    print('Column: ', colm)
    ##print('Results: \n',df)

    clm = ['A','B','C','D','E','F','G','H','I','J','K','L','M']

    f = df.shape[1]-1
    print('Number of Factors = ', f)
    ##print(df.iloc[:, f:])

    # Standard L9 OA
    L9_ = {
        "A": [1,1,1,2,2,2,3,3,3],
        "B": [1,2,3,1,2,3,1,2,3],
        "C": [1,2,3,2,3,1,3,1,2],
        'D': [1,2,3,3,1,2,2,3,1]
    }

    L27_ = {
        "A":  [1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3],
        "B":  [1,1,1,2,2,2,3,3,3, 1,1,1,2,2,2,3,3,3, 1,1,1,2,2,2,3,3,3],
        "C":  [1,1,1,2,2,2,3,3,3, 2,2,2,3,3,3,1,1,1, 3,3,3,1,1,1,2,2,2],
        "D":  [1,1,1,2,2,2,3,3,3, 3,3,3,1,1,1,2,2,2, 2,2,2,3,3,3,1,1,1],
        "E":  [1,2,3,1,2,3,1,2,3, 1,2,3,1,2,3,1,2,3, 1,2,3,1,2,3,1,2,3],
        "F":  [1,2,3,1,2,3,1,2,3, 2,3,1,2,3,1,2,3,1, 3,1,2,3,1,2,3,1,2],
        "G":  [1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3],
        "H":  [1,2,3,2,3,1,3,1,2, 2,3,1,3,1,2,1,2,3, 3,1,2,1,2,3,2,3,1],
        "I":  [1,2,3,3,1,2,2,3,1, 3,1,2,2,3,1,1,2,3, 2,3,1,1,2,3,3,1,2],
        "J":  [1,2,3,2,3,1,3,1,2, 1,2,3,2,3,1,3,1,2, 1,2,3,2,3,1,3,1,2],
        "K":  [1,2,3,3,1,2,2,3,1, 2,3,1,1,2,3,3,1,2, 3,1,2,2,3,1,1,2,3],
        "L":  [1,2,3,2,3,1,3,1,2, 3,1,2,1,2,3,2,3,1, 2,3,1,3,1,2,1,2,3],
        "M":  [1,2,3,3,1,2,2,3,1, 1,2,3,3,1,2,2,3,1, 1,2,3,3,1,2,2,3,1]
    }

    ##L27 = pd.DataFrame({
    ##    "A":  [1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3],
    ##    "B":  [1,1,1,2,2,2,3,3,3, 1,1,1,2,2,2,3,3,3, 1,1,1,2,2,2,3,3,3],
    ##    "C":  [1,2,3,1,2,3,1,2,3, 1,2,3,1,2,3,1,2,3, 1,2,3,1,2,3,1,2,3],
    ##    "D":  [1,1,1,2,2,2,3,3,3, 2,2,2,3,3,3,1,1,1, 3,3,3,1,1,1,2,2,2],
    ##    "E":  [1,2,3,2,3,1,3,1,2, 1,2,3,2,3,1,3,1,2, 1,2,3,2,3,1,3,1,2],
    ##    "F":  [1,2,3,3,1,2,2,3,1, 2,3,1,1,2,3,3,1,2, 3,1,2,2,3,1,1,2,3],
    ##    "G":  [1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3],
    ##    "H":  [1,2,3,2,3,1,3,1,2, 2,3,1,3,1,2,1,2,3, 3,1,2,1,2,3,2,3,1],
    ##    "I":  [1,2,3,3,1,2,2,3,1, 3,1,2,2,3,1,1,2,3, 2,3,1,1,2,3,3,1,2],
    ##    "J":  [1,1,1,2,2,2,3,3,3, 3,3,3,1,1,1,2,2,2, 2,2,2,3,3,3,1,1,1],
    ##    "K":  [1,2,3,1,2,3,1,2,3, 2,3,1,2,3,1,2,3,1, 3,1,2,3,1,2,3,1,2],
    ##    "L":  [1,2,3,2,3,1,3,1,2, 3,1,2,1,2,3,2,3,1, 2,3,1,3,1,2,1,2,3],
    ##    "M":  [1,2,3,3,1,2,2,3,1, 1,2,3,3,1,2,2,3,1, 1,2,3,3,1,2,2,3,1]
    ##})
    L9 = {}
    L27 = {}

    if f > 4:
        for i in range(len(colm)):
            L27[colm[i]] = L27_[clm[i]]
        L27 = pd.DataFrame(L27)
        r = 27
        L27 = L27.iloc[:, :f]
        L27[resp] = df.iloc[:, f:]
        print(L27,"\n",r)
    else:
        for i in range(len(colm)):
            L9[colm[i]] = L9_[clm[i]]
        L9 = pd.DataFrame(L9)
        r = 9
        L9 = L9.iloc[:, :f]
        L9[resp] = df.iloc[:, f:]
        print(L9,"\n",r)

    ##results = [
    ##    [52, 55, 50],
    ##    [48, 47, 49],
    ##    [60, 58, 59],
    ##    [54, 53, 55],
    ##    [46, 45, 47],
    ##    [62, 61, 60],
    ##    [56, 57, 55],
    ##    [49, 50, 48],
    ##    [63, 64, 62]
    ##]

    if f < 5:
    ##    results = results[:9]
    ##    sn_values = [snr(r, "larger") for r in results]
        sn_values = L9[resp].apply(lambda x: sn_ratio(x, 'larger'))
        # Compute S/N for Each Experiment
        L9["SNR"] = sn_values
        L9["SNR"] = L9["SNR"].round(4)
        ##L9["A"] = L9["A"].astype(int)
        ##L9["B"] = L9["B"].astype(int)
        ##L9["C"] = L9["C"].astype(int)
        print(L9)

    else:
    ##    sn_values = [snr(r, "larger") for r in results]
        
        sn_values = L27[resp].apply(lambda x: sn_ratio(x, 'larger'))
    ##    sn_values = L27[resp].apply(lambda x: sn_ratio(x, 'smaller'))
        
        # Compute S/N for Each Experiment
        L27["SNR"] = sn_values
        L27["SNR"] = L27["SNR"].round(4)
        ##L9["A"] = L9["A"].astype(int)
        ##L9["B"] = L9["B"].astype(int)
        ##L9["C"] = L9["C"].astype(int)
        print(L27)


    # Main Effects (ANOM Table)
    main_effects = {}

    if f < 5:
        for factor in colm:
            print(factor)
            main_effects[factor] = L9.groupby(factor)["SNR"].mean()
    else:
        for factor in colm:
            print(factor)
            main_effects[factor] = L27.groupby(factor)["SNR"].mean()
    ##else:
    ##    colm = colm.pop()
    ##    for factor in colm:
    ##        main_effects[factor] = L9.groupby(factor)["SNR"].mean()
    ##print(main_effects['A'])
    print(main_effects)
    main_effects_df = pd.DataFrame(main_effects)
    main_effects_df = main_effects_df.round(4)
    ##main_effects_df.iloc[0, 0] = " "
    print("Main_Effects \n",main_effects_df)


    # Main Effects Plot
    plt.figure()
    for i in colm:
        plt.plot(main_effects_df.index, main_effects_df[i], marker='o', label=f"{i}")
    ##plt.plot(main_effects_df.index, main_effects_df[i], marker='o', label="Factor B")
    ##plt.plot(main_effects_df.index, main_effects_df["C"], marker='o', label="Factor C")
    ##    if f == 4:
    ##        plt.plot(main_effects_df.index, main_effects_df["D"], marker='o', label="Factor C")

    ##plt.plot(main_effects_df.index, main_effects_df["A"], marker='o', label="Factor A")
    ##plt.plot(main_effects_df.index, main_effects_df["B"], marker='o', label="Factor B")
    ##plt.plot(main_effects_df.index, main_effects_df["C"], marker='o', label="Factor C")
    ##if f == 4:
    ##    plt.plot(main_effects_df.index, main_effects_df["D"], marker='o', label="Factor C")
    plt.xlabel("Level")
    plt.ylabel("Mean S/N Ratio (dB)")
    plt.title("Main Effects Plot for S/N Ratio")
    plt.legend()
    plt.grid(True)
    ##plt.show()
    ##plt.savefig("plot.png", dpi=300, bbox_inches="tight")
    ##plt.close()



    img_buffer = BytesIO()
    plt.savefig(img_buffer, format="png", bbox_inches="tight", dpi=300)
    plt.close()
    img_buffer.seek(0)


    # Optimal Parameter Levels
    optimal_levels = {
        factor: main_effects_df[factor].idxmax()
        for factor in main_effects_df.columns
    }

    print("Optimal Levels:", optimal_levels)


    # ANOVA for Taguchi (% Contribution)
    if f < 5:
        overall_mean = L9["SNR"].mean()
    else:
        overall_mean = L27["SNR"].mean()

    anova = []

    if f < 5:
        total_ss = sum((L9["SNR"] - overall_mean) ** 2)

        for factor in colm:
            ss = sum(
                L9.groupby(factor)["SNR"].count()
                * (L9.groupby(factor)["SNR"].mean() - overall_mean) ** 2
            )
            anova.append([factor, ss])
    else:
        total_ss = sum((L27["SNR"] - overall_mean) ** 2)

        for factor in colm:
            ss = sum(
                L27.groupby(factor)["SNR"].count()
                * (L27.groupby(factor)["SNR"].mean() - overall_mean) ** 2
            )
            anova.append([factor, ss])

    anova_df = pd.DataFrame(anova, columns=["Factor", "SS"])
    anova_df["% Contribution"] = 100 * anova_df["SS"] / total_ss
    anova_df = anova_df.round(4)
    print(anova_df)


    # Confirmation Experiment Prediction
    predicted_sn = overall_mean
    for factor, level in optimal_levels.items():
        predicted_sn += main_effects_df.loc[level, factor] - overall_mean

    print("Predicted S/N Ratio:", predicted_sn)


    ### Multi-Response Taguchi (Grey Relational Analysis)
    ### Step 1: Responses (example: Strength ↑, Roughness ↓)
    ##responses = pd.DataFrame({
    ##    "Strength": [np.mean(r) for r in results],
    ##    "Roughness": [2.1, 2.3, 2.0, 2.2, 2.4, 1.9, 2.1, 2.3, 1.8]
    ##})
    ##
    ##
    ### Step 2: Normalization
    ##norm = pd.DataFrame()
    ##norm["Strength"] = (responses["Strength"] - responses["Strength"].min()) / \
    ##                   (responses["Strength"].max() - responses["Strength"].min())
    ##
    ##norm["Roughness"] = (responses["Roughness"].max() - responses["Roughness"]) / \
    ##                    (responses["Roughness"].max() - responses["Roughness"].min())
    ##
    ##
    ### Step 3: Grey Relational Coefficient & Grade
    ##delta = abs(norm - 1)
    ##xi = 0.5
    ##
    ##grc = (delta.min() + xi * delta.max()) / (delta + xi * delta.max())
    ##responses["GRG"] = grc.mean(axis=1)
    ##
    ##if f < 5:
    ##    L9["GRG"] = responses["GRG"]
    ##    L9["GRG"] = L9["GRG"].round(4)
    ##    ##L9 = L9.iloc[:, 1:]
    ##    print(L9)
    ##
    ##    # Optimal Levels Using GRG
    ##    grg_effects = L9.groupby("A")["GRG"].mean().to_frame("A")
    ##    grg_effects["B"] = L9.groupby("B")["GRG"].mean()
    ##    grg_effects["C"] = L9.groupby("C")["GRG"].mean()
    ##else:
    ##    L27["GRG"] = responses["GRG"]
    ##    L27["GRG"] = L27["GRG"].round(4)
    ##    ##L9 = L9.iloc[:, 1:]
    ##    print(L27)
    ##
    ##    # Optimal Levels Using GRG
    ##    grg_effects = L27.groupby("A")["GRG"].mean().to_frame("A")
    ##    grg_effects["B"] = L27.groupby("B")["GRG"].mean()
    ##    grg_effects["C"] = L27.groupby("C")["GRG"].mean()
    ##
    ##optimal_grg = {col: grg_effects[col].idxmax() for col in grg_effects.columns}
    ##print("Optimal Levels (Multi-response):", optimal_grg)


    # Export Results to Excel (Paper-Ready)
    if f < 5:
        with pd.ExcelWriter("Taguchi_Complete_Analysis.xlsx") as writer:
            L9.to_excel(writer, sheet_name="L9_with_SN", index=False)
            main_effects_df.to_excel(writer, sheet_name="Main_Effects")
            anova_df.to_excel(writer, sheet_name="ANOVA")
    ##        responses.to_excel(writer, sheet_name="Grey_Relational")
    else:
        with pd.ExcelWriter("Taguchi_Complete_Analysis.xlsx") as writer:
            L27.to_excel(writer, sheet_name="L27_with_SN", index=False)
            main_effects_df.to_excel(writer, sheet_name="Main_Effects")
            anova_df.to_excel(writer, sheet_name="ANOVA")
    ##        responses.to_excel(writer, sheet_name="Grey_Relational")





    def get_col_widths_pdf(df, page_width=A4[0], left_margin=80, right_margin=80, top_margin=50, bottom_margin=50):
        """
        df : pandas DataFrame
        page_width : PDF page width in points (default A4)
        left_margin, right_margin : margins in points
        """
        # Available width
        avail_width = page_width - left_margin - right_margin
        n_cols = len(df.columns)
        print(page_width,avail_width,n_cols)
        
        # Estimate relative widths based on max string length
    ##    ll = max(df[col].astype(str).map(len).max(), len(str(col)))
        max_lens = [max(df[col].astype(str).map(len).max(), len(str(col)))+2
                    if max(df[col].astype(str).map(len).max(), len(str(col))) == 1
                    else max(df[col].astype(str).map(len).max(), len(str(col)))
                    for col in df.columns]
        total_len = sum(max_lens)
        print(max_lens, total_len)    
        
        # Column widths proportional to max length, scaled to available width
    ##    col_widths = [avail_width * (l / total_len) for l in max_lens]
        col_widths = [avail_width * ((l+1) / total_len) if l == 1 else avail_width * (l / total_len) for l in max_lens]

        print(col_widths)    
        return col_widths

    ##col_widths = get_col_widths_pdf(L9)

    def df_to_table(df):
        data = [df.columns.tolist()] + df.reset_index(drop=True).astype(object).values.tolist()
        wrapped_data = [[Paragraph(str(cell), wrap_style) for cell in row]
                        for row in data]
        print(wrapped_data)

        n_cols = len(df.columns)

        page_width, _ = A4
        left_margin = right_margin = 50

        usable_width = page_width - left_margin - right_margin
        col_width = usable_width / n_cols

        table = Table(wrapped_data, colWidths=[col_width] * n_cols)
        
    ##    table = Table(wrapped_data,colWidths=get_col_widths_pdf(df))
        table.setStyle(TableStyle([
            ('FONTNAME', (0, 0), (-1, -1), 'Helvetica'),
            ('FONTSIZE', (0, 0), (-1, -1), 12.5),
            ('BACKGROUND', (0,0), (-1,0), colors.lavender),
    ##        ('LINEBEFORE', (0,0), (0,-1), 1, colors.black),
    ##        ('LINEAFTER', (-1,0), (-1,-1), 1, colors.black),
            ('LINEBEFORE', (1,0), (-1,-1), 1, colors.black),
    ####        ('GRID', (0,0), (-1,-1), 0.7, colors.lightgrey),
    ####        ('INNERGRID', (0, 0), (-1, -1), 0.75, colors.lightgrey),
            ('LINEBELOW', (0, 0), (-1, -1), 0.7, colors.lightgrey),
            ('LINEBELOW', (0, 0), (-1, 0), 1.2, colors.black),
    ####        ('LINERIGHT', (0, 0), (-1, -1), 1.2, colors.black),
            ('BOX', (0, 0), (-1, -1), 1.2, colors.black),
            ('ALIGN', (0,0), (-1,-1), 'CENTER'),
            ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
        ]))
        return table

    def df_to_table2(df):
        
    ##    data = []
    ##
    ##    for i, row in enumerate(df.values):
    ##        if i == 0:  # First row shift
    ##            data.append([Paragraph(str(row[0]), styles['Normal']),
    ##                           Paragraph(str(row[1]), styles['Normal'])])
    ##        else:
    ##            data.append(list(row))
        df.insert(0, 'Levels', [1, 2, 3])
        data = [df.columns.tolist()]+ df.astype(object).values.tolist()
        wrapped_data = [[Paragraph(str(cell), wrap_style) for cell in row]
                        for row in data]
        print(wrapped_data)

        n_cols = len(df.columns)

        page_width, _ = A4
        left_margin = right_margin = 50

        usable_width = page_width - left_margin - right_margin
        col_width = usable_width / n_cols

        table = Table(wrapped_data, colWidths=[col_width] * n_cols)
    ##    table = Table(wrapped_data,colWidths=get_col_widths_pdf(df))
    ##    table = Table(wrapped_data,colWidths=col_widths)
        table.setStyle(TableStyle([
            ('FONTNAME', (0, 0), (-1, -1), 'Helvetica'),
            ('FONTSIZE', (0, 0), (-1, -1), 12.5),
            ('BACKGROUND', (0,0), (-1,0), colors.lavender),
    ##        ('LINEBEFORE', (0,0), (0,-1), 1, colors.black),
    ##        ('LINEAFTER', (-1,0), (-1,-1), 1, colors.black),
            ('LINEBEFORE', (1,0), (-1,-1), 1, colors.black),
    ####        ('GRID', (0,0), (-1,-1), 0.7, colors.lightgrey),
    ####        ('INNERGRID', (0, 0), (-1, -1), 0.75, colors.lightgrey),
            ('LINEBELOW', (0, 0), (-1, -1), 0.7, colors.lightgrey),
            ('LINEBELOW', (0, 0), (-1, 0), 1.2, colors.black),
    ####        ('LINERIGHT', (0, 0), (-1, -1), 1.2, colors.black),
            ('BOX', (0, 0), (-1, -1), 1.2, colors.black),
            ('ALIGN', (0,0), (-1,-1), 'CENTER'),
            ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
            ("NOSPLIT", (0,0), (-1,-1)),
        ]))
        return table

    pdf = SimpleDocTemplate(
        "Taguchi_Analysis_Report.pdf",
        pagesize=A4,
        rightMargin=50,
        leftMargin=50,
        topMargin=36,
        bottomMargin=36
    )

    style = ParagraphStyle(
        name="MyStyle",
        fontName="Helvetica",
        fontSize=14,      # 👈 Font size here
        leading=14        # 👈 Line spacing (usually fontSize + 2)
    )

    styles = getSampleStyleSheet()
    wrap_style = ParagraphStyle(
        name="wrap",
        parent=styles["Normal"],
        fontSize=12.5,
        alignment=1,        # 0=left, 1=center, 2=right
        wordWrap='LTR',       # ✅ word-based wrapping
        splitLongWords=False
    )

    story = []
    if f < 5:
        data1 = [
            ["Taguchi Array", f": L9 3^{f}"],
            ["Number of Factors", f": {f}"],
            ["Number of Runs", f": {r}"]
        ]
    else:
        data1 = [
            ["Taguchi Array", f": L27 3^{f}"],
            ["Number of Factors", f": {f}"],
            ["Number of Runs", f": {r}"]
        ]

    table1 = Table(data1, colWidths=[130, 50],hAlign='LEFT')
    table1.setStyle(TableStyle([
    ##    ('GRID', (0,0), (-1,-1), 1, colors.black),
    ##    ('BACKGROUND', (0,0), (-1,0), colors.lightgrey),
        ('FONTSIZE', (0,0), (-1,-1), 12.5),
        ('ALIGN', (0,0), (-1,-1), 'LEFT'),
        ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ]))
    red_style = ParagraphStyle(
        'RedText',
        parent=styles['Title'],
        textColor=colors.purple
    )

    green_style = ParagraphStyle(
        'GreenText',
        parent=styles['Title'],
        textColor=colors.deeppink,
        fontSize = 15
    )

    style_2 = ParagraphStyle(
        'BlueText',
        parent=styles['Title'],
        textColor=colors.green,
        fontSize = 15
    )

    # Title
    story.append(Paragraph("<b>Taguchi Method Analysis Report</b>", red_style))
    story.append(Spacer(1, 6))

    # L9 Table
    story.append(table1)
    ##story.append(Paragraph(f"Taguchi Array &nbsp;: L9 3^{f}", styles["Normal"]))
    ##story.append(Paragraph(f"Number of Factors &nbsp;: {f}", styles["Normal"]))
    ##story.append(Paragraph(f"Number of Runs &nbsp;: {r}", styles["Normal"]))
    story.append(Spacer(1, 1))

    # L9 Table
    if f < 5:
        story.append(Paragraph("<b>L9 Orthogonal Array with S/N Ratio</b>", styles["Heading2"]))
        story.append(df_to_table(L9))
        story.append(Spacer(1, 6))
    else:
        story.append(Paragraph("<b>L27 Orthogonal Array with S/N Ratio</b>", styles["Heading2"]))
        story.append(df_to_table(L27))
        story.append(Spacer(1, 6))

    if f > 4:
        story.append(PageBreak())

    # Main Effects
    story.append(Paragraph("<b>Main Effects (ANOM)</b>", styles["Heading2"]))
    story.append(df_to_table2(main_effects_df))
    story.append(Spacer(1, 16))

    if f < 5:
        story.append(PageBreak())
        
    img = Image(img_buffer, width=440, height=320)
    story.append(img)
    story.append(Spacer(1, 12))



    # ANOVA
    story.append(Paragraph("<b>ANOVA Results</b>", styles["Heading2"]))
    story.append(df_to_table(anova_df))
    story.append(Spacer(1, 22))

    # Prediction
    story.append(Paragraph(
    ##    f"<b>Predicted Optimal S/N Ratio:</b>",# {predicted_sn:.3f}",
    ##    style,
    ##    Paragraph(f"{predicted_sn:.4f}",style_2)
        '<font color="black"  ><b>Predicted Optimal S/N Ratio:</b></font> '
        f'<font color="blue" size="15"><b> {predicted_sn:.4f} </b></font>',style
    ))
    story.append(Spacer(1, 22))

    # Optimal Results
    story.append(Paragraph(
        "<b>Optimal Levels :</b>",
        style
    ))
    story.append(Spacer(1, 12))

    story.append(Paragraph(
        f"<b>{optimal_levels}</b>",
        green_style
    ))

    pdf.build(story)


    ##from matplotlib.backends.backend_pdf import PdfPages
    ##import matplotlib.pyplot as plt

    ##with PdfPages("Taguchi_Results_Quick.pdf") as pdf:
    ##
    ##    # Table page
    ##    fig, ax = plt.subplots()
    ##    ax.axis('tight')
    ##    ax.axis('off')
    ##
    ##    table = ax.table(
    ##        cellText=L9.values,
    ##        colLabels=L9.columns,
    ##        loc='center'
    ##    )
    ##    table.scale(1, 1.5)
    ##
    ##    pdf.savefig(fig)
    ##    plt.close()
    ##
    ##    # Main effects plot page
    ##    plt.figure()
    ##    for col in main_effects_df.columns:
    ##        plt.plot(main_effects_df.index, main_effects_df[col], marker='o', label=col)
    ##
    ##    plt.xlabel("Level")
    ##    plt.ylabel("Mean S/N Ratio (dB)")
    ##    plt.title("Main Effects Plot")
    ##    plt.legend()
    ##    plt.grid(True)
    ##
    ##    pdf.savefig()
    ##    plt.close()





    ##import pandas as pd
    ##import numpy as np
    ##
    ##L27 = pd.DataFrame({
    ##    "A": [1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3],
    ##    "B": [1,1,1,2,2,2,3,3,3, 1,1,1,2,2,2,3,3,3, 1,1,1,2,2,2,3,3,3],
    ##    "C": [1,2,3,1,2,3,1,2,3, 1,2,3,1,2,3,1,2,3, 1,2,3,1,2,3,1,2,3],
    ##    "D": [1,1,1,2,2,2,3,3,3, 2,2,2,3,3,3,1,1,1, 3,3,3,1,1,1,2,2,2],
    ##    "E": [1,2,3,2,3,1,3,1,2, 1,2,3,2,3,1,3,1,2, 1,2,3,2,3,1,3,1,2]
    ##})
    ##
    ##L27["Response"] = [
    ##    45.2, 46.1, 46.9, 47.3, 48.1, 48.8, 49.4, 49.9, 50.5,
    ##    47.1, 47.9, 48.6, 49.2, 50.0, 50.8, 51.3, 51.9, 52.6,
    ##    48.4, 49.0, 49.8, 50.4, 51.1, 51.9, 52.6, 53.3, 54.0
    ##]
    ##
    ##def sn_larger_better(y):
    ##    y = np.array(y)
    ##    return -10 * np.log10(np.mean(1 / y**2))
    ##
    ##L27["SNR"] = L27["Response"].apply(lambda x: sn_larger_better([x]))
    ##
    ##
    ##factors = ["A", "B", "C", "D", "E"]
    ##
    ##response_tables = {}
    ##
    ##for f in factors:
    ##    response_tables[f] = L27.groupby(f)["SNR"].mean()
    ##    print(f"\nFactor {f} Response Table:")
    ##    print(response_tables[f])
    ##
    ##
    ##optimal_levels = {}
    ##
    ##for f in factors:
    ##    optimal_levels[f] = response_tables[f].idxmax()
    ##
    ##print("\nOptimal Factor Levels:")
    ##print(optimal_levels)
    ##
    ##
    ##import matplotlib.pyplot as plt
    ##
    ##for f in factors:
    ##    plt.figure()
    ##    response_tables[f].plot(marker='o')
    ##    plt.xlabel(f"Factor {f}")
    ##    plt.ylabel("Mean S/N Ratio")
    ##    plt.title(f"Main Effect Plot for Factor {f}")
    ##    plt.grid(True)
    ##    plt.show()
    ##
    ##predicted_sn = sum(response_tables[f][optimal_levels[f]] for f in factors) \
    ##               - (len(factors)-1) * L27["SNR"].mean()
    ##
    ##print("Predicted S/N at optimal condition:", predicted_sn)
    ##
    ##print(L27.groupby("E")["SNR"].mean())
    ##
