// package manual: Initialize Pins and Lugs
// checks compliance with manuals
package manual

import "math/rand"

var Flag_skip_HC bool
var Flag_skip_SA bool
var REFERENCE_MANUAL string  // "" (No restriction, 1942, ...)

type PtrFunc func() ([21]int)
var InitLugs map[string]PtrFunc

type PtrFuncV func([21]int) (bool)
var VLugs map[string]PtrFuncV

// --- verify that the 26 shifts are possible
func TwentyFive(o [21]int) bool {
    var theShifts [64]int
    var possible [26]int
    var shift int
    var idx int

    for i:=0; i<2; i++ {
     for j:=0; j<2; j++ {
      for k:=0; k<2; k++ {
       for l:=0; l<2; l++ {
        for m:=0; m<2; m++ {
         for n:=0; n<2; n++ {
    shift = 0
    shift += o[0]  * i
    shift += o[1]  * j
    shift += o[2]  * k
    shift += o[3]  * l
    shift += o[4]  * m
    shift += o[5]  * n
    shift += o[6]  * (i | j)
    shift += o[7]  * (i | k)
    shift += o[8]  * (i | l)
    shift += o[9]  * (i | m)
    shift += o[10] * (i | n)
    shift += o[11] * (j | k)
    shift += o[12] * (j | l)
    shift += o[13] * (j | m)
    shift += o[14] * (j | n)
    shift += o[15] * (k | l)
    shift += o[16] * (k | m)
    shift += o[17] * (k | n)
    shift += o[18] * (l | m)
    shift += o[19] * (l | n)
    shift += o[20] * (m | n)              
    idx = i + j*2 + k*4 + l*8 + m*16 + n*32
    theShifts[ idx ] = shift
    possible[ shift % 26] = 1
         }
        }
       }
      }
     }
    }
    for i:=0; i<26; i++ {
        if possible[i] != 1 { return false }
    }
    return true
}

func initKeyPin() string {
    currentKeyPins := ""
    for i:=0; i< 131; i++ {
        if rand.Float64() > 0.5 {
            currentKeyPins += "1"
        } else {
            currentKeyPins += "0"
        }
    }
    return currentKeyPins
}
         

// --- Random initializes the pins 
func RandomPins(PINS string) ([131]byte) {
    var pins [131]byte
    if Flag_skip_SA {
        copy(pins[:],PINS)
    } else {
        copy(pins[:], initKeyPin())
    }
    return pins
}

// --- Random initializes the Lugs 
func RandomLugs(LUGS [21]int) ([21]int) {
    if Flag_skip_HC {
        return LUGS
    }
    return InitLugs[ REFERENCE_MANUAL ]()
}

// --- Verify if Lugs configuration is in compliance with Manual
func VerifyLugs(lugs [21]int) (bool) {
    return VLugs[ REFERENCE_MANUAL ](lugs)
}

// --- Almost no restriction on Lugs initialization
func InitLugsMin() ([21]int) {
    lugs := [21]int{}
    for {
        total := 0
        for i:= 0; i<6; i++ {
            lugs[i] = rand.Intn(12)+1
            total += lugs[i]
        } 
        if total <= 27 { break }
    }
    return lugs
}

// --- Initialize only lugs (without overlaps)
func InitLugsOnly() ([21]int) {
    lugs := [21]int{}
    for {
        nbOnes := 0
        total := 0
        for i:= 0; i<6; i++ {
            lugs[i] = rand.Intn(12)+1
            if lugs[i] == 1 { nbOnes ++ }
            total += lugs[i]
        }
        if (nbOnes > 0 && nbOnes < 3 ) && (total == 27) && TwentyFive(lugs) {
            break
        }
    }
    return lugs
}

// --- Initialization compliant with 1942 manual
func InitLugs1942() ([21]int) {
    lugs := [21]int{}
    for {
        total := 0
        totalOver := 0
        nbOnes := 0
        //twoPresent := false
        //threePresent := false
        for i:=0; i<21; i++ {
            if i <6 { 
                lugs[i] = rand.Intn(12)+1 
                if lugs[i] == 1 { nbOnes ++ }
                //if lugs[i] == 2 { twoPresent = true }
                //if lugs[i] == 3 { threePresent = true }
            } else {
                if rand.Float64() > 0.7 {
                    lugs[i] = rand.Intn(4)
                    totalOver ++
                }
            }
            total += lugs[i]
        }
        //if ((nbOnes == 2 && threePresent) || (nbOnes == 1 && twoPresent)) &&
        //if (nbOnes >= 1 && nbOnes <= 2 ) &&
        //   (total == 27 ) && (totalOver >= 1 && totalOver <= 12 ) && 
        //   TwentyFive(lugs) {
        if (nbOnes >= 1 && nbOnes <=2) && (total == 27) && TwentyFive(lugs) {
            break
        }
    }
    return lugs
}


func nbLugsFrontW(lugs [21]int) ([6]int) {
    var lugsFrontWheels [6]int  

    for idx := 0; idx<21; idx++ {
        switch {
        case idx > 0 && idx < 6 :
            lugsFrontWheels[idx] += lugs[idx]
        case idx == 6:
            lugsFrontWheels[0] += lugs[idx]
            lugsFrontWheels[1] += lugs[idx]
        case idx == 7:
            lugsFrontWheels[0] += lugs[idx]
            lugsFrontWheels[2] += lugs[idx]
        case idx == 8:
            lugsFrontWheels[0] += lugs[idx]
            lugsFrontWheels[3] += lugs[idx]
        case idx == 9:
            lugsFrontWheels[0] += lugs[idx]
            lugsFrontWheels[4] += lugs[idx]
        case idx == 10:
            lugsFrontWheels[0] += lugs[idx]
            lugsFrontWheels[5] += lugs[idx]
        case idx == 11:
            lugsFrontWheels[1] += lugs[idx]
            lugsFrontWheels[2] += lugs[idx]
        case idx == 12:
            lugsFrontWheels[1] += lugs[idx]
            lugsFrontWheels[3] += lugs[idx]
        case idx == 13:
            lugsFrontWheels[1] += lugs[idx]
            lugsFrontWheels[4] += lugs[idx]
        case idx == 14:
            lugsFrontWheels[1] += lugs[idx]
            lugsFrontWheels[5] += lugs[idx]
        case idx == 15:
            lugsFrontWheels[2] += lugs[idx]
            lugsFrontWheels[3] += lugs[idx]
        case idx == 16:
            lugsFrontWheels[2] += lugs[idx]
            lugsFrontWheels[4] += lugs[idx]
        case idx == 17:
            lugsFrontWheels[2] += lugs[idx]
            lugsFrontWheels[5] += lugs[idx]
        case idx == 18:
            lugsFrontWheels[3] += lugs[idx]
            lugsFrontWheels[4] += lugs[idx]
        case idx == 19:
            lugsFrontWheels[3] += lugs[idx]
            lugsFrontWheels[5] += lugs[idx]
        case idx == 20:
            lugsFrontWheels[4] += lugs[idx]
            lugsFrontWheels[5] += lugs[idx]
        }
    }
    return lugsFrontWheels
}

// --- functions which verify the compliance with manual
func VLugsMin(lugs [21]int) (bool) { return true }
func VLugsOnly(lugs [21]int) (bool) { return true }

// --- This function is bugged: 2minutes for its execution !!!!!
func VLugs1942(lugs [21]int) (bool) {
    if ! TwentyFive(lugs) { return false }

    lugsFrontWheels := nbLugsFrontW( lugs )

    nbOnes := 0

    for i:=0; i<6; i++ {
        x := lugsFrontWheels[i] 
        if x < 1 || x > 13 { return false }
        if x == 1 { nbOnes ++ }
    }
    total := 0
    for i:=0; i<21; i++ {
        total += lugs[i]
    }
    if (total < 27 || total > 39) { return false }
    if ! (nbOnes >= 1 && nbOnes <= 2) { return false }
    return true
}

func InitRandom(seed int) {
    rand.Seed(int64(seed))
}

func init() {

    InitLugs = map[string]PtrFunc{
        "":       InitLugsMin,    // (Almost) no restriction
        "1":      InitLugsOnly,   // Only Lugs (without overlaps), 27 bars
        "1942":   InitLugs1942,   // Compliant with 1942 manual
    }

    VLugs = map[string]PtrFuncV{
        "":       VLugsMin,    // (Almost) no restriction
        "1":      VLugsOnly,   // Only Lugs (without overlaps), 27 bars
        "1942":   VLugs1942,   // Compliant with 1942 manual
    }

} 
