defmodule UmyaSpreadsheet.OleObjects do
  @moduledoc """
  Functions for working with OLE (Object Linking and Embedding) objects in a spreadsheet.

  OLE objects allow you to embed documents and files from other applications into your
  Excel spreadsheets, such as Word documents, PowerPoint presentations, PDF files, and more.

  ## Overview

  This module provides functionality to:
  - Create and manage collections of OLE objects
  - Add embedded objects to worksheets
  - Load and save OLE object data
  - Configure object properties and settings
  - Check object formats and requirements

  ## Examples

      # Create a new OLE objects collection
      {:ok, ole_objects} = UmyaSpreadsheet.OleObjects.new_ole_objects()

      # Add the collection to a worksheet
      :ok = UmyaSpreadsheet.OleObjects.set_ole_objects(spreadsheet, "Sheet1", ole_objects)

      # Create an OLE object from a file
      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object_from_file("document.docx")

      # Add the object to the collection
      :ok = UmyaSpreadsheet.OleObjects.add_ole_object(ole_objects, ole_object)
  """

  alias UmyaSpreadsheet.Spreadsheet
  alias UmyaSpreadsheet.ErrorHandling
  alias UmyaNative

  # OLE Objects Collection Management

  @doc """
  Creates a new OLE objects collection.

  ## Returns

  - `{:ok, ole_objects}` - A new OLE objects collection resource
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_objects} = UmyaSpreadsheet.OleObjects.new_ole_objects()
  """
  def new_ole_objects do
    UmyaNative.create_ole_objects()
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Gets the OLE objects collection from a worksheet.

  ## Parameters

  - `spreadsheet` - The spreadsheet struct
  - `sheet_name` - The name of the worksheet

  ## Returns

  - `{:ok, ole_objects}` - The OLE objects collection from the worksheet
  - `{:error, reason}` on failure

  ## Examples

      {:ok, spreadsheet} = UmyaSpreadsheet.read_file("input.xlsx")
      {:ok, ole_objects} = UmyaSpreadsheet.OleObjects.get_ole_objects_from_worksheet(spreadsheet, "Sheet1")
  """
  def get_ole_objects_from_worksheet(%Spreadsheet{reference: ref}, sheet_name) do
    UmyaNative.get_ole_objects(ref, sheet_name)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Sets the OLE objects collection for a worksheet.

  ## Parameters

  - `spreadsheet` - The spreadsheet struct
  - `sheet_name` - The name of the worksheet
  - `ole_objects` - The OLE objects collection resource

  ## Returns

  - `:ok` on success
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_objects} = UmyaSpreadsheet.OleObjects.new_ole_objects()
      :ok = UmyaSpreadsheet.OleObjects.set_ole_objects_to_worksheet(spreadsheet, "Sheet1", ole_objects)
  """
  def set_ole_objects_to_worksheet(%Spreadsheet{reference: ref}, sheet_name, ole_objects) do
    UmyaNative.set_ole_objects(ref, sheet_name, ole_objects)
    |> ErrorHandling.standardize_result()
  end

  # Individual OLE Object Management

  @doc """
  Creates a new OLE object.

  ## Returns

  - `{:ok, ole_object}` - A new OLE object resource
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object()
  """
  def new_ole_object do
    UmyaNative.create_ole_object()
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Creates a new OLE object and loads data from a file.

  ## Parameters

  - `file_path` - Path to the file to embed

  ## Returns

  - `{:ok, ole_object}` - A new OLE object with file data loaded
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object_from_file("document.docx")
  """
  def new_ole_object_from_file(file_path) do
    # Based on umya_native.ex, this should be load_ole_object_from_file but it takes different params
    # We'll need to create a simple wrapper that determines the prog_id from file extension
    extension = file_path |> Path.extname() |> String.trim_leading(".")

    prog_id =
      case extension do
        "docx" -> "Word.Document.12"
        "xlsx" -> "Excel.Sheet.12"
        "pptx" -> "PowerPoint.Show.12"
        _ -> "Package"
      end

    UmyaNative.load_ole_object_from_file(file_path, prog_id)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Creates a new OLE object with binary data.

  ## Parameters

  - `data` - Binary data to embed
  - `file_extension` - File extension (e.g., "docx", "pptx")

  ## Returns

  - `{:ok, ole_object}` - A new OLE object with the provided data
  - `{:error, reason}` on failure

  ## Examples

      data = File.read!("document.docx")
      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object_with_data(data, "docx")
  """
  def new_ole_object_with_data(data, file_extension) do
    prog_id =
      case file_extension do
        "docx" -> "Word.Document.12"
        "xlsx" -> "Excel.Sheet.12"
        "pptx" -> "PowerPoint.Show.12"
        _ -> "Package"
      end

    # Convert string to binary list if needed
    binary_data = if is_binary(data), do: :binary.bin_to_list(data), else: data

    UmyaNative.create_ole_object_with_data(prog_id, file_extension, binary_data)
    |> ErrorHandling.standardize_result()
  end

  # Collection Operations

  @doc """
  Adds an OLE object to a collection.

  ## Parameters

  - `ole_objects` - The OLE objects collection
  - `ole_object` - The OLE object to add

  ## Returns

  - `:ok` on success
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_objects} = UmyaSpreadsheet.OleObjects.new_ole_objects()
      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object_from_file("document.docx")
      :ok = UmyaSpreadsheet.OleObjects.add_ole_object(ole_objects, ole_object)
  """
  def add_ole_object(ole_objects, ole_object) do
    UmyaNative.add_ole_object(ole_objects, ole_object)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Lists all OLE objects in a collection.

  ## Parameters

  - `ole_objects` - The OLE objects collection

  ## Returns

  - `{:ok, objects_list}` - List of OLE object resources
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_objects} = UmyaSpreadsheet.OleObjects.get_ole_objects_from_worksheet(spreadsheet, "Sheet1")
      {:ok, objects_list} = UmyaSpreadsheet.OleObjects.list_ole_objects(ole_objects)
  """
  def list_ole_objects(ole_objects) do
    UmyaNative.get_ole_object_list(ole_objects)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Gets the count of OLE objects in a collection.

  ## Parameters

  - `ole_objects` - The OLE objects collection

  ## Returns

  - `{:ok, count}` - Number of OLE objects in the collection
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_objects} = UmyaSpreadsheet.OleObjects.get_ole_objects_from_worksheet(spreadsheet, "Sheet1")
      {:ok, count} = UmyaSpreadsheet.OleObjects.get_ole_objects_count(ole_objects)
  """
  def get_ole_objects_count(ole_objects) do
    UmyaNative.count_ole_objects(ole_objects)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Checks if an OLE objects collection has any objects.

  ## Parameters

  - `ole_objects` - The OLE objects collection

  ## Returns

  - `{:ok, boolean}` - true if collection has objects, false otherwise
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_objects} = UmyaSpreadsheet.OleObjects.get_ole_objects_from_worksheet(spreadsheet, "Sheet1")
      {:ok, has_objects} = UmyaSpreadsheet.OleObjects.has_ole_objects(ole_objects)
  """
  def has_ole_objects(ole_objects) do
    UmyaNative.has_ole_objects(ole_objects)
    |> ErrorHandling.standardize_result()
  end

  # OLE Object Properties

  @doc """
  Gets the properties of an OLE object.

  ## Parameters

  - `ole_object` - The OLE object resource

  ## Returns

  - `{:ok, properties}` - The embedded object properties resource
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object_from_file("document.docx")
      {:ok, properties} = UmyaSpreadsheet.OleObjects.get_ole_object_properties(ole_object)
  """
  def get_ole_object_properties(ole_object) do
    UmyaNative.get_ole_object_properties(ole_object)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Sets the properties for an OLE object.

  ## Parameters

  - `ole_object` - The OLE object resource
  - `properties` - The embedded object properties resource

  ## Returns

  - `:ok` on success
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object()
      {:ok, properties} = UmyaSpreadsheet.OleObjects.new_embedded_object_properties()
      :ok = UmyaSpreadsheet.OleObjects.set_ole_object_properties(ole_object, properties)
  """
  def set_ole_object_properties(ole_object, properties) do
    UmyaNative.set_ole_object_properties(ole_object, properties)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Creates new embedded object properties.

  ## Returns

  - `{:ok, properties}` - A new embedded object properties resource
  - `{:error, reason}` on failure

  ## Examples

      {:ok, properties} = UmyaSpreadsheet.OleObjects.new_embedded_object_properties()
  """
  def new_embedded_object_properties do
    UmyaNative.create_embedded_object_properties()
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Gets the 'requires' attribute from an OLE object.

  ## Parameters

  - `ole_object` - The OLE object resource

  ## Returns

  - `{:ok, requires}` - The requires string value
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object_from_file("document.docx")
      {:ok, requires} = UmyaSpreadsheet.OleObjects.get_ole_object_requires(ole_object)
  """
  def get_ole_object_requires(ole_object) do
    UmyaNative.get_ole_object_requires(ole_object)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Sets the 'requires' attribute for an OLE object.

  ## Parameters

  - `ole_object` - The OLE object resource
  - `requires` - The requires string value

  ## Returns

  - `:ok` on success
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object()
      :ok = UmyaSpreadsheet.OleObjects.set_ole_object_requires(ole_object, "application")
  """
  def set_ole_object_requires(ole_object, requires) do
    UmyaNative.set_ole_object_requires(ole_object, requires)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Gets the ProgID from an OLE object.

  ## Parameters

  - `ole_object` - The OLE object resource

  ## Returns

  - `{:ok, prog_id}` - The ProgID string value
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object_from_file("document.docx")
      {:ok, prog_id} = UmyaSpreadsheet.OleObjects.get_ole_object_prog_id(ole_object)
  """
  def get_ole_object_prog_id(ole_object) do
    UmyaNative.get_ole_object_prog_id(ole_object)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Sets the ProgID for an OLE object.

  ## Parameters

  - `ole_object` - The OLE object resource
  - `prog_id` - The ProgID string value

  ## Returns

  - `:ok` on success
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object()
      :ok = UmyaSpreadsheet.OleObjects.set_ole_object_prog_id(ole_object, "Word.Document.12")
  """
  def set_ole_object_prog_id(ole_object, prog_id) do
    UmyaNative.set_ole_object_prog_id(ole_object, prog_id)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Gets the file extension from an OLE object.

  ## Parameters

  - `ole_object` - The OLE object resource

  ## Returns

  - `{:ok, extension}` - The file extension string
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object_from_file("document.docx")
      {:ok, extension} = UmyaSpreadsheet.OleObjects.get_ole_object_extension(ole_object)
  """
  def get_ole_object_extension(ole_object) do
    UmyaNative.get_ole_object_extension(ole_object)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Sets the file extension for an OLE object.

  ## Parameters

  - `ole_object` - The OLE object resource
  - `extension` - The file extension string

  ## Returns

  - `:ok` on success
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object()
      :ok = UmyaSpreadsheet.OleObjects.set_ole_object_extension(ole_object, "docx")
  """
  def set_ole_object_extension(ole_object, extension) do
    UmyaNative.set_ole_object_extension(ole_object, extension)
    |> ErrorHandling.standardize_result()
  end

  # Embedded Object Properties

  @doc """
  Gets the ProgID from embedded object properties.

  ## Parameters

  - `properties` - The embedded object properties resource

  ## Returns

  - `{:ok, prog_id}` - The ProgID string value
  - `{:error, reason}` on failure

  ## Examples

      {:ok, properties} = UmyaSpreadsheet.OleObjects.get_ole_object_properties(ole_object)
      {:ok, prog_id} = UmyaSpreadsheet.OleObjects.get_embedded_object_prog_id(properties)
  """
  def get_embedded_object_prog_id(properties) do
    UmyaNative.get_embedded_object_prog_id(properties)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Sets the ProgID for embedded object properties.

  ## Parameters

  - `properties` - The embedded object properties resource
  - `prog_id` - The ProgID string value

  ## Returns

  - `:ok` on success
  - `{:error, reason}` on failure

  ## Examples

      {:ok, properties} = UmyaSpreadsheet.OleObjects.new_embedded_object_properties()
      :ok = UmyaSpreadsheet.OleObjects.set_embedded_object_prog_id(properties, "Word.Document.12")
  """
  def set_embedded_object_prog_id(properties, prog_id) do
    UmyaNative.set_embedded_object_prog_id(properties, prog_id)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Gets the shape ID from embedded object properties.

  ## Parameters

  - `properties` - The embedded object properties resource

  ## Returns

  - `{:ok, shape_id}` - The shape ID number
  - `{:error, reason}` on failure

  ## Examples

      {:ok, properties} = UmyaSpreadsheet.OleObjects.get_ole_object_properties(ole_object)
      {:ok, shape_id} = UmyaSpreadsheet.OleObjects.get_embedded_object_shape_id(properties)
  """
  def get_embedded_object_shape_id(properties) do
    UmyaNative.get_embedded_object_shape_id(properties)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Sets the shape ID for embedded object properties.

  ## Parameters

  - `properties` - The embedded object properties resource
  - `shape_id` - The shape ID number

  ## Returns

  - `:ok` on success
  - `{:error, reason}` on failure

  ## Examples

      {:ok, properties} = UmyaSpreadsheet.OleObjects.new_embedded_object_properties()
      :ok = UmyaSpreadsheet.OleObjects.set_embedded_object_shape_id(properties, 1)
  """
  def set_embedded_object_shape_id(properties, shape_id) do
    UmyaNative.set_embedded_object_shape_id(properties, shape_id)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Gets the binary data from an OLE object.

  ## Parameters

  - `ole_object` - The OLE object resource

  ## Returns

  - `{:ok, data}` - The binary data
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object_from_file("document.docx")
      {:ok, data} = UmyaSpreadsheet.OleObjects.get_ole_object_data(ole_object)
  """
  def get_ole_object_data(ole_object) do
    UmyaNative.get_ole_object_data(ole_object)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Sets the binary data for an OLE object.

  ## Parameters

  - `ole_object` - The OLE object resource
  - `data` - The binary data to set

  ## Returns

  - `:ok` on success
  - `{:error, reason}` on failure

  ## Examples

      data = File.read!("document.docx")
      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object()
      :ok = UmyaSpreadsheet.OleObjects.set_ole_object_data(ole_object, data)
  """
  def set_ole_object_data(ole_object, data) do
    # Convert string to binary list if needed
    binary_data = if is_binary(data), do: :binary.bin_to_list(data), else: data

    UmyaNative.set_ole_object_data(ole_object, binary_data)
    |> ErrorHandling.standardize_result()
  end

  # File Operations

  @doc """
  Loads an OLE object from a file.
  Note: This function signature differs from the underlying NIF which takes (file_path, prog_id).

  ## Parameters

  - `ole_object` - This parameter is ignored for this function
  - `file_path` - Path to the file to load

  ## Returns

  - `{:ok, ole_object}` - A new OLE object with file data loaded
  - `{:error, reason}` on failure

  ## Examples

      # This creates a new OLE object rather than loading into an existing one
      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.load_ole_object_from_file(nil, "document.docx")
  """
  def load_ole_object_from_file(_ole_object, file_path) do
    extension = file_path |> Path.extname() |> String.trim_leading(".")

    prog_id =
      case extension do
        "docx" -> "Word.Document.12"
        "xlsx" -> "Excel.Sheet.12"
        "pptx" -> "PowerPoint.Show.12"
        _ -> "Package"
      end

    UmyaNative.load_ole_object_from_file(file_path, prog_id)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Saves an OLE object to a file.

  ## Parameters

  - `ole_object` - The OLE object resource
  - `file_path` - Path where the file should be saved

  ## Returns

  - `:ok` on success
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object_from_file("input.docx")
      :ok = UmyaSpreadsheet.OleObjects.save_ole_object_to_file(ole_object, "output.docx")
  """
  def save_ole_object_to_file(ole_object, file_path) do
    UmyaNative.save_ole_object_to_file(ole_object, file_path)
    |> ErrorHandling.standardize_result()
  end

  # Format Checking

  @doc """
  Checks if an OLE object is in binary format.

  ## Parameters

  - `ole_object` - The OLE object resource

  ## Returns

  - `{:ok, boolean}` - true if in binary format, false otherwise
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object_from_file("document.docx")
      {:ok, is_binary} = UmyaSpreadsheet.OleObjects.is_ole_object_binary_format(ole_object)
  """
  def is_ole_object_binary_format(ole_object) do
    UmyaNative.is_ole_object_binary(ole_object)
    |> ErrorHandling.standardize_result()
  end

  @doc """
  Checks if an OLE object is in Excel format.

  ## Parameters

  - `ole_object` - The OLE object resource

  ## Returns

  - `{:ok, boolean}` - true if in Excel format, false otherwise
  - `{:error, reason}` on failure

  ## Examples

      {:ok, ole_object} = UmyaSpreadsheet.OleObjects.new_ole_object_from_file("spreadsheet.xlsx")
      {:ok, is_excel} = UmyaSpreadsheet.OleObjects.is_ole_object_excel_format(ole_object)
  """
  def is_ole_object_excel_format(ole_object) do
    UmyaNative.is_ole_object_excel(ole_object)
    |> ErrorHandling.standardize_result()
  end

  # Helper Functions

  @doc """
  Determines the ProgID from a file extension.

  ## Parameters

  - `extension` - The file extension (without the dot)

  ## Returns

  - `string` - The appropriate ProgID for the extension

  ## Examples

      "Word.Document.12" = UmyaSpreadsheet.OleObjects.determine_prog_id("docx")
      "Excel.Sheet.12" = UmyaSpreadsheet.OleObjects.determine_prog_id("xlsx")
  """
  def determine_prog_id(extension) do
    case extension do
      "docx" -> "Word.Document.12"
      "xlsx" -> "Excel.Sheet.12"
      "pptx" -> "PowerPoint.Show.12"
      "pdf" -> "AcroExch.Document"
      "txt" -> "txtfile"
      _ -> "Package"
    end
  end
end
