// m209.go: v3 : a small package which simulates M-209 for Key Breaking
// --------------------------------------------------------------------
package m209

import (
    "fmt"
    "os"
)

type M209 struct {
    wheels     [6][]int
    nbOfWheels int
    nbOfPins   int
    lenWheels  [6]int
    lugsOvers  [21]int
    theShifts  [64]int
}

func NewM209() M209 {
    var e M209

    e.nbOfWheels = 6
    e.nbOfPins  = 131
    e.lenWheels = [6]int{26,25,23,21,19,17}

    for i:=0; i< e.nbOfWheels; i++ {
        e.wheels[i] = make([]int, e.lenWheels[i])
    }
    return e
}

func (this *M209) InitShifts() {
    var o = this.lugsOvers
    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
    this.theShifts[ idx ] = shift
         }
        }
       }
      }
     }
    }
}

func (this *M209) SetLugsOvers(lesLO [21]int) {
    total := 0
    for i:=0; i<21; i++ {
        total += lesLO[i]
    }
    if total != 27 {
        fmt.Println("ERROR: you must use all the 27 bars")
        os.Exit(1)
    }
    this.lugsOvers = lesLO
    this.InitShifts()
}

func (e *M209) SetAllPins(pins string) {
    var debut, fin int
    var partie string

    debut = 0
    for i:=0; i<e.nbOfWheels; i++ {
        fin = debut + e.lenWheels[i]
        partie = pins[debut:fin]
        for j:=0; j< e.lenWheels[i]; j++ {
            e.wheels[i][j] = int(partie[j]-'0')
        }
        debut = fin
    }
}

func (e M209) Info() string {
    var ch string = ""

    ch += fmt.Sprintf("Number of Wheels: %3d\n", e.nbOfWheels)
    ch += fmt.Sprintf("Lugs and Overlaps: ")
    for i:=0; i<len(e.lugsOvers); i++ {
        ch += fmt.Sprintf("%2d", e.lugsOvers[i])
    }
    ch += "\n"
    for i:=0; i< e.nbOfWheels; i++ {
        ch += fmt.Sprintf("%2d: (%2d) - ", i, len(e.wheels[i]))
        for j:=0 ; j<len(e.wheels[i]); j++ {
            ch += fmt.Sprintf("%1d", e.wheels[i][j])
        }
        ch += fmt.Sprintf("\n")
    }
    ch += "----------------------"
    return ch
}

// --- Cipher(): decrypt a cryptogram (or crypt a plain text)
func Cipher(e  *M209, message string) string {
    var ch []byte
    var P,C,K, aBit, idx int // Plain, Crypto, Key

    for i := 0; i < len(message); i++ {
        K = 0 ; idx = 0
        for j:=0; j< e.nbOfWheels; j++ {
            aBit = e.wheels[j][i % e.lenWheels[j] ]
            if aBit == 1 {
                switch j {
                case 0:
                    idx += 1
                case 1:
                    idx += 2
                case 2:
                    idx += 4
                case 3:
                    idx += 8
                case 4:
                    idx += 16
                case 5:
                    idx += 32
                }
            }   
        }
        K = e.theShifts[idx] 
        P = int( message[i] - 'A')
        C = (25 + K - P) % 26
        ch = append(ch, byte(C) + 'A')
    }
	return string(ch)
}

