// package http_arch_doc -- HTTP-API для ArchDoc
package http_arch_doc

import (
	"bytes"
	_ "embed"
	"fmt"

	svg "github.com/ajstarks/svgo"
	"github.com/gofiber/fiber/v2"
	"gitp78su.ipnodns.ru/svi/kern/v3"
	"gopkg.in/yaml.v3"

	"gitp78su.ipnodns.ru/svi/goarch/lev0/cons"
	"gitp78su.ipnodns.ru/svi/goarch/lev0/types"
	"gitp78su.ipnodns.ru/svi/goarch/lev1/canvas"
)

// HttpArchDoc -- HTTP-API для ArchDoc
type HttpArchDoc struct {
	oldBinArch []byte           // Кеш старой архитектуры
	diaMode    string           // Режим диаграммы
	size       types.IViewSize  // Размер холста в его координатах
	pos        types.IViewCoord // Координаты холста
	mapDrawer  types.IViewTree  // Словарь отрисовщиков
}

// NewHttpArchDoc -- создание нового экземпляра HTTP-API для ArchDoc
func NewHttpArchDoc() *HttpArchDoc {
	sf := &HttpArchDoc{
		diaMode: cons.ModeUseCase,
	}
	sHttp := kern.GetKernelServerHttp()
	fibApp := sHttp.Fiber()
	fibApp.Post("/api/arch_doc/parse", sf.postArchDocParse)
	return sf
}

// FormArch -- извлекает данные из текстового поля для обновления
type FormArch struct {
	StrData string `form:"text"`
}

// По запросу парсит, что получилось -- обновляет архитектуру
func (sf *HttpArchDoc) postArchDocParse(ctx *fiber.Ctx) error {
	form := &FormArch{}
	if err := ctx.BodyParser(form); err != nil {
		return ctx.SendString(fmt.Sprintf("HttpArchDoc.postArchDocParse(): in BodyParser, err=<br>%v", err))
	}
	binData := []byte(form.StrData)
	if len(binData) == len(sf.oldBinArch) {
		return ctx.Send(sf.oldBinArch)
	}
	mapElem := []map[string]interface{}{}
	err := yaml.Unmarshal(binData, &mapElem)
	if err != nil {
		return ctx.SendString(fmt.Sprintf("HttpArchDoc.postArchDocParse(): in Decode JSON, err=<br>%v", err))
	}
	wr := bytes.NewBuffer(nil)
	canvas, err := sf.getCanvas(wr, mapElem)
	if err != nil {
		return ctx.SendString(fmt.Sprintf("HttpArchDoc.postArchDocParse(): in create canvas, err=<br>%v", err))
	}
	switch sf.diaMode {
	case cons.ModeUseCase: // режим вариантов использования
		if err := sf.useCase(canvas); err != nil {
			return ctx.SendString(fmt.Sprintf("HttpArchDoc.postArchDocParse(): in useCase, err=<br>%v", err))
		}
	default: // неизвестный режим
		return ctx.SendString(fmt.Sprintf("HttpArchDoc.postArchDocParse(): unknown mode=%q", sf.diaMode))
	}

	canvas.End()
	binData = wr.Bytes()
	return ctx.Send(binData)
}

// Возвращает новый холст
func (sf *HttpArchDoc) getCanvas(wr *bytes.Buffer, lstElem []map[string]interface{}) (*svg.SVG, error) {
	var (
		elem         map[string]interface{}
		isFind, isOk bool
		_section     interface{}
	)
	for _, elem = range lstElem {
		_section, isOk = elem["type"]
		if !isOk {
			continue
		}
		section, isOk := _section.(string)
		if !isOk {
			return nil, fmt.Errorf("ServHttp.canvas(): in mapElem, type(%#+v) not string<br>%#+v", _section, elem)
		}
		if section == cons.SectionConfig {
			isFind = true
			break
		}
	}
	if !isFind {
		return nil, fmt.Errorf("ServHttp.canvas(): in mapElem, config not found")
	}
	confCanvas := canvas.NewCanvas(sf.diaMode, sf.size, sf.pos)
	sizeX, sizeY := confCanvas.Size().Int()
	canvas := svg.New(wr)
	canvas.Start(sizeX, sizeY)
	confCanvas.Draw(canvas)
	return canvas, nil
}

// Формирует варианты использования
func (sf *HttpArchDoc) useCase(canvas *svg.SVG) error {
	for _, drawer := range sf.mapDrawer.TreeView() {
		drawer.Draw(canvas)
	}
	return nil
}
