<commit_before>package ui

import (
	"errors"
	"fmt"
	"sort"
	"sync"

	"github.com/draganm/go-reactor"
	"github.com/netice9/apparatchik/core"
)

type Application struct {
	sync.Mutex
	ctx       reactor.ScreenContext
	app       *core.Application
	alert     error
	status    core.ApplicationStatus
	showModal bool
}

func ApplicationFactory(ctx reactor.ScreenContext) reactor.Screen {
	appName := ctx.Params["application"]

	app, err := core.ApparatchikInstance.GetApplicationByName(appName)

	if err != nil {
		return reactor.DefaultNotFoundScreenFactory(ctx)
	}

	return &Application{
		ctx: ctx,
		app: app,
	}
}

func (a *Application) Mount() {
	a.app.On("update", a.onUpdate)
	a.app.On("terminated", a.onTerminated)
	a.onUpdate(a.app.Status())
}

func (a *Application) OnUserEvent(evt *reactor.UserEvent) {
	a.Lock()
	defer a.Unlock()

	switch evt.ElementID {
	case "deleteButton":
		a.showModal = true
	case "deleteCancelButton":
		a.showModal = false
	case "deleteConfirmButton":
		a.showModal = false
		a.alert = errors.New("Deleting application.")

		err := core.ApparatchikInstance.TerminateApplication(a.app.Name)
		if err != nil {
			a.alert = err
		}

	}
	a.render()

}

func (a *Application) onUpdate(status core.ApplicationStatus) {
	a.Lock()
	defer a.Unlock()
	a.status = status
	a.render()
}

func (a *Application) onTerminated() {
	location := "#/"
	a.ctx.UpdateScreen(&reactor.DisplayUpdate{
		Location: &location,
	})
}

func (a *Application) render() {
	view := applicationUI.DeepCopy()
	view.SetElementAttribute("app_panel", "header", a.status.Name)
	view.SetElementText("main_goal", a.app.MainGoal)

	goalNames := []string{}

	for goalName := range a.status.Goals {
		goalNames = append(goalNames, goalName)
	}

	sort.Strings(goalNames)

	for _, name := range goalNames {
		goal := a.status.Goals[name]
		row := goalRowUI.DeepCopy()
		row.SetElementText("goal_name", name)
		row.SetElementAttribute("goal_name", "href", fmt.Sprintf("#/apps/%s/%s", a.app.Name, goal.Name))
		row.SetElementAttribute("goal_term_link", "href", fmt.Sprintf("#/apps/%s/%s/xterm", a.app.Name, goal.Name))
		row.SetElementText("goal_state", goal.Status)
		view.AppendChild("goal_table_body", row)
	}

	if a.alert != nil {
		view.SetElementText("alert", a.alert.Error())
	} else {
		view.DeleteChild("alert")
	}

	view.SetElementAttribute("delete_confirm_modal", "show", a.showModal)
	view.SetElementText("application_name", a.app.Name)

	a.ctx.UpdateScreen(&reactor.DisplayUpdate{
		Model: WithNavigation(view, [][]string{{"Applications", "#/"}, {a.app.Name, fmt.Sprintf("#/apps/%s", a.app.Name)}}),
	})
}

func (a *Application) Unmount() {
	a.app.RemoveListener("update", a.onUpdate)
	a.app.RemoveListener("terminated", a.onUpdate)
}

var goalRowUI = reactor.MustParseDisplayModel(`
  <tr id="row">
    <td ><a id="goal_name" href="#" className="btn btn-default"/></td>
    <td id="goal_state" />
    <td id="goal_actions">
			<a id="goal_term_link" href="#" className="btn btn-default">XTerm</a>
		</td>
  </tr>
`)
var applicationUI = reactor.MustParseDisplayModel(`
  <div>
    <bs.Panel id="app_panel" header="">
      <bs.Alert id="alert" bsStyle="danger"/>
      <dl>
        <dt>Main Goal</dt>
        <dd id="main_goal"></dd>
      </dl>
      <dl>
        <dt>Goals</dt>
        <dd>
          <bs.Table bool:striped="true" bool:bordered="true" bool:condensed="true" bool:hover="true">
            <thead>
              <tr>
                <th>Name</th>
                <th>State</th>
                <th>Actions</th>
              </tr>
            </thead>
            <tbody id="goal_table_body" /><commit_msg>fixed application status screen blocking<commit_after>package ui

import (
	"errors"
	"fmt"
	"sort"
	"sync"

	"github.com/draganm/go-reactor"
	"github.com/netice9/apparatchik/core"
)

type Application struct {
	sync.Mutex
	ctx       reactor.ScreenContext
	app       *core.Application
	alert     error
	status    core.ApplicationStatus
	showModal bool
}

func ApplicationFactory(ctx reactor.ScreenContext) reactor.Screen {
	appName := ctx.Params["application"]

	app, err := core.ApparatchikInstance.GetApplicationByName(appName)

	if err != nil {
		return reactor.DefaultNotFoundScreenFactory(ctx)
	}

	return &Application{
		ctx: ctx,
		app: app,
	}
}

func (a *Application) Mount() {
	a.app.On("update", a.onUpdate)
	a.app.On("terminated", a.onTerminated)
	a.onUpdate(a.app.Status())
}

func (a *Application) OnUserEvent(evt *reactor.UserEvent) {
	a.Lock()
	defer a.Unlock()

	switch evt.ElementID {
	case "deleteButton":
		a.showModal = true
	case "deleteCancelButton":
		a.showModal = false
	case "deleteConfirmButton":
		a.showModal = false
		a.alert = errors.New("Deleting application.")

		err := core.ApparatchikInstance.TerminateApplication(a.app.Name)
		if err != nil {
			a.alert = err
		}

	}
	a.render()

}

func (a *Application) onUpdate(status core.ApplicationStatus) {
	a.Lock()
	defer a.Unlock()
	a.status = status
	a.render()
}

func (a *Application) onTerminated() {
	location := "#/"
	a.ctx.UpdateScreen(&reactor.DisplayUpdate{
		Location: &location,
	})
}

func (a *Application) render() {
	view := applicationUI.DeepCopy()
	view.SetElementAttribute("app_panel", "header", a.status.Name)
	view.SetElementText("main_goal", a.app.MainGoal)

	goalNames := []string{}

	for goalName := range a.status.Goals {
		goalNames = append(goalNames, goalName)
	}

	sort.Strings(goalNames)

	for _, name := range goalNames {
		goal := a.status.Goals[name]
		row := goalRowUI.DeepCopy()
		row.SetElementText("goal_name", name)
		row.SetElementAttribute("goal_name", "href", fmt.Sprintf("#/apps/%s/%s", a.app.Name, goal.Name))
		row.SetElementAttribute("goal_term_link", "href", fmt.Sprintf("#/apps/%s/%s/xterm", a.app.Name, goal.Name))
		row.SetElementText("goal_state", goal.Status)
		view.AppendChild("goal_table_body", row)
	}

	if a.alert != nil {
		view.SetElementText("alert", a.alert.Error())
	} else {
		view.DeleteChild("alert")
	}

	view.SetElementAttribute("delete_confirm_modal", "show", a.showModal)
	view.SetElementText("application_name", a.app.Name)

	a.ctx.UpdateScreen(&reactor.DisplayUpdate{
		Model: WithNavigation(view, [][]string{{"Applications", "#/"}, {a.app.Name, fmt.Sprintf("#/apps/%s", a.app.Name)}}),
	})
}

func (a *Application) Unmount() {
	a.app.RemoveListener("update", a.onUpdate)
	a.app.RemoveListener("terminated", a.onTerminated)
}

var goalRowUI = reactor.MustParseDisplayModel(`
  <tr id="row">
    <td ><a id="goal_name" href="#" className="btn btn-default"/></td>
    <td id="goal_state" />
    <td id="goal_actions">
			<a id="goal_term_link" href="#" className="btn btn-default">XTerm</a>
		</td>
  </tr>
`)
var applicationUI = reactor.MustParseDisplayModel(`
  <div>
    <bs.Panel id="app_panel" header="">
      <bs.Alert id="alert" bsStyle="danger"/>
      <dl>
        <dt>Main Goal</dt>
        <dd id="main_goal"></dd>
      </dl>
      <dl>
        <dt>Goals</dt>
        <dd>
          <bs.Table bool:striped="true" bool:bordered="true" bool:condensed="true" bool:hover="true">
            <thead>
              <tr>
                <th>Name</th>
                <th>State</th>
                <th>Actions</th>
              </tr>
            </thead>
            <tbody id="goal_table_body" />
{'fraction_non_alphanumeric': 0.16696465410535002, 'fraction_numerical': 0.00027506532801540364, 'mean_word_length': 3.6466453674121406, 'pattern_counts': {'":': 6, '<': 73, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5777107', 'n_tokens_mistral': 2700, 'n_tokens_neox': 2562, 'n_words': 466}