package skeb

import scala.gui.Publisher

/** A class representing a single cell in the spreadsheet. All cells must be
  * referenced from within a Sheet instance. */
abstract class Cell extends Publisher {
  
  /** The function that evaluates the content of this cell. */
  private val evaluate = new Evaluator(this)
                             with operation.Numerator
                             with operation.Algebraic
  
  /** A reference to the containing Sheet instance. */
  val sheet: Sheet
  /** The column index in the containing Sheet for this cell. */
  val column: Int
  /** The row index in the containing Sheet for this cell. */
  val row: Int
  
  /** Prints the Cell in a human-readable form. */
  override def toString() =
    "Cell(" + column + "#" + row + ")"
  
  /** The current expression provided by the user for this cell. This must be
    * evaluated to calculate the actual value. */
  var exp: Expression = expression.Empty
  
  /** The displayable value of this cell. This is the result of evaluating the
    * exp expression. */
  def value: expression.Constant = valueBuffer match {
    case None =>
      valueBuffer = Some(evaluate(exp))
      value
    case Some(v) => v
  }
  private var valueBuffer: Option[expression.Constant] = None
  
  /** A method called by the user-interface when the user sets a new expression
    * for this cell.
    * @param value Either the new value as a non-empty string, or None when the
    *              cell has been emptied. */
  def userSets (value: Option[String]) = {
    // Removes all subscribtions: the still relevant ones will be re-created.
    for (val ref <- exp.references) unsubscribe(ref)
    // Parses the expression from the string the user provided.
    if (value.isEmpty)
      exp = expression.Empty
    else
      exp = sheet.parse(value.get)
    // Subscribe to all cells where changes are relevant to this cell.
    for (val ref <- exp.references) subscribe(ref)
    // Tell all relevant cells (that is cells which value can have an impact
    // on this cell's value) that the value changed.
    publish(sheet.CellValueChanged(this))
    // Also inform this cell in order to clear its value buffer.
    this send sheet.CellValueChanged(this)
  }
  
  // This is the constructor: declares the event loop to handle a value change
  // on a relevant cell.
  toplevel eventloop {
    case sheet.CellValueChanged(cell) =>
      valueBuffer = None
      if (cell != this) publish(sheet.CellValueChanged(this))
  }
  
}