package gocaptcha

import (
  "crypto/rand"
  "math/big"
  "encoding/base64"
  "image"
  "image/color"
  "image/draw"
  "image/png"
  "io/ioutil"
  "strings"
  "bytes"
  "log"

  "golang.org/x/image/font"
  "golang.org/x/image/font/sfnt"
  "golang.org/x/image/math/fixed"
  "golang.org/x/image/font/opentype"
)

type Captcha struct {
  Text  string
  Image []byte
}

const (
  Latin    = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  Cyrillic = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя"
  Hiragana = "あいうえおかがきぎくぐけげこごさざしじすずせぜそぞただちぢつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもやゆよらりるれろわゐゑをん"
  Katakana = "アイウエオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂツヅテデトドナニヌネノハバパヒビピフビプヘベペホボポマミムメモヤユヨラリルレロワヰヱヲン"
  Kanji    = "一二三四五六七八九十口日月火水木金土曜上下中山川土空田天生花草虫犬人名女男子目耳口手足見音力気円入出立休先夕本文字学校村町森正水火玉王石礼羽老考者而也切友夕化外多夜太妹姉宀它安宅字守寺時尺少山工左川巾市帰年広弓引弟弱張強当形後心思戸才教数文新方日明星春昼月有朝木本東林森校桜橋正歩死母毛気水火父片牛犬王生田男矢石示禾米羊考耳聞肉自至舌船良色茶見貝赤走起足車辛辰辷邑部都里野金長門間阜隹雨青非面革韋音页風飛食首香馬高黒黽齐交会体作仕他代以低住使便借側停備働元兄光写冬切別力勉動区医去参商問図園土地場声売夏外多夜夢大天太女好妹姉始字守室家寒少専局屋山岩工市帰常平年広座式引弱強当形役後心必忘忙思急性息悪意愛感慣懐成我戦戸所手打投拾持指放整早映春昼昼暑曲有服期望未末本村果林森楽橋歌止歩死毎比氏民求池治法泳洋活流浴海消深温港湖湯漢火版然牛物特犬王珍理生産用田由甲申男疲白皮皿相看県真研私究究窓立答等箱節米粉約級終組経線羽考聞肉自船良色花茶行血表詩語読誰負財買走起足身転軽農近道野金長門開間関阪陽階集面頭題飲館駅験马高黒"
)

func GenerateCaptcha(fontPath, charset string, length int) (*Captcha, error) {
  text := genTxt(charset, length)

  fontBytes, err := ioutil.ReadFile(fontPath)
  if err != nil {
    return nil, err
  }

  f, err := sfnt.Parse(fontBytes)
  if err != nil {
    return nil, err
  }

  face, err := opentype.NewFace(f, &opentype.FaceOptions{
    Size:    24,
    DPI:     72,
    Hinting: font.HintingFull,
  })
  if err != nil {
    return nil, err
  }
  defer face.Close()

  img := image.NewRGBA(image.Rect(0, 0, 300, 100))
  bgColor := color.RGBA{0, 0, 0, 255}
  draw.Draw(img, img.Bounds(), &image.Uniform{bgColor}, image.Point{}, draw.Src)

  d := &font.Drawer{
    Dst:  img,
    Src:  image.NewUniform(color.RGBA{255, 255, 255, 255}),
    Face: face,
  }

  d.Dot = fixed.Point26_6{
    X: fixed.Int26_6(20 * 64),
    Y: fixed.Int26_6(50 * 64),
  }
  d.DrawString(text)

  imgBuffer := new(bytes.Buffer)
  err = png.Encode(imgBuffer, img)
  if err != nil {
    return nil, err
  }

  imgBytes := imgBuffer.Bytes()
  imgBase64Str := base64.StdEncoding.EncodeToString(imgBytes)

  return &Captcha{
    Text:  text,
    Image: []byte(imgBase64Str),
  }, nil
}

func VerifyCaptcha(input, captchaText string) bool {
  return strings.EqualFold(input, captchaText)
}

func genTxt(charset string, length int) string {
  runes := []rune(charset)
  text := make([]rune, length)
  for i := range text {
    randomIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(runes))))
    if err != nil {
      log.Fatal(err)
    }
    text[i] = runes[randomIndex.Int64()]
  }
  return string(text)
}