# This module is under development. APIs and behavior may change without notice.

import "csv" |

# Extract table structures from a list of markdown nodes.
def tables(md_nodes):
  if (is_empty(md_nodes)):
    []
  else:
    do
      var tables = []
      | var current_align = []
      | var current_row_index = -1
      | var current_row = []
      | var current_rows = []
      | var i = 0
      | let nodes_len = len(md_nodes)
      | while (i < nodes_len):
          let node = md_nodes[i]
          | if (is_table_cell(node)):
              if (node.row != current_row_index) do
                  if (!is_empty(current_row)):
                    current_rows += [current_row]
                  | current_row = []
                  | current_row_index = node.row
                  | current_row = [node]
                end
              else:
                current_row += node
            elif (is_table_align(node)): current_align = node
            else: do
                if (!is_empty(current_rows)) do
                    tables += [{type: :table, align: current_align, header: current_rows[0], rows: current_rows[1:]}]
                    | current_align = []
                    | current_rows = []
                    | current_row = []
                    | current_row_index = -1
                  end
              end
          | i = i + 1
          | None
        end
      | if (!is_empty(current_row)) do
            current_rows += [current_row]
          end
      | if (!is_empty(current_rows)) do
            tables += [{type: :table, align: current_align, header: current_rows[0], rows: current_rows[1:]}]
          end
      | tables
    end
end

# Set the alignment for a table.
def set_align(table, align):
  set(table, :align, set_attr(table[:align], "align", join(map(align, to_string), ",")))
end

# Add a new row to a table.
def add_row(table, row):
  if (len(row) != len(table[:header])):
    error("Row length does not match table header length.")
  else: do
      var i = 0
      | let cells = foreach (cell, row):
              to_md_table_cell(cell, len(table[:rows]) + 1, i)
              | i += 1
            end
      | set(table, :rows, table[:rows] + [cells])
    end
end

# Add a new column to a table.
def add_column(table, col):
  if (len(col) != len(table[:rows]) + 1):
    error("Column length does not match table row count.")
  else: do
      let header_cell = to_md_table_cell(col[0], 0, len(table[:header]))
      | let new_header = table[:header] + [header_cell]
      | var i = 0
      | let new_rows = foreach (row, table[:rows]):
              let cell = to_md_table_cell(col[i], i, len(row) - 1)
              | i += 1
              | row + [cell]
            end
      | set(table, :header, new_header) | set(table, :rows, new_rows)
    end
end

# Remove a row from a table at the specified index.
def remove_row(table, row_index):
  set(table, :rows, del(table[:rows], row_index))
end

# Remove a column from a table at the specified index.
def remove_column(table, col_index):
  let rows = foreach (row, table[:rows]):
      del(row, col_index)
    end
  | set(table, :rows, rows)
end

# Map a function over each row in the table.
def map_rows(table, f):
  set(table, :rows, map(table[:rows], f))
end

# Filter tables from markdown nodes based on a predicate function.
def filter_tables(tables, f):
  filter(tables, fn(table): f(table[:header], table[:rows]);)
end

# Filter rows in the table based on a predicate function.
def filter_rows(table, f):
  set(table, :rows, filter(table[:rows], f))
end

# Sort rows in the table by a specified column index or default sorting.
def sort_rows(table, column_index = None):
  let new_rows = if (column_index == None):
    sort(table[:rows])
  else:
    sort_by(table[:rows], fn(row): row[column_index];)
  | set(table, :rows, new_rows)
end

# Convert a table structure back into a list of markdown nodes.
def to_markdown(table)
  table[:header] + table[:align] + flatten(table[:rows])
end

# Convert a table structure into a CSV string with the specified delimiter.
def to_csv(table, delimiter = ",")
  let header = table[:header]
  | let rows = table[:rows]
  | csv::csv_stringify([header] + rows, delimiter)
end

