// package hc_score
package hc_score

import (
	"bufio"
	"fmt"
	"math"
	"os"
	"strconv"
	"strings"
)

type PtrScore func(string) float64
type PtrInit func(string)

var SCORE_INIT = map[string]PtrInit{
	"IC":  InitIC,
	"UNI": InitUni,
	"BIG": InitBig,
    "ADE": InitPlain,
    "TRI": InitTri,
    "QUA": InitQua,
}

var SCORE = map[string]PtrScore{
	"IC":  ScoreIC,
	"UNI": ScoreUni,
	"BIG": ScoreBig,
    "ADE": ScoreADE,
    "TRI": ScoreTri,
    "QUA": ScoreQua,
}

// probabilities of letters A,B,C... (65,66,67,...)
var lesUni [26]float64

// plain test
var lePlain string

// probabilities of bigrams (AA, AB, ... BA, BB, ..., ZZ)
var lesBig [26 * 26]float64

// probabilities of trigrams (AAA, ...CHK, .... ZZZ)
var lesTri [26 * 26 * 26]float64

// probabilities of quadrigrams 
var lesQua map[string]float64

// -- IC init
func InitIC(fic_nom string) {
}

// -- UNI init (The data read is already logarithms)
func InitUni(fic_nom string) {
	var racine string = os.Getenv("M209_FIC")
	if racine != "" {
		racine += "/"
	}
	var chemin string = racine + "STATS/" + fic_nom + ".uni"
	fichier, err := os.Open(chemin)
	if err != nil {
		fmt.Println("Error: Impossible to Open ", chemin)
		os.Exit(1)
	}
	defer fichier.Close()

	scanner := bufio.NewScanner(fichier)
	for scanner.Scan() {
		ch := scanner.Text()
		tb := strings.Split(ch, ",")
		letter_idx := tb[0][0] - 65
		x, _ := strconv.Atoi(strings.Trim(tb[1], " "))
		lesUni[letter_idx] = float64(x)
	}
}

func InitPlain(fic_nom string) {
	var racine string = os.Getenv("M209_FIC")
	if racine != "" {
		racine += "/"
	}
	var chemin string = racine + fic_nom 
	fichier, err := os.Open(chemin)
	if err != nil {
		fmt.Println("Error: Impossible to Open ", chemin)
		os.Exit(1)
    }
	defer fichier.Close()
	scanner := bufio.NewScanner(fichier)
    var leTexte []byte
	for scanner.Scan() {
		ch := strings.ToUpper( scanner.Text() )
        for i:=0; i<len(ch); i++ {
            car := ch[i]
            if car >= 'A' && car <= 'Z' { 
                leTexte = append(leTexte, car)
            }
        }
    }
    lePlain = string(leTexte)
}

// -- BIG init
func InitBig(fic_nom string) {
	var racine string = os.Getenv("M209_FIC")
	if racine != "" {
		racine += "/"
	}
	var chemin string = racine + "STATS/" + fic_nom + ".big"
	fichier, err := os.Open(chemin)
	if err != nil {
		fmt.Println("Error: Impossible to Open ", chemin)
		os.Exit(1)
	}
	defer fichier.Close()

    for i:=0; i<(26*26); i++ {
        lesBig[i] = 1.
    }
	scanner := bufio.NewScanner(fichier)
	for scanner.Scan() {
		ch := scanner.Text()
		tb := strings.Split(ch, ",")
		idx1 := int(tb[0][0] - 65)
		idx2 := int(tb[0][1] - 65)
		x, _ := strconv.Atoi(strings.Trim(tb[1], " "))
		lesBig[idx1*26+idx2] = math.Log(float64(x))
	}
}

// -- TRI init
func InitTri(fic_nom string) {
	var racine string = os.Getenv("M209_FIC")
	if racine != "" {
		racine += "/"
	}
	var chemin string = racine + "STATS/" + fic_nom + ".tri"
	fichier, err := os.Open(chemin)
	if err != nil {
		fmt.Println("Error: Impossible to Open ", chemin)
		os.Exit(1)
	}
	defer fichier.Close()

    for i:=0; i<(26*26*26); i++ {
        lesTri[i] = 1.
    }
	scanner := bufio.NewScanner(fichier)
	for scanner.Scan() {
		ch := scanner.Text()
		tb := strings.Split(ch, ",")
		idx1 := int(tb[0][0] - 65)
		idx2 := int(tb[0][1] - 65)
		idx3 := int(tb[0][2] - 65)
		x, _ := strconv.Atoi(strings.Trim(tb[1], " "))
		lesTri[idx1*26*26+idx2*26+idx3] = math.Log(float64(x))
    }
}

// -- QUA Init
func InitQua(fic_nom string) {
	var racine string = os.Getenv("M209_FIC")
	if racine != "" {
		racine += "/"
	}
	var chemin string = racine + "STATS/" + fic_nom + ".qua"
	fichier, err := os.Open(chemin)
	if err != nil {
		fmt.Println("Error: Impossible to Open ", chemin)
		os.Exit(1)
	}
    lesQua = make(map[string]float64)    

	scanner := bufio.NewScanner(fichier)
	for scanner.Scan() {
		ch := scanner.Text()
		tb := strings.Split(ch, ",")
		x, _ := strconv.Atoi(strings.Trim(tb[1], " "))
        lesQua[tb[0]] = math.Log(float64(x))
    }
}

//-------------------------------------------------------
// -- IC score
func ScoreIC(texte string) float64 {
	var f [26]int
	N := len(texte)
	for i := 0; i < N; i++ {
		idx := texte[i] - 65
		f[idx]++
	}
	var total int = 0
	for i := 0; i < 26; i++ {
		if f[i] > 1 {
			total += f[i] * (f[i] - 1)
		}
	}
	return float64(total) / float64(N*(N-1))
}

// -- UNI Score
func ScoreUni(texte string) float64 {
	var total float64 = 0
	var idx byte
    N := len(texte)

	for i := 0; i < N; i++ {
		idx = texte[i] - 65
		total += lesUni[idx]
	}
	return total /(100.  * float64(N))
}

// -- BIG Score
func ScoreBig(texte string) float64 {
	var total float64 = 0.
	var idx1, idx2 int

    N := len(texte)
	for i := 0; i < len(texte)-1; i++ {
		idx1 = int(texte[i] - 65)
		idx2 = int(texte[i+1] - 65)
		total += lesBig[idx1*26+idx2]
	}
	return total / float64(N)
}

// -- TRI Score
func ScoreTri(texte string) float64 {
	var total float64 = 0.
	var idx1, idx2, idx3 int

    N := len(texte)
	for i := 0; i < len(texte)-2; i++ {
		idx1 = int(texte[i] - 65)
		idx2 = int(texte[i+1] - 65)
		idx3 = int(texte[i+2] - 65)
		total += lesTri[idx1*26*26 + idx2*26 + idx3]
	}
	return total / float64(N)
}

// -- QUA Score
func ScoreQua(texte string) float64 {
    var total float64 = 0.
    var qua string

    N := len(texte)
    for i:= 0; i < len(texte)-3; i++ {
        qua = string(texte[i])+string(texte[i+1])
        qua += string(texte[i+2])+string(texte[i+3])
        valeur, existe := lesQua[qua]
        if existe {
            total += valeur
        }
    }
    return total / float64(N)
}

func ScoreADE(texte string) float64 {
    N := len(lePlain)
    maxds := N * 26
    for i:=0; i<N; i++ {
        x := 0
        if lePlain[i] > texte[i] { x = int(lePlain[i] - texte[i])
        } else { x = int(texte[i] - lePlain[i]) }
        if x > 13 { x = x - 13 }
        maxds -= int(x)
    }
    return float64(maxds) / float64(N * 26)
}

//--------------------------------------------------------
func GetPlain() string {
    return lePlain
}
//--------------------------------------------------------
func init() {
	fmt.Println("<<package hc_score initialized>>")
}
