<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Real Estate Investor Waterfall Calculator</title>
</head>
<body class="bg-gray-100 p-6">

    <div class="grid grid-cols-1 md:grid-cols-2 gap-1 mb-1">
        <div>
            <label class="block mb-1 text-black">Total LP Investment: $</label>
            <small>This is the total size of your raise</small>
            <input type="number" id="totalLpInvestment" class="block w-full p-1 border rounded-md bg-gray-100 text-black" value="1000000">
        </div>
        <div>
            <label class="block mb-1 text-black">Minimum LP Share Size: $</label>
            <small>This is the minimum size of your share</small>
            <input type="number" id="proportionalLpShare" class="block w-full p-1 border rounded-md bg-gray-100 text-black" value="100000">
        </div>
        <div>
            <label class="block mb-1 text-black">Pre-Recoupment LP: %</label>
            <small>% the LP gets prior to recouping their initial investment</small>
            <input type="number" id="preWaterfallLp" class="block w-full p-1 border rounded-md bg-gray-100 text-black" value="50">
        </div>
        <div>
            <label class="block mb-1 text-black">Up to 1.5x Return: %</label>
            <small>Waterfalled return from 1.0 to 1.5x their initial investment</small>
            <input type="number" id="upTo1_5xReturnLp" class="block w-full p-1 border rounded-md bg-gray-100 text-black" value="30">
        </div>
        <div>
            <label class="block mb-1 text-black">1.5 to 2.5x Return: %</label>
            <small>Waterfalled return from 1.5 to 2.5x their initial investment</small>
            <input type="number" id="between1_5xAnd2_5xReturnLp" class="block w-full p-1 border rounded-md bg-gray-100 text-black" value="20">
        </div>
        <div>
            <label class="block mb-1 text-black">Above 2.5x Return: %</label>
            <small>Waterfalled return above 2.5x their initial investment</small>
            <input type="number" id="above2_5xReturnLp" class="block w-full p-1 border rounded-md bg-gray-100 text-black" value="20">
        </div>
    </div>
    <div class="pt-2">
        <button onclick="updateTable()" class="bg-blue-500 text-white font-semibold py-1 px-2 rounded mb-2">Update Table</button>
    </div>
    
    <div class="overflow-x-auto">
        <table id="investmentTable" class="min-w-full bg-white border-collapse border border-gray-800">
            <thead>
                <tr class="text-sm bg-gray-300 text-black">
                    <th class="py-1 px-1 border border-gray-800">Year</th>
                    <th class="py-1 px-1 border border-gray-800">Net Return</th>
                    <th class="py-1 px-1 border border-gray-800">LP %</th>
                    <th class="py-1 px-1 border border-gray-800">GP %</th>
                    <th class="py-1 px-1 border border-gray-800">Total LP $</th>
                    <th class="py-1 px-1 border border-gray-800">Total GP $</th>
                    <th class="py-1 px-1 border border-gray-800">Prop LP $</th>
                    <th class="py-1 px-1 border border-gray-800">LP IRR</th>
                    <th class="py-1 px-1 border border-gray-800">LP AAR</th>
                    <th class="py-1 px-1 border border-gray-800">Equity Multiple</th>
                </tr>
                <tr class="text-xs text-gray-800 bg-gray-700">
                    <td class="py-1 px-1 border border-gray-800 pb-2"></td>
                    <td class="py-1 px-1 border border-gray-800 pb-2">Add Your NOI From Your P&L</td>
                    <td class="py-1 px-1 border border-gray-800 pb-2">LP Percentage</td>
                    <td class="py-1 px-1 border border-gray-800 pb-2">GP Percentage</td>
                    <td class="py-1 px-1 border border-gray-800 pb-2">Total Return for LP</td>
                    <td class="py-1 px-1 border border-gray-800 pb-2">Total Return for GP</td>
                    <td class="py-1 px-1 border border-gray-800 pb-2">Proportional Return for LP</td>
                    <td class="py-1 px-1 border border-gray-800 pb-2">Internal Rate of Return for LP</td>
                    <td class="py-1 px-1 border border-gray-800 pb-2">Average Annual Return for LP</td>
                    <td class="py-1 px-1 border border-gray-800 pb-2">Return on Equity Multiple</td>
                </tr>
            </thead>
            <tbody id="investmentTableBody">
                <!-- Table rows will be added here by JavaScript -->
            </tbody>
        </table>
    </div>
    <div class="pt-2">
        <button onclick="updateTable()" class="bg-blue-500 text-white font-semibold py-1 px-2 rounded mb-2">Update Table</button>
        <button onclick="addYear()" class="bg-gray-500 text-white font-semibold text-xs py-1 px-2 rounded mx-1 mb-1">Add Additonal Year</button>
        <!--<button onclick="downloadPDF()" class="bg-red-500 text-white font-semibold py-1 px-2 rounded mb-2">Download PDF</button>-->
    </div>
    
    <div class="mt-6">
        <h2 class="text-black">LP Cumulative Returns</h2>
    </div>
    <canvas id="lpChart" class="mt-6"></canvas>
    
    <script src="https://cdn.tailwindcss.com"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.3.1/jspdf.umd.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.3.2/html2canvas.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script>
        let netReturns = [125000, 350000, 400000, 450000, 525000, 575000, 625000, 650000, 700000, 725000];
        let lpChart;
    
        function populateInitialTable() {
            const tableBody = document.getElementById('investmentTableBody');
            tableBody.innerHTML = '';
    
            netReturns.forEach((netReturn, index) => {
                const year = index + 1;
                const row = document.createElement('tr');
                row.innerHTML = `
                    <td class="text-sm py-1 px-1 border border-gray-800">${year}</td>
                    <td class="text-sm py-1 px-1 border border-gray-800">
                        <div class="flex items-center">
                            <span>$&nbsp;</span>
                            <input type="number" class="w-[150px] p-1 border rounded-md bg-gray-100 text-black" id="netReturn${year}" value="${netReturn}">
                        </div>
                    </td>
                    <td class="text-sm py-1 px-1 border border-gray-800" id="lpPercentage${year}"></td>
                    <td class="text-sm py-1 px-1 border border-gray-800" id="gpPercentage${year}"></td>
                    <td class="text-sm py-1 px-1 border border-gray-800" id="totalLpReturn${year}"></td>
                    <td class="text-sm py-1 px-1 border border-gray-800" id="totalGpReturn${year}"></td>
                    <td class="text-sm py-1 px-1 border border-gray-800" id="proportionalLpReturn${year}"></td>
                    <td class="text-sm py-1 px-1 border border-gray-800" id="lpIrr${year}"></td>
                    <td class="text-sm py-1 px-1 border border-gray-800" id="lpAverageAnnualReturn${year}"></td>
                    <td class="text-sm py-1 px-1 border border-gray-800" id="currentEquityMultiple${year}"></td>
                `;
                tableBody.appendChild(row);
            });
        }
    
        function addYear() {
            const tableBody = document.getElementById('investmentTableBody');
            const newYear = netReturns.length + 1;
            netReturns.push(0); // Add default net return value for the new year
    
            const row = document.createElement('tr');
            row.innerHTML = `
                <td class="text-sm py-1 px-1 border border-gray-800">${newYear}</td>
                <td class="text-sm py-1 px-1 border border-gray-800">
                    <div class="flex items-center">
                        <span>$&nbsp;</span>
                        <input type="number" class="w-[150px] p-1 border rounded-md bg-gray-100 text-black" id="netReturn${newYear}" value="0">
                    </div>
                </td>         
                <td class="text-sm py-1 px-1 border border-gray-800" id="lpPercentage${newYear}"></td>
                <td class="text-sm py-1 px-1 border border-gray-800" id="gpPercentage${newYear}"></td>
                <td class="text-sm py-1 px-1 border border-gray-800" id="totalLpReturn${newYear}"></td>
                <td class="text-sm py-1 px-1 border border-gray-800" id="totalGpReturn${newYear}"></td>
                <td class="text-sm py-1 px-1 border border-gray-800" id="proportionalLpReturn${newYear}"></td>
                <td class="text-sm py-1 px-1 border border-gray-800" id="lpIrr${newYear}"></td>
                <td class="text-sm py-1 px-1 border border-gray-800" id="lpAverageAnnualReturn${newYear}"></td>
                <td class="text-sm py-1 px-1 border border-gray-800" id="currentEquityMultiple${newYear}"></td>
            `;
            tableBody.appendChild(row);
        }
    
        function updateChart(data) {
            const ctx = document.getElementById('lpChart').getContext('2d');
    
            if (lpChart) {
                lpChart.data.labels = data.labels;
                lpChart.data.datasets[0].data = data.values;
                lpChart.update();
            } else {
                lpChart = new Chart(ctx, {
                    type: 'line',
                    data: {
                        labels: data.labels,
                        datasets: [{
                            label: 'LP Cumulative Return',
                            data: data.values,
                            borderColor: 'rgba(75, 192, 192, 1)',
                            borderWidth: 2,
                            fill: false
                        }]
                    },
                    options: {
                        responsive: true,
                        scales: {
                            x: {
                                title: {
                                    display: true,
                                    text: 'Year'
                                }
                            },
                            y: {
                                title: {
                                    display: true,
                                    text: 'LP Cumulative Return'
                                },
                                beginAtZero: true,
                                ticks: {
                                    callback: function(value, index, values) {
                                        return '$' + value.toLocaleString();
                                    }
                                }
                            }
                        },
                        plugins: {
                            tooltip: {
                                callbacks: {
                                    label: function(context) {
                                        let label = context.dataset.label || '';
                                        if (label) {
                                            label += ': ';
                                        }
                                        if (context.parsed.y !== null) {
                                            label += '$' + context.parsed.y.toLocaleString();
                                        }
                                        return label;
                                    }
                                }
                            }
                        }
                    }
                });
            }
        }
    
        function updateTable() {
            const totalLpInvestment = parseFloat(document.getElementById('totalLpInvestment').value);
            const proportionalLpShare = parseFloat(document.getElementById('proportionalLpShare').value);
            const preWaterfallLp = parseFloat(document.getElementById('preWaterfallLp').value);
            const upTo1_5xReturnLp = parseFloat(document.getElementById('upTo1_5xReturnLp').value);
            const between1_5xAnd2_5xReturnLp = parseFloat(document.getElementById('between1_5xAnd2_5xReturnLp').value);
            const above2_5xReturnLp = parseFloat(document.getElementById('above2_5xReturnLp').value);
    
            let cumulativeProportionalLpReturn = 0;
            let cashFlows = [-proportionalLpShare]; // Initial investment as a negative cash flow
            let proportionalLpReturns = [];
            let years = [];
    
            netReturns.forEach((netReturn, index) => {
                const year = index + 1;
                const netReturnValue = Math.round(parseFloat(document.getElementById(`netReturn${year}`).value));
                let lpPercentage;
    
                if (cumulativeProportionalLpReturn < proportionalLpShare) {
                    lpPercentage = preWaterfallLp;
                } else if (cumulativeProportionalLpReturn < 1.5 * proportionalLpShare) {
                    lpPercentage = upTo1_5xReturnLp;
                } else if (cumulativeProportionalLpReturn < 2.5 * proportionalLpShare) {
                    lpPercentage = between1_5xAnd2_5xReturnLp;
                } else {
                    lpPercentage = above2_5xReturnLp;
                }
    
                const gpPercentage = 100 - lpPercentage;
                const totalLpReturn = Math.round(netReturnValue * lpPercentage / 100);
                const totalGpReturn = Math.round(netReturnValue * gpPercentage / 100);
                const proportionalLpReturn = Math.round(totalLpReturn * proportionalLpShare / totalLpInvestment);
                cumulativeProportionalLpReturn += proportionalLpReturn;
                cashFlows.push(proportionalLpReturn); // Add this year's return to cash flows
    
                proportionalLpReturns.push(cumulativeProportionalLpReturn);
                years.push(year);
    
                const lpAverageAnnualReturn = cumulativeProportionalLpReturn / proportionalLpShare / year;
                const currentEquityMultiple = cumulativeProportionalLpReturn / proportionalLpShare;
                const irrValue = approximateIRR(cashFlows);
    
                document.getElementById(`lpPercentage${year}`).innerText = `${lpPercentage}%`;
                document.getElementById(`gpPercentage${year}`).innerText = `${gpPercentage}%`;
                document.getElementById(`totalLpReturn${year}`).innerText = `$${totalLpReturn.toLocaleString()}`;
                document.getElementById(`totalGpReturn${year}`).innerText = `$${totalGpReturn.toLocaleString()}`;
                document.getElementById(`proportionalLpReturn${year}`).innerText = `$${proportionalLpReturn.toLocaleString()}`;
                document.getElementById(`lpIrr${year}`).innerText = (irrValue < 0 || !isFinite(irrValue)) ? "N/A" : `${(irrValue * 100).toFixed(2)}%`;
                document.getElementById(`lpAverageAnnualReturn${year}`).innerText = (year === netReturns.length) ? `${(lpAverageAnnualReturn * 100).toFixed(2)}%` : "N/A";
                document.getElementById(`currentEquityMultiple${year}`).innerText = `${currentEquityMultiple.toFixed(2)}`;
            });
    
            updateChart({ labels: years, values: proportionalLpReturns });
        }
    
        // Simplified IRR approximation function
        function approximateIRR(cashFlows, guess = 0.1) {
            let npv = calculateNPV(guess, cashFlows);
            const tolerance = 0.0001; // NPV tolerance for convergence
            const maxIterations = 1000; // Maximum iterations to avoid infinite loop
            let iteration = 0;
    
            while (Math.abs(npv) > tolerance && iteration < maxIterations) {
                const derivative = calculateNPVDerivative(guess, cashFlows);
                guess -= npv / derivative;
                npv = calculateNPV(guess, cashFlows);
                iteration++;
            }
    
            return guess;
        }
    
        // Function to calculate NPV
        function calculateNPV(rate, cashFlows) {
            return cashFlows.reduce((acc, curr, i) => acc + curr / Math.pow(1 + rate, i), 0);
        }
    
        // Function to calculate the derivative of NPV with respect to the discount rate
        function calculateNPVDerivative(rate, cashFlows) {
            return cashFlows.reduce((acc, curr, i) => acc - i * curr / Math.pow(1 + rate, i + 1), 0);
        }
    
        function downloadPDF() {
            const { jsPDF } = window.jspdf;
            html2canvas(document.getElementById('investmentTable')).then(canvas => {
                const imgData = canvas.toDataURL('image/png');
                const pdf = new jsPDF('l', 'mm', 'a4');
                const imgProps = pdf.getImageProperties(imgData);
                const pdfWidth = pdf.internal.pageSize.getWidth();
                const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
                pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight);
                pdf.save('investment_table.pdf');
            });
        }
    
        // Initial table population
        populateInitialTable();
        updateTable();
    </script>
    

</body>
</html>