package main

import (
	"fmt"
	"io"
	"os"

	"go.wit.com/log"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
)

// this file is named poorly. It has more than Sort()

func (pb *Files) makeNewSortfile(pf *File) error {
	wSort, _ := os.OpenFile(pf.Filebase+".sort.pb.go", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	defer wSort.Close()
	wFind, _ := os.OpenFile(pf.Filebase+".find.pb.go", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	defer wFind.Close()

	header(wSort, pf)

	pf.Base.iterTop(wSort)
	pf.Base.iterNext(wSort)
	pf.selectAllFunc(wSort)
	pf.iterSelect(wSort)

	pf.sortByFunc(wSort)

	/*
		pf.appendUnique(wFind) // Append() enforce no unique keys
		if argv.Delete {
			pf.deleteWithCopyFunc(wFind)
		} else {
			pf.deleteFunc(wFind)
		}
		pf.findFunc(wFind)
	*/

	header(wFind, pf)
	pf.syncLock(wFind)
	pf.specialBases(wFind)

	// attempt to add sort functions for pf.Base
	if err := pf.processMessage(pf.Bases, wSort, wFind); err != nil {
		return err
	}
	if err := pf.processMessage(pf.Base, wSort, wFind); err != nil {
		return err
	}
	return nil
}

func (pf *File) specialBases(wFind io.Writer) {
	var FRUIT string = cases.Title(language.English, cases.NoLower).String(pf.Bases.Name)
	var APPLES string = cases.Title(language.English, cases.NoLower).String(pf.Bases.Name)
	var APPLE string = cases.Title(language.English, cases.NoLower).String(pf.Base.Name)

	pf.Bases.simpleAppend(wFind, FRUIT, APPLES, APPLE)

}

// to simplify being able to read the code, FRUIT, APPLES and APPLE are used
// FRUIT == the string name of the message in the protobuf file
// APPLE == the type of the repeated variable
// APPLES == the variable name of the repeated struct
func (pf *File) processMessage(msg *MsgName, wSort, wFind io.Writer) error {
	var FRUIT string = cases.Title(language.English, cases.NoLower).String(msg.Name)

	log.Printf("Generating functions for %s\n", FRUIT)

	for _, v := range msg.Vars {
		if !v.IsRepeated {
			log.Printf("\tSKIP %s %s\n", v.VarName, v.VarType)
			continue
		}
		log.Printf("\tFOUND REPEATED %s %s\n", v.VarName, v.VarType)
		// use easier to read variable names APPLE and APPLES in the code
		var APPLE string = v.VarType
		var APPLES string = cases.Title(language.English, cases.NoLower).String(v.VarName)

		// try and find the message struct for APPLE
		var found *MsgName
		if pf.Base.Name == APPLE {
			found = pf.Base
		}
		for _, m := range pf.MsgNames {
			if m.Name == APPLE {
				found = m
				break
			}
		}
		if found == nil {
			return fmt.Errorf("failed to find struct %s", APPLE)
		}

		log.Printf("FOUND: %s %s for %s\n", APPLES, APPLE, FRUIT)

		found.addFindByMsg(wFind, FRUIT, APPLES, APPLE)
		found.addAppendByMsg(wFind, FRUIT, APPLES, APPLE)
		found.addDeleteByMsg(wFind, FRUIT, APPLES, APPLE)
		found.addInsertByMsg(wFind, FRUIT, APPLES, APPLE) // new idea

		found.addSortByMsg(wSort, FRUIT, APPLES, APPLE)
	}
	return nil
}

func (parent *MsgName) addFindByMsg(w io.Writer, FRUIT, APPLES, APPLE string) {
	log.Printf("\tINSERT: %s %s for %s\n", APPLES, APPLE, FRUIT)
	for _, v := range parent.Vars {
		if v.HasUnique {
			var COLOR string = cases.Title(language.English, cases.NoLower).String(v.VarName)
			log.Printf("\t\t(x %s) InsertBy%s(string) *%s\n", FRUIT, COLOR, APPLE)
			parent.findBy(w, FRUIT, APPLES, APPLE, COLOR)
		}
	}
}

func (parent *MsgName) addDeleteByMsg(w io.Writer, FRUIT, APPLES, APPLE string) {
	log.Printf("\tDELETE: %s %s for %s\n", APPLES, APPLE, FRUIT)
	var COLORS []string
	for _, v := range parent.Vars {
		if !v.HasUnique {
			continue
		}
		var COLOR string = cases.Title(language.English, cases.NoLower).String(v.VarName)
		COLORS = append(COLORS, COLOR)

		log.Printf("\t\t(x %s) DeleteBy%s(string) *%s\n", FRUIT, COLOR, APPLE)
		if argv.Delete {
			parent.deleteByWithCopy(w, FRUIT, APPLES, APPLE, COLOR)
		} else {
			parent.deleteBy(w, FRUIT, APPLES, APPLE, COLOR)
		}
	}
}

func (parent *MsgName) addInsertByMsg(w io.Writer, FRUIT, APPLES, APPLE string) {
	log.Printf("\tINSERT: %s %s for %s\n", APPLES, APPLE, FRUIT)
	for _, v := range parent.Vars {
		if v.HasUnique {
			var COLOR string = cases.Title(language.English, cases.NoLower).String(v.VarName)
			log.Printf("\t\t(x %s) InsertBy%s(string) *%s\n", FRUIT, COLOR, APPLE)
			parent.insertBy(w, FRUIT, APPLES, APPLE, COLOR)
		}
	}
}

func (parent *MsgName) addAppendByMsg(w io.Writer, FRUIT, APPLES, APPLE string) {
	log.Printf("\tAPPEND!: %s %s for %s\n", APPLES, APPLE, FRUIT)
	var COLORS []string

	for _, v := range parent.Vars {
		if v.HasUnique {
			var COLOR string = cases.Title(language.English, cases.NoLower).String(v.VarName)
			COLORS = append(COLORS, COLOR)

			log.Printf("\t\t(x %s) AppendUniqueBy%s(%s)\n", FRUIT, COLOR, APPLE)
			parent.appendUniqueBy(w, FRUIT, APPLES, APPLE, COLOR)
		}
	}
	if len(COLORS) > 0 {
		parent.appendUnique(w, FRUIT, APPLES, APPLE, COLORS)
	}
}

func (parent *MsgName) addSortByMsg(w io.Writer, FRUIT, APPLES, APPLE string) {
	log.Printf("\tSORT!: %s %s for %s\n", APPLES, APPLE, FRUIT)

	for _, v := range parent.Vars {
		if v.HasSort {
			// log.Printf("\tSort!: %s %s for %s\n", APPLES, APPLE, v.VarName)
			newS := cases.Title(language.English, cases.NoLower).String(v.VarName)
			log.Printf("\t\t(x %s) SortdBy%s() *%sIter\n", FRUIT, newS, APPLE)
		}
	}
}