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()

	header(wSort, pf)
	pf.syncLock(wSort)

	fmt.Fprintf(wSort, "// START SORT\n")
	fmt.Fprintf(wSort, "\n")

	log.Printf("START ITERATORS\n")
	// add iterators for all the structs to be used
	for i, msg := range pf.allMsg() {
		PARENT := msg.Name
		LOCK := msg.Lockname

		funcdef := newIter(wSort, msg)
		log.Printf("ITER: %-2d %20s %20s %20s %20s %s\n", i, PARENT, "", "", LOCK, funcdef)
	}
	log.Printf("END ITERATORS\n")
	log.Printf("\n")

	log.Printf("START COMPARE\n")
	for i, msg := range pf.allMsg() {
		PARENT := msg.Name
		for _, v := range msg.Vars {
			if !v.HasSort {
				continue
			}
			if v.IsRepeated {
				// can't work against slices
				continue
			}
			VARNAME := v.VarName
			funcdef := newSortType(wSort, PARENT, VARNAME)
			log.Printf("TYPE: %-2d %20s %20s %20s %10s %s\n", i, PARENT, "", "", "", funcdef)
		}
	}
	log.Printf("END COMPARE\n")
	log.Printf("\n")

	// add selectAll() functions for all the sort combinations that will be used
	for i, s := range pf.ToSort {
		// log.Printf("SORT: %-2d %20s %20s %20s %20s\n", i, s.MsgName, s.VarType, s.VarName, s.Lockname)
		FRUIT := s.MsgName
		APPLE := s.VarType
		APPLES := s.VarName
		LOCK := s.Lockname
		funcdef := newIterAll(wSort, FRUIT, APPLE, APPLES, LOCK)
		log.Printf("JUNK: %-2d %20s %20s %20s %20s %s\n", i, FRUIT, APPLE, "", LOCK, funcdef)
	}
	log.Printf("\n")

	log.Printf("START SELECT\n")
	// make the sort iterators selectAll()
	for i, s := range pf.ToSort {
		PARENT := s.MsgName
		CHILD := s.VarType
		VARNAME := s.VarName
		LOCK := s.Lockname

		funcdef := addSelectAll(wSort, PARENT, CHILD, VARNAME, LOCK)
		log.Printf("SORT: %-2d %20s %20s %20s %20s %s\n", i, PARENT, CHILD, VARNAME, LOCK, funcdef)
	}
	log.Printf("END SELECT\n")
	log.Printf("\n")

	log.Printf("START SORT\n")
	// make the SortBy() functions
	for i, s := range pf.ToSort {
		// var funcname string
		PARENT := s.MsgName
		CHILD := s.VarType
		VARNAME := s.VarName

		log.Printf("SORT: %-2d %20s %20s %20s %20s %s\n", i, PARENT, CHILD, VARNAME, "", "")

		msg := pf.findMsg(s.VarType)
		if msg == nil {
			return fmt.Errorf("failed to find struct %s", s.VarType)
		}

		var FUNCTYPE string
		if PARENT == VARNAME {
			FUNCTYPE = PARENT
		} else {
			FUNCTYPE = VARNAME
		}

		for _, v := range msg.Vars {
			if v.IsRepeated {
				// can't work against slices
				continue
			}
			if v.HasSort {
				// funcname := "func (x *" + FUNCTYPE + ") SortBy" + v.VarName + "(" + v.VarType + ") *[]iter" + s.VarType
				// log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "fix", "", "", funcname)
				// funcdef := "func (x *"+FRUIT+") SortBy"+COLOR+"() *"+APPLE+"Iterator"

				if v.VarType == "string" {
					var sortby string
					// func newSortBy(w io.Writer, STRUCT, ITER, SORTNAME, SORTBY, SELECT string) string {
					if PARENT == VARNAME {
						sortby = "SortBy" + v.VarName
					} else {
						sortby = "Sort" + VARNAME + "By" + v.VarName
					}
					sortname := s.VarType + v.VarName
					selectName := "selectAll" + VARNAME
					funcdef := newSortBy(wSort, PARENT, s.VarType, sortname, sortby, selectName)
					log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcdef)
				} else {
					funcname := "func (x *" + FUNCTYPE + ") SortBy" + v.VarName + "(" + v.VarType + ") *[]iter" + s.VarType + " # can not do this yet"
					log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname)
				}
			}
		}
	}
	log.Printf("END SORT\n")
	log.Printf("\n")

	fmt.Fprintf(wSort, "\n")
	fmt.Fprintf(wSort, "// END SORT\n")

	// make Len()
	for _, msg := range pf.allMsg() {
		PARENT := msg.Name
		LOCK := msg.Lockname

		for i, v := range msg.Vars {
			if v.IsRepeated {
				CHILD := v.VarType
				VARNAME := v.VarName
				// funcdef := "func (x *" + PARENT + ") Len" + VARNAME + "() int " + CHILD + " name:" + VARNAME

				if PARENT == VARNAME {
					// special case
					funcdef := addLenFunc(wSort, PARENT, VARNAME, LOCK) // + " " + v.VarType + " name:" + v.VarName
					funcdef += "  # is special struct=varname"
					log.Printf("LEN:  %-2d %20s %20s %20s %20s %s\n", i, PARENT, CHILD, VARNAME, LOCK, funcdef)
				}
			}
		}
	}
	log.Printf("\n")

	// add All()
	for _, s := range pf.ToSort {
		PARENT := s.MsgName
		CHILD := s.VarType
		VARNAME := s.VarName

		if PARENT == VARNAME {
			funcdef := addAllFunc(wSort, PARENT, CHILD, VARNAME)
			log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "done", "", funcdef)
		}

		/*
			// todo: figure out how to do more All() functions correction
			var FUNCTYPE string
			if PARENT == VARNAME {
				FUNCTYPE = PARENT
			} else {
				FUNCTYPE = VARNAME
			}

			if s.VarType+"s" == s.VarName {
				funcname = "func (x *" + FUNCTYPE + ") All() *[]iter" + s.VarType
			} else {
				funcname = "func (x *" + FUNCTYPE + ") all" + s.VarName + "() *[]iter" + s.VarType
			}
			log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname)
		*/
	}

	// make delete()
	/*
		for _, msg := range pf.allMsg() {
			PARENT := msg.Name

			for _, v := range msg.Vars {
				if !v.HasUnique {
					continue
				}
				funcname := "func (x *" + PARENT + ") Delete" + msg.Name + "By" + v.VarName + "(" + v.VarType + ") bool"
				log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname)
				FRUIT := PARENT
				APPLES := msg.Name
				APPLE := v.VarName
				COLOR := v.VarType
				FUNCNAME := "Delete" + msg.Name + "By" + v.VarName

				var funcdef string
				if argv.Delete {
					funcdef = msg.deleteByWithCopy(wSort, FRUIT, APPLES, APPLE, COLOR, FUNCNAME)
				} else {
					funcdef = msg.deleteBy(wSort, FRUIT, APPLES, APPLE, COLOR, FUNCNAME)
				}
				log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "done", "", funcdef)
			}
		}
	*/

	// add Find() Delete() Append() Insert()
	log.Printf("      %-2s %20s %20s %20s %20s\n", "", "PARENT STRUCT", "VAR STRUCT TYPE", "VAR NAME", "LOCK")
	for i, s := range pf.ToSort {
		var funcname string
		PARENT := s.MsgName
		CHILD := s.VarType
		VARNAME := s.VarName
		LOCK := s.Lockname

		log.Printf("SORT: %-2d %20s %20s %20s %20s %s\n", i, PARENT, CHILD, VARNAME, LOCK, "")

		msg := pf.findMsg(s.VarType)
		if msg == nil {
			return fmt.Errorf("failed to find struct %s", s.VarType)
		}

		var ucount int
		// append() functions
		for _, v := range msg.Vars {
			if v.IsRepeated {
				continue
			}
			if !v.HasUnique {
				continue
			}
			ucount += 1
			// funcname := "func (x *" + PARENT + ") Append" + VARNAME + "By" + v.VarName + "(" + v.VarType + ")"
			// log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname)
			// func (msg *MsgName) addAppendBy(w io.Writer, STRUCT, FUNCNAME, VARNAME, CHILD, COLOR string) string {
			// func (msg *MsgName) addAppendBy(w io.Writer, STRUCT, FUNCNAME, STRUCTVAR, VARNAME, VARTYPE string) string {
			FUNCNAME := "Append" + VARNAME + "By" + v.VarName
			funcdef := msg.addAppendBy(wSort, PARENT, FUNCNAME, VARNAME, v.VarName, s.VarType)
			log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "done", "", funcdef)
		}

		/*
			// delete() functions
			for _, v := range msg.Vars {
				if !v.HasUnique {
					continue
				}
				funcname := "func (x *" + PARENT + ") Delete" + VARNAME + "By" + v.VarName + "(" + v.VarType + ") bool"
				log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname)
				FRUIT := PARENT
				APPLES := VARNAME
				APPLE := v.VarName
				COLOR := s.VarType
				if argv.Delete {
					msg.deleteByWithCopy(wSort, FRUIT, APPLES, APPLE, COLOR)
				} else {
					msg.deleteBy(wSort, FRUIT, APPLES, APPLE, COLOR)
				}
			}
		*/

		for _, v := range msg.Vars {
			if v.HasUnique {
				funcname = "func (x *" + PARENT + ") Find" + VARNAME + "By" + v.VarName + "(a " + v.VarType + ") *" + s.VarType + "(using" + v.VarName + ")"
				log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname)
			} else {
				if v.VarType == "string" {
					funcname = "func (x *" + PARENT + ") Find" + VARNAME + "By" + v.VarName + "(a string) []*" + s.VarType + "  ???"
					log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname)
				}
			}
		}
		if ucount == 1 {
			for _, v := range msg.Vars {
				if !v.HasUnique {
					continue
				}
				funcname = "func (x *" + PARENT + ") Insert(a *" + v.VarType + ") (*" + CHILD + ", isNew bool)"
				log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname)
			}
		}
		if ucount > 1 {
			funcname = "func (x *" + PARENT + ") Insert(a *" + CHILD + ") (*" + CHILD + ", isNew bool)"
			log.Printf("      %-2s %20s %20s %20s %s %s\n", "", "", "", "", "", funcname)
		}

	}
	return nil
}

func (pf *File) findMsg(s string) *MsgName {
	if pf.Bases.Name == s {
		return pf.Bases
	}
	if pf.Base.Name == s {
		return pf.Base
	}
	for _, msg := range pf.MsgNames {
		if msg.Name == s {
			return msg
		}
	}
	return nil
}

func (pf *File) allMsg() []*MsgName {
	var all []*MsgName
	all = append(all, pf.Bases)
	all = append(all, pf.Base)
	for _, msg := range pf.MsgNames {
		all = append(all, msg)
	}
	return all
}

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(parent *MsgName, wSort, wFind io.Writer) error {
	var FRUIT string = cases.Title(language.English, cases.NoLower).String(parent.Name)
	var LOCK string = parent.Lockname

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

	for _, v := range parent.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, LOCK)
		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, LOCK string) {
	for _, v := range parent.Vars {
		if v.HasUnique {
			var COLOR string = cases.Title(language.English, cases.NoLower).String(v.VarName)
			log.Printf("\tFIND: (x %s) FindBy%s(string) *%s\n", FRUIT, COLOR, APPLE)
			generateFindBy(w, FRUIT, APPLES, APPLE, COLOR, LOCK)
		}
	}
}

/*
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("\tDELETE: (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("\tINSERT: (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("\tAPPEND: (x %s) AppendUniqueBy%s(%s)\n", FRUIT, COLOR, APPLE)
			parent.appendUniqueCOLOR(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)
		}
	}
}