package loci

import language._

import scala.annotation.compileTimeOnly
import scala.concurrent.ExecutionContext
import scala.language.experimental.macros
import scala.language.implicitConversions

class Instance[P] private[loci] (dummy: Int) {
  @compileTimeOnly("Multitier peer instantiation must be of the form: multitier start new Instance[P]")
  def this() = this(0)

  @compileTimeOnly("Multitier peer instantiation must be of the form: multitier start new Instance[P]")
  def this(connect: Connections) = this(0)

  @compileTimeOnly("Multitier peer instantiation must be of the form: multitier start new Instance[P]")
  def this(context: ExecutionContext) = this(0)

  @compileTimeOnly("Multitier peer instantiation must be of the form: multitier start new Instance[P]")
  def this(separateMainThread: Boolean) = this(0)

  @compileTimeOnly("Multitier peer instantiation must be of the form: multitier start new Instance[P]")
  def this(context: ExecutionContext, connect: Connections) = this(0)

  @compileTimeOnly("Multitier peer instantiation must be of the form: multitier start new Instance[P]")
  def this(separateMainThread: Boolean, connect: Connections) = this(0)

  @compileTimeOnly("Multitier peer instantiation must be of the form: multitier start new Instance[P]")
  def this(context: ExecutionContext, separateMainThread: Boolean) = this(0)

  @compileTimeOnly("Multitier peer instantiation must be of the form: multitier start new Instance[P]")
  def this(context: ExecutionContext, separateMainThread: Boolean, connect: Connections) = this(0)
}

object Instance {
  final implicit class Ops[P](instance: Instance[P]) {
    def retrieve[T](retrievable: Retrievable[T]): T =
      macro language.impl.Instance.retrieve

    def terminate(): Unit = instance match {
      case instance: runtime.Instance[P] => instance.terminate()
      case _ => throw new runtime.PeerImplementationError
    }

    def terminated: Notice.Steady[Unit] = instance match {
      case instance: runtime.Instance[P] => instance.terminated
      case _ => throw new runtime.PeerImplementationError
    }
  }


  trait SubjectiveValue[T, R] {
    def to(remote: Remote[R]): T
  }


  sealed trait Retrievable[T]

  sealed trait RetrievableDefault {
    implicit def default[T](v: T): Retrievable[T] = erased
  }

  sealed trait RetrievablePlaced extends RetrievableDefault {
    implicit def placed[T, P](v: T on P): Retrievable[T] = erased
  }

  sealed trait RetrievableLocal extends RetrievablePlaced {
    implicit def local[T, P, _Local_[T] <: Local[T]](v: _Local_[T] on P): Retrievable[T] = erased
  }

  object Retrievable extends RetrievableLocal {
    implicit def subjective[T, R, P](v: T per R on P): Retrievable[SubjectiveValue[T, P]] = erased
  }
}