package amyc.utils

import java.io.File

import silex._

object Position:
  /** Number of bits used to encode the line number */
  private final val LINE_BITS   = 20
  /** Number of bits used to encode the column number */
  private final val COLUMN_BITS = 31 - LINE_BITS // no negatives => 31
  /** Mask to decode the line number */
  private final val LINE_MASK   = (1 << LINE_BITS) - 1
  /** Mask to decode the column number */
  private final val COLUMN_MASK = (1 << COLUMN_BITS) - 1

  private def lineOf(pos: Int): Int = (pos >> COLUMN_BITS) & LINE_MASK
  private def columnOf(pos: Int): Int = pos & COLUMN_MASK

  def fromFile(f: File, i: Int) =
    SourcePosition(f, lineOf(i), columnOf(i))

abstract class Position:
  val file: File
  val line: Int
  val col: Int

  def isDefined: Boolean
  def withoutFile: String

case class SourcePosition(file: File, line: Int, col: Int) extends Position:
  override def toString: String = s"${file.getPath}:$line:$col"
  def withoutFile = s"$line:$col"
  val isDefined = true

case object NoPosition extends Position:
  val file = null
  val line = 0
  val col = 0

  override def toString: String = "?:?"
  def withoutFile = toString
  val isDefined = false

// A trait for entities which have a position in a file
trait Positioned:

  protected var pos_ : Position = NoPosition

  def hasPosition = pos_ != NoPosition

  def position = pos_

  def setPos(pos: Position): this.type =
    pos_ = pos
    this

  def setPos(other: Positioned): this.type =
    setPos(other.position)

case class SourcePositioner(file: File) extends Positioner[Char, SourcePosition]:
  override val start: SourcePosition = SourcePosition(file, 1, 1)

  override def increment(position: SourcePosition, character: Char): SourcePosition =
    if character == '\n' then
      position.copy(line = position.line + 1, col = 1)
    else
      position.copy(col = position.col + 1)

