// AttainmentGroups produces a page with a summary of the
// attainment of each subgroup
func AttainmentGroups(e env.Env) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		if redir := checkRedirect(e, w, r, 1); redir {
			return
		}
		header(e, w, r, 1)
		defer footer(e, w, r)

		f := getFilter(e, r)
		g, err := e.GroupByFilter(f)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
			return
		}

		data := struct {
			Query  template.URL
			Groups []subGroup
		}{
			template.URL(r.URL.RawQuery),
			subGroups(g),
		}

		err = e.Templates.ExecuteTemplate(w, "attainmentgroups.tmpl", data)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
		}
	}
}
// Search returns a student search page
func Search(e env.Env) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		if redir := checkRedirect(e, w, r, 0); redir {
			return
		}
		header(e, w, r, 0)
		defer footer(e, w, r)

		name := r.URL.Query().Get("name")

		f := getFilter(e, r)
		g, err := e.Search(name, f)

		data := struct {
			Query template.URL
			Name  string
			Group group.Group
		}{
			template.URL(r.URL.RawQuery),
			name,
			g,
		}

		err = e.Templates.ExecuteTemplate(w, "studentsearch.tmpl", data)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
		}
	}
}
func SubjectSpreadsheet(e env.Env, f database.Filter, subj *subject.Subject, w io.Writer) error {

	file := xlsx.NewFile()

	g, err := e.GroupByFilteredClass(strconv.Itoa(subj.SubjID), "", f)
	if err != nil {
		return err
	}

	sheet, err := file.AddSheet("Progress Grid")
	if err != nil {
		return err
	}
	subjectGrid(sheet, g, subj, f.NatYear)

	sheet, err = file.AddSheet("Students")
	if err != nil {
		return err
	}
	subjectBroadsheet(e, subj, sheet, g)

	sheet, err = file.AddSheet("Export Details")
	if err != nil {
		return err
	}
	exportInfoSheet(sheet, e, f)

	file.Write(w)
	return nil
}
// AttendanceExplorer provides a page for exploring the attendance figures
// in more detail, and examine individual students.
func AttendanceExplorer(e env.Env) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		if redir := checkRedirect(e, w, r, 2); redir {
			return
		}

		header(e, w, r, 2)
		defer footer(e, w, r)

		f := getFilter(e, r)
		g, err := e.GroupByFilter(f)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
			return
		}

		week, _ := e.CurrentWeek()

		data := struct {
			Query template.URL
			Week  string
			Group group.Group
		}{
			template.URL(r.URL.RawQuery),
			week,
			g,
		}

		err = e.Templates.ExecuteTemplate(w, "attendance.tmpl", data)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
		}
	}
}
// Index produces a landing page
func Index(e env.Env) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		if redir := checkRedirect(e, w, r, 0); redir {
			return
		}
		header(e, w, r, 0)
		defer footer(e, w, r)

		news, err := e.News()
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
		}

		data := struct {
			School string
			News   []database.NewsItem
		}{
			e.Config.School,
			news,
		}

		err = e.Templates.ExecuteTemplate(w, "index.tmpl", data)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
		}

	}
}
// Student produces a page for an individual student with all of their
// details/results/attendance etc.
func Student(e env.Env) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		if redir := checkRedirect(e, w, r, 0); redir {
			return
		}
		header(e, w, r, 0)
		defer footer(e, w, r)

		path := strings.Split(r.URL.Path, "/")
		if len(path) < 3 {
			fmt.Fprintf(w, "Error: Invalid path")
			return
		}
		upn := path[2]

		f := getFilter(e, r)
		s, err := e.Student(upn, f)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
		}

		data := struct {
			Student student.Student
		}{
			s,
		}

		err = e.Templates.ExecuteTemplate(w, "student.tmpl", data)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
		}
	}
}
func subjectGroupPage(e env.Env, w http.ResponseWriter, r *http.Request) {

	if redir := checkRedirect(e, w, r, 2); redir {
		return
	}
	header(e, w, r, 2)
	defer footer(e, w, r)

	path := strings.Split(r.URL.Path, "/")
	subjID, err := strconv.Atoi(path[3])
	if err != nil {
		fmt.Fprintf(w, "Error: %v", err)
		return
	}
	subj := e.Subjects[subjID]

	f := getFilter(e, r)
	g, err := e.GroupByFilteredClass(path[3], "", f)
	if err != nil {
		fmt.Fprintf(w, "Error: %v", err)
	}

	classes, err := e.Classes(path[3], f.Date)
	if err != nil {
		fmt.Fprintf(w, "Error: %v", err)
	}
	sort.Sort(sort.StringSlice(classes))

	clsGrps := []subGroup{}
	for _, c := range classes {
		grp := g.SubGroup(group.Class(subj.Subj, c))
		if len(grp.Students) > 0 {
			clsGrps = append(clsGrps, subGroup{c, template.URL(c), grp})
		}
	}

	data := struct {
		Query     template.URL
		Year      string
		Subj      *subject.Subject
		SubGroups []subGroup
		Matrix    subGroupMatrix
		Classes   []subGroup
	}{
		template.URL(r.URL.RawQuery),
		f.Year,
		subj,
		subGroups(g),
		groupMatrix(g),
		clsGrps,
	}

	err = e.Templates.ExecuteTemplate(w, "subjectgroups.tmpl", data)
	if err != nil {
		fmt.Fprintf(w, "Error: %v", err)
		return
	}

}
// EnglishAndMaths produces a summary page with the number/percentage
// of students achieving passes in English and/or Maths.
func EnglishAndMaths(e env.Env) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		if redir := checkRedirect(e, w, r, 2); redir {
			return
		}
		header(e, w, r, 2)
		defer footer(e, w, r)

		f := getFilter(e, r)
		g, err := e.GroupByFilter(f)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
			return
		}

		groups := []group.Group{{}, {}, {}, {}}
		for _, s := range g.Students {
			eng := s.EBaccArea("E").Achieved
			maths := s.EBaccArea("M").Achieved
			switch {
			case eng && maths:
				groups[2].Students = append(groups[2].Students, s)
			case eng:
				groups[0].Students = append(groups[0].Students, s)
			case maths:
				groups[1].Students = append(groups[1].Students, s)
			default:
				groups[3].Students = append(groups[3].Students, s)
			}
		}

		pcts := []float64{}
		for _, grp := range groups {
			pcts = append(pcts, float64(len(grp.Students))/float64(len(g.Students)))
		}

		data := struct {
			Query  template.URL
			Names  []string
			Groups []group.Group
			Pcts   []float64
		}{
			template.URL(r.URL.RawQuery),
			[]string{"English Only", "Mathematics Only", "English & Maths", "Neither"},
			groups,
			pcts,
		}

		err = e.Templates.ExecuteTemplate(w, "em.tmpl", data)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
		}
	}

}
// Performs analysis of the results
func progressGridPage(e env.Env, w http.ResponseWriter, r *http.Request) {

	if redir := checkRedirect(e, w, r, 2); redir {
		return
	}
	header(e, w, r, 2)
	defer footer(e, w, r)

	path := strings.Split(r.URL.Path, "/")
	subjID, err := strconv.Atoi(path[3])
	if err != nil {
		fmt.Fprintf(w, "Error: %v", err)
		return
	}
	subject := e.Subjects[subjID]
	class := path[4]
	if strings.HasPrefix(path[4], "All") {
		class = ""
	}

	f := getFilter(e, r)
	g, err := e.GroupByFilteredClass(path[3], class, f)
	if err != nil {
		fmt.Fprintf(w, "Error: %v", err)
	}

	data := struct {
		Query        template.URL
		Year         string
		Subject      string
		Level        string
		SubjID       string
		Class        string
		KS2Prior     string
		Group        group.Group
		ProgressGrid group.ProgressGrid
	}{
		template.URL(r.URL.RawQuery),
		f.Year,
		subject.Subj,
		subject.Lvl,
		path[3],
		path[4],
		subject.KS2Prior,
		g,
		g.ProgressGrid(subject, f.NatYear),
	}

	err = e.Templates.ExecuteTemplate(w, "progressgrid.tmpl", data)
	if err != nil {
		fmt.Fprintf(w, "Error: %v", err)
		return
	}

}
// AttendanceGroups produces a page with attendance summaries for the
// various student groups.
func AttendanceGroups(e env.Env) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		if redir := checkRedirect(e, w, r, 0); redir {
			return
		}
		header(e, w, r, 0)
		defer footer(e, w, r)

		f := getFilter(e, r)
		g, err := e.GroupByFilter(f)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
			return
		}

		type YearGroup struct {
			Name   string
			Query  template.URL
			Groups []subGroup
			Matrix subGroupMatrix
		}

		// Ignore error - will appear as blank string anyway
		week, _ := e.CurrentWeek()

		data := struct {
			Week       string
			Query      template.URL
			YearGroups []YearGroup
		}{
			week,
			template.URL(r.URL.RawQuery),
			[]YearGroup{{"All Years", template.URL(""), subGroups(g), groupMatrix(g)}},
		}

		for year := 7; year < 15; year++ {
			y := g.SubGroup(group.Year(year))
			if len(y.Students) == 0 {
				continue
			}
			yeargroup := YearGroup{fmt.Sprintf("Year %v", year),
				template.URL(fmt.Sprintf("&year=%v", year)),
				subGroups(y),
				groupMatrix(y)}
			data.YearGroups = append(data.YearGroups, yeargroup)

		}

		err = e.Templates.ExecuteTemplate(w, "attendancegroups.tmpl", data)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
		}
	}
}
// KS3Groups produces a group breakdown page for
func KS3Groups(e env.Env) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		if redir := checkRedirect(e, w, r, 1); redir {
			return
		}
		header(e, w, r, 1)
		defer footer(e, w, r)

		f := getFilter(e, r)
		g, err := e.GroupByFilter(f)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
			return
		}

		ks3Subjects := subject.List{}
		for _, s := range e.Subjects {
			if s.Lvl == "KS3" {
				ks3Subjects = append(ks3Subjects, *s)
			}
		}
		sort.Sort(ks3Subjects)

		data := struct {
			Query    template.URL
			Year     string
			Subjects subject.List
			Groups   []subGroup
			Matrix   subGroupMatrix
		}{
			template.URL(r.URL.RawQuery),
			f.Year,
			ks3Subjects,
			subGroups(g),
			groupMatrix(g),
		}

		err = e.Templates.ExecuteTemplate(w, "ks3groups.tmpl", data)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
		}
	}
}
// KS3Summary produces a page with the student
func KS3Summary(e env.Env) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		if redir := checkRedirect(e, w, r, 2); redir {
			return
		}
		header(e, w, r, 2)
		defer footer(e, w, r)

		f := getFilter(e, r)
		g, err := e.GroupByFilter(f)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
			return
		}

		ks3Subjects := subject.List{}
		var lvl subject.Level
		for _, s := range e.Subjects {
			if s.Lvl == "KS3" {
				lvl = s.Level
				ks3Subjects = append(ks3Subjects, *s)
			}
		}
		sort.Sort(ks3Subjects)

		data := struct {
			Query    template.URL
			Subjects subject.List
			KS3      subject.Level
			Group    group.Group
		}{
			template.URL(r.URL.RawQuery),
			ks3Subjects,
			lvl,
			g,
		}

		err = e.Templates.ExecuteTemplate(w, "ks3summary.tmpl", data)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
		}
	}
}
// FilterLabels generates the labels for the filter page
func filterLabels(e env.Env, f database.Filter) []label {

	labels := []label{}

	// Lookup date and resultset names
	// Lookup date and resultset names
	date, _ := e.LookupDate(f.Date)
	rs, _ := e.LookupResultset(f.Resultset)
	nat, _ := e.LookupNatYear(f.NatYear)

	labels = append(labels, label{nat, "default"})
	labels = append(labels, label{date, "primary"})
	labels = append(labels, label{rs, "primary"})

	if f.Year != "" {
		labels = append(labels, label{"Yeargroup: " + f.Year, "success"})
	}

	switch f.Gender {
	case "1":
		labels = append(labels, label{"Boys", "warning"})
	case "0":
		labels = append(labels, label{"Girls", "warning"})
	}

	switch f.PP {
	case "1":
		labels = append(labels, label{"Disadvantaged", "warning"})
	case "0":
		labels = append(labels, label{"Non-Disadvantaged", "warning"})
	}

	switch f.EAL {
	case "1":
		labels = append(labels, label{"EAL", "warning"})
	case "0":
		labels = append(labels, label{"Non-EAL", "warning"})
	}

	if len(f.SEN) >= 1 && len(f.SEN) < 4 {
		labels = append(labels, label{"SEN: " + strings.Join(f.SEN, ", "), "warning"})
	}

	if len(f.KS2Bands) >= 1 && len(f.SEN) < 4 {
		labels = append(labels, label{"KS2: " + strings.Join(f.KS2Bands, ", "), "warning"})
	}

	if len(f.Ethnicities) >= 1 && len(f.Ethnicities) <= len(e.Ethnicities) {
		eths := []string{}
		for _, eth := range f.Ethnicities {
			if !e.OtherEths[eth] {
				eths = append(eths, eth)
			}
		}
		labels = append(labels, label{"Ethnicity: " + strings.Join(eths, ", "), "warning"})
	}

	return labels
}
func subjectBroadsheet(e env.Env, subj *subject.Subject, sheet *xlsx.Sheet, g group.Group) error {

	// Get a list of all resultsets for historical data
	resultsets := e.Resultsets

	// Create set of maps, keyed by resultsetID, then UPN
	historical := map[string](map[string]string){}
	for _, rs := range resultsets {
		historical[rs.ID] = map[string]string{}
	}

	// Load all historical data from the database
	for _, s := range g.Students {
		grds, err := e.HistoricalResults(s.UPN, subj.SubjID)
		switch err {
		case nil:
			for rs, grd := range grds {
				historical[rs][s.UPN] = grd
			}
		case sql.ErrNoRows:
			continue
		default:
			return err
		}
	}

	// Create set of empty resultsets
	empty := map[string]bool{}
	for rs, results := range historical {
		if len(results) == 0 {
			empty[rs] = true
		}
	}

	// Write headers to the sheet
	row := sheet.AddRow()
	row.SetHeightCM(4.5)
	newCell(row, "Name", newStyle("Bold", "None", "Bottom", "Left"))
	newCell(row, "Class", newStyle("Bold", "None", "Bottom", "Left"))

	headers := []string{"Gender", "PP", "KS2", "SEN", "Grade",
		"Effort", "VA", "Attendance"}
	for _, h := range headers {
		newCell(row, h, newStyle("Bold", "None", "Bottom", "Center"))
	}

	// Add historical resultsets to the headers
	for _, rs := range resultsets {
		if !empty[rs.ID] {
			newCell(row, rs.Name, newStyle("Bold", "None", "Bottom", "Vertical"))
		}
	}

	for _, h := range []string{"Barriers to Learning", "Intervention"} {
		newCell(row, h, newStyle("Bold", "None", "Bottom", "Center"))
	}

	// Add Student data
	for _, s := range g.Students {
		row := sheet.AddRow()
		newCell(row, s.Name(), newStyle("Default", "None", "None", "Left"))
		newCell(row, s.Class(subj.Subj), newStyle("Default", "None", "None", "Left"))
		newCell(row, s.Gender.String(), newStyle("Default", "None", "None", "Center"))
		newBool(row, s.PP, newStyle("Default", "None", "None", "Center"))
		newCell(row, s.KS2.Score(subj.KS2Prior), newStyle("Default", "None", "None", "Center"))
		newCell(row, s.SEN.Status, newStyle("Default", "None", "None", "Center"))
		newCell(row, s.SubjectGrade(subj.Subj), newStyle("Default", "None", "None", "Center"))
		newCell(row, s.SubjectEffort(subj.Subj), newStyle("Default", "None", "None", "Center"))
		newFloat(row, s.SubjectVA(subj.Subj).Score(), "+0.00;-0.00;0.00", newStyle("Default", "None", "None", "Center"))
		newFloat(row, s.Attendance.Latest(), "0.0%", newStyle("Default", "None", "None", "Center"))
		for _, rs := range resultsets {
			if !empty[rs.ID] {
				grd, _ := historical[rs.ID][s.UPN]
				newCell(row, grd, newStyle("Default", "None", "None", "Center"))
			}
		}

	}

	return nil
}
// Progress8 returns a handler to produce the Progress 8 page.
func Progress8(e env.Env) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		if redir := checkRedirect(e, w, r, 2); redir {
			return
		}
		header(e, w, r, 2)
		defer footer(e, w, r)

		f := getFilter(e, r)
		g, err := e.GroupByFilter(f)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
			return
		}

		nat, exists := e.Attainment8[f.NatYear]
		if !exists {
			fmt.Fprintf(w, "Error: %v", err)
		}

		natLine := points{}
		for ks2, att8 := range nat {
			n, err := strconv.ParseFloat(ks2, 64)
			if err != nil {
				fmt.Fprintf(w, "Error: %v", err)
			}
			natLine = append(natLine, point{X: n, Y: att8.Overall})
		}
		sort.Sort(natLine)

		pupilData := [4]points{}
		for _, s := range g.Students {

			p8 := s.Basket().Overall()
			var key int
			switch {
			case s.PP && s.Gender == 1:
				key = 0
			case s.Gender == 1:
				key = 1
			case s.PP && s.Gender == 0:
				key = 2
			case s.Gender == 0:
				key = 3
			}

			pupilData[key] = append(pupilData[key], point{X: s.KS2.APS / 6,
				Y:    p8.Attainment,
				Name: s.Name(),
				P8:   p8})
		}

		data := struct {
			Query     template.URL
			Group     group.Group
			NatLine   points
			PupilData [4]points
		}{
			template.URL(r.URL.RawQuery),
			g,
			natLine,
			pupilData,
		}

		err = e.Templates.ExecuteTemplate(w, "progress8.tmpl", data)
		if err != nil {
			fmt.Fprintf(w, "Error: %v", err)
		}
	}
}