package decoder

import Types.{Bit, Digit, Even, Odd, NoParity, One, Parity, Pixel, Str, Zero}
import scala.collection.immutable

object Decoder {
    // TODO 1.1.1
    given char2Bit: Conversion[Char, Bit] with 
      def apply(x: Char): Bit = ???
    given int2Bit: Conversion[Int, Bit] with
      def apply(s: Int): Bit = ???

    // TODO 1.1.2
    extension(c:Bit)
      def complement: Bit = ???
    // TODO 1.1.3
    val LStrings: List[String] = List("0001101", "0011001", "0010011", "0111101", "0100011",
    "0110001", "0101111", "0111011", "0110111", "0001011")
    val leftOddList: List[List[Bit]] = Nil // codificări L
    val rightList: List[List[Bit]] = Nil // codificări R
    val leftEvenList: List[List[Bit]] = Nil // codificări  G
  
    // TODO 1.1.4
    extension[A](l: List[A])
      def groupedByEquality: List[List[A]] = ???
  
    // TODO 1.1.5
    def runLength[A](l: List[A]): List[(Int, A)] = ???
  
  case class RatioInt(n: Int, d: Int) extends Ordered[RatioInt] {
    require(d != 0, "Denominator cannot be zero")
    private val gcd = BigInt(n).gcd(BigInt(d)).toInt
    val a = n / gcd // numărător
    val b = d / gcd // numitor

    override def toString: String = s"$a/$b"

    override def equals(obj: Any): Boolean = obj match {
      case that: RatioInt => this.a.abs == that.a.abs &&
        this.b.abs == that.b.abs &&
        this.a.sign * this.b.sign == that.a.sign * that.b.sign
      case _ => false
    }
    //TODO 1.2.1
    def -(other: RatioInt) = ???

    def +(other: RatioInt) = ???

    def *(other: RatioInt) = ???

    def /(other: RatioInt) = ???

    //TODO 1.2.2
    def compare(other: RatioInt): Int = ???
  }

    // TODO 1.3.1
    def scaleToOne[A](l: List[(Int, A)]): List[(RatioInt, A)] = ???

    // TODO 1.3.2
    def scaledRunLength(l: List[(Int, Bit)]): (Bit, List[RatioInt]) = ???
  
    // TODO 1.3.3
    def toParities(s: Str): List[Parity] = ???
  
    // TODO 1.3.4
    val PStrings: List[String] = List("LLLLLL", "LLGLGG", "LLGGLG", "LLGGGL", "LGLLGG",
      "LGGLLG", "LGGGLL", "LGLGLG", "LGLGGL", "LGGLGL")
    val leftParityList: List[List[Parity]] = Nil

    // TODO 1.3.5
    type SRL = (Bit, List[RatioInt])
    val leftOddSRL:  List[SRL] = Nil
    val leftEvenSRL:  List[SRL] = Nil
    val rightSRL:  List[SRL] = Nil

    // TODO 1.4.1
    def distance(l1: SRL, l2: SRL): RatioInt = ???
  
    // TODO 1.4.2
    def bestMatch(SRL_Codes: List[SRL], digitCode: SRL): (RatioInt, Digit) = ???
  
    // TODO 1.4.3
    def bestLeft(digitCode: SRL): (Parity, Digit) = ???
  
    // TODO 1.4.4
    def bestRight(digitCode: SRL): (Parity, Digit) = ???
    
    def chunksOf[A](n: Int)(l: List[A]): List[List[A]] = {  
      def chunkWith[A](f: List[A] => (List[A], List[A]))(l: List[A]): List[List[A]] = {
      l match {
        case Nil => Nil
        case _ =>
          val (h, t) = f(l)
          h :: chunkWith(f)(t)
        }
      }
      chunkWith((l: List[A]) => l.splitAt(n))(l)
      }

    // TODO 1.4.5
    def findLast12Digits(rle:  List[(Int, Bit)]): List[(Parity, Digit)] = ???

    // TODO 1.4.6
    def firstDigit(l: List[(Parity, Digit)]): Option[Digit] = ???

    // TODO 1.4.7
    def checkDigit(l: List[Digit]): Digit = ???
  
    // TODO 1.4.8
    def verifyCode(code: List[(Parity, Digit)]): Option[String] = ???
  
    // TODO 1.4.9
    def solve(rle:  List[(Int, Bit)]): Option[String] = ???
    
    def checkRow(row: List[Pixel]): List[List[(Int, Bit)]] = {
      val rle = runLength(row);

      def condition(sl: List[(Int, Pixel)]): Boolean = {
        if (sl.isEmpty) false
        else if (sl.size < 59) false
        else sl.head._2 == 1 &&
          sl.head._1 == sl.drop(2).head._1 &&
          sl.drop(56).head._1 == sl.drop(58).head._1
      }

      rle.sliding(59, 1)
        .filter(condition)
        .toList
        .map(_.map(pair => (pair._1, pair._2)))
    }
}
