package main

// Kalici Everything sureci. Everything eklentisi acilista bir kez baslatilir ve stdin/stdout uzerinden
// isteklere cevap verir. Her istek bir kimlik (id) tasir; cevap ayni kimlikle eslesir. Bu sayede
// dosya simgeleri ve onizlemeler surec icindeki onbellekte kalir.
// Surec yoksa ya da cevap alinamazsa tek seferlik calistirmaya (eklentiCalistir) dusulur.

import (
	"encoding/json"
	"errors"
	"sync"
	"time"
)

const kaliciZamanAsimi = 10 * time.Second

var (
	kaliciMu       sync.Mutex
	kaliciBekleyen = map[string]chan eklentiCikti{}
)

// surecOlayYonlendir: surec satirlarini dagitir. Istek cevabiysa ilgili bekleyene iletilir;
// degilse (olay) eski yolla web'e gider.
func surecOlayYonlendir(anahtar string, satir []byte) {
	if kaliciYanitIsle(anahtar, satir) {
		return
	}
	eklentiOlayIlet(anahtar, satir)
}

// kaliciYanitIsle: satir bir istek cevabiysa ilgili bekleyene iletir ve true doner.
func kaliciYanitIsle(anahtar string, satir []byte) bool {
	if anahtar != everythingPaketAnahtari {
		return false
	}
	var y struct {
		ID    string          `json:"id"`
		Tamam bool            `json:"tamam"`
		Veri  json.RawMessage `json:"veri"`
		Hata  string          `json:"hata"`
	}
	if err := json.Unmarshal(satir, &y); err != nil || y.ID == "" {
		return false
	}
	kaliciMu.Lock()
	ch, ok := kaliciBekleyen[y.ID]
	if ok {
		delete(kaliciBekleyen, y.ID)
	}
	kaliciMu.Unlock()
	if ok {
		ch <- eklentiCikti{Tamam: y.Tamam, Veri: y.Veri, Hata: y.Hata}
	}
	return true
}

// kaliciBaslat: Everything surecini dogrulanmis yoldan baslatir.
func kaliciBaslat(yol string) error {
	if err := eklentiYoluGecerliMi(yol); err != nil {
		return err
	}
	if err := surecYonetici.Baslat(everythingPaketAnahtari, yol); err != nil {
		return err
	}
	olayLog("everything kalici surec baslatildi")
	return nil
}

// kaliciAra: kalici surece "ara" istegi yollar ve cevabi bekler. Surec yoksa once baslatir.
func kaliciAra(yol string, veri json.RawMessage, ayar json.RawMessage) (eklentiCikti, error) {
	var bos eklentiCikti
	if !surecYonetici.Calisiyor(everythingPaketAnahtari) {
		if err := kaliciBaslat(yol); err != nil {
			return bos, err
		}
	}
	id, err := rastgeleHex(8)
	if err != nil {
		return bos, err
	}
	ch := make(chan eklentiCikti, 1)
	kaliciMu.Lock()
	kaliciBekleyen[id] = ch
	kaliciMu.Unlock()
	defer func() {
		kaliciMu.Lock()
		delete(kaliciBekleyen, id)
		kaliciMu.Unlock()
	}()

	istek := map[string]any{"id": id, "islem": "ara", "veri": veri}
	if ayar != nil {
		istek["ayar"] = ayar
	}
	if err := surecYonetici.Gonder(everythingPaketAnahtari, istek); err != nil {
		return bos, err
	}
	select {
	case c := <-ch:
		return c, nil
	case <-time.After(kaliciZamanAsimi):
		return bos, errors.New("kalici surec zaman asimina ugradi")
	}
}

// eklentiAra: "ara" istegini calistirir. Everything icin kalici surec denenir; olmazsa tek seferlik calistirma.
// Cagiran, eklentinin kurulu ve etkin oldugunu once dogrulamis olmalidir.
func eklentiAra(anahtar, yol string, veri json.RawMessage, ayar json.RawMessage) (eklentiCikti, error) {
	if anahtar == everythingPaketAnahtari && kuruluEtkinMi(anahtar) {
		c, err := kaliciAra(yol, veri, ayar)
		if err == nil {
			return c, nil
		}
		olayLog("everything kalici sorgu basarisiz, tek seferlik deneniyor: %v", err)
	}
	return eklentiCalistir(yol, nil, eklentiGirdi{Islem: "ara", Veri: veri, Ayar: ayar}, calistirZamanAsimi)
}
