/*
   Copyright 2010 Aaron J. Radke

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package cc.drx

trait Unsigned extends Any{
  //---required
  def depth:Int
  def toLong:Long

  //--optimizable
  def mask:Long = depth.iterate(0L){_ << 1 | 1}

  def signed:Long = {val shift = 64 - depth; toLong << shift >> shift} //move sign bit up then pull it back down if it is signed

  def hex = s"0x%0${depth/4}x" format toLong
  override def toString = hex
}

trait UnsignedObject{
  // def depth:Int
  /*
  //TODO make better byte compositions to avoid java 2's complement integer assumptions oddities
  type US = Iterable[Unsigned]
  trait Pack[A] { def pack(xs:US):A }
  object Pack{
    def packLong(us:US):Long = us.toList.reverse.foldLeft( (0,0L) ){case ((d,v), u) => (d + u.depth,  u.toLong << d) }._2

    implicit object PackInt extends Pack[Int]{   def pack(xs:US):Int = packLong(xs).toInt}
    implicit object PackLong extends Pack[Long]{ def pack(xs:US):Long = packLong(xs) }
    implicit object PackByte extends Pack[Byte]{ def pack(xs:US):Byte = packLong(xs).toByte }
  }
  def pack[A](us:US)(implicit packer:Pack[A]):A = packer.pack(us)
  */
}

//TODO add assertions bounds for the constructions: i.e. Ints should not be bigger than an Byte if an unsigned byte is constructed
//the following classes make using unsigned integer types more first class
object U4 extends UnsignedObject{
  val MinValue = U4(0x0)
  val MaxValue = U4(0xF)
  val byteWidth = 1 // 4bits
  // val bound = Bound(MinValue,MaxValue)
  @inline final def apply(v:Int):U4 =  new U4((v & 0xF).toByte)
  @inline final def apply(v:Byte):U4 = new U4((v & 0xF).toByte) //warning: bytes could be lost here
  // def fromByteArray(bytes:Array[Bytes],offset:Int=0):U4 = new U4( bytes(offset) )
}
object U8{
  val MinValue = U8(0x00)
  val MaxValue = U8(0xFF)
  val byteWidth = 1 // 8bits
  // val bound = Bound(MinValue,MaxValue)
  @inline final def apply(v:Byte):U8 = new U8(v)
  @inline final def apply(v:Int):U8 =  new U8(v.toByte) //TODO maybe make these over determined constructors unsafe?
  @inline final def apply(h:U4,l:U4):U8   = new U8( ( h.toInt<<4 | l.toInt).toByte )
  def fromByteArray(bytes:Array[Byte],offset:Int=0):U4 = U4(bytes(offset))
}
object U12{
  val MinValue = U12(0x000)
  val MaxValue = U12(0xFFF)
  val byteWidth = 2
  // val bound = Bound(MinValue,MaxValue)
  @inline final def apply(v:Short):U12 = new U12((v & 0x0FFF).toShort)
  @inline final def apply(v:Int):U12 = new U12((v & 0x0FFF).toShort)
  @inline final def apply(hh:U4, h:U4,l:U4):U12  = new U12( ((hh.toInt << 8 | h.toInt<<4 | l.toInt)&0x0FFF).toShort )

}
object U16{
  val MinValue = U16(0x0000)
  val MaxValue = U16(0xFFFF)
  val byteWidth = 2
  // val bound = Bound(MinValue,MaxValue)
  @inline final def apply(v:Short):U16 = new U16(v)
  @inline final def apply(v:Int):U16 = new U16(v.toShort)
  @inline final def apply(h:U8,l:U8):U16 = new U16( (h.toInt<<8 | l.toInt ).toShort )
  @inline final def apply(a:U4,b:U4,c:U4,d:U4):U16 = new U16(( a.toInt<<12 | b.toInt<<8 | c.toInt<<4 | d.toInt).toShort)
}
object U24{
  val MinValue = U24(0x00000000)
  val MaxValue = U24(0x00FFFFFF)
  // val bound = Bound(MinValue,MaxValue)
  @inline final def apply(v:Int):U24 = new U24((v & 0x00FFFFFF).toInt)
  val byteWidth = 3
}
/**Int constructor*/
object U32{
  val MinValue = U32(0x00000000L)
  val MaxValue = U32(0xFFFFFFFFL)
  val byteWidth = 4
  // val bound = Bound(MinValue,MaxValue)
  @inline final def apply(v:Int):U32 = new U32(v)
  @inline final def apply(v:Long):U32 = new U32((v & 0xFFFFFFFFL).toInt)
  @inline final def apply(h:U16,l:U16):U32 = new U32( h.toInt<<16 | l.toInt )
  @inline final def apply(a:U8,b:U8,c:U8,d:U8):U32 = new U32(( a.toInt<<24 | b.toInt<<16 | c.toInt<<8 | d.toInt).toShort)

  def fromByteArray(bytes:Array[Byte],offset:Int=0):U32 = {
    @inline def b(i:Int):U8 = U8(bytes(i+offset))
    U32(b(0), b(1), b(2), b(3))
  }
}
/**Long constructor*/
object U64{
  val MinValue = U64(0x0L)
  val MaxValue = U64(0xFFFFFFFFFFFFFFFFL)
  val byteWidth = 8
  // val bound = Bound(MinValue,MaxValue)
  @inline final def apply(v:Long):U64 = new U64(v)
  @inline final def apply(h:U32,l:U32):U64 = new U64( h.toLong<<32 | l.toLong)
}

//--classes
/**Nibble wrapper*/
class U4(val value:Byte) extends AnyVal with Unsigned{
  def depth:Int = 4
  def toByte  = value
  def toShort = toInt.toShort
  def toInt  = value & 0xF
  def toLong = value & 0xFL
  def +(that:U4):U4 = U4(this.value + that.value)
}
/**Byte wrapper*/
class U8(val value:Byte) extends AnyVal with Unsigned{
  def depth:Int = 8
  def toShort = toInt.toShort
  def toInt  = value & 0xFF
  def toLong = value & 0xFFL
  def hi:U4 = U4(value >> 4)
  def lo:U4 = U4(value)
  def +(that:U8):U8 = U8(this.value + that.value)
}
class U12(val value:Short) extends AnyVal with Unsigned{
  def depth:Int = 12
  def toShort = value
  def toInt  = value & 0x0FFF
  def toLong = value & 0x0FFFL
  def +(that:U12):U12 = U12(this.value + that.value) //TODO optimize so no masking is required
}
/**Short wrapper*/
class U16(val value:Short) extends AnyVal with Unsigned{
  def depth:Int = 16
  def toChar = value.toChar
  def toInt  = value & 0xFFFF
  def toLong = value & 0xFFFFL
  def hi:Byte = (value >> 8).toByte
  def lo:Byte = value.toByte
  def +(that:U16):U16 = U16(this.value + that.value)
}
class U24(val value:Int) extends AnyVal with Unsigned{
  def depth:Int = 24
  def toInt  = value & 0x00FFFFFF
  def toLong = value & 0x00FFFFFFL
  def +(that:U24):U24 = U24(this.value + that.value) //TODO optimize
}
/**Int wrapper*/
class U32(val value:Int) extends AnyVal with Unsigned{
  def depth:Int = 32
  //toInt is not safe here because the value could be negative
  def toLong = value & 0xFFFFFFFFL
  def hi:Short = (value >> 16).toShort
  def lo:Short = value.toShort
  def +(that:U32):U32 = U32(this.value + that.value)
  // def toByteArray:Array[Byte] = {   //--implicit from DrxInt
  //   @inline def b(shift:Int):Byte = ((value >> shift) & 0xFF).toByte
  //   Array( b(24), b(16), b(8), b(0) )
  // }
}
/**Long wrapper*/
class U64(val value:Long) extends AnyVal with Unsigned{
  def depth:Int = 64
  def toLong = value
  def hi:Int = (value >> 32).toInt
  def lo:Int = value.toInt
  def +(that:U64):U64 = U64(this.value + that.value)
}