package applications

import (
	"git.openprivacy.ca/cwtch.im/tapir"
)

// ApplicationChain is a meta-app that can be used to build complex applications from other applications
type ApplicationChain struct {
	TranscriptApp
	apps         []tapir.Application
	endapp       tapir.InteractiveApplication
	capabilities []tapir.Capability
}

// ChainApplication adds a new application to the chain. Returns a pointer to app so this call
// can itself be chained.
func (appchain *ApplicationChain) ChainApplication(app tapir.Application, capability tapir.Capability) *ApplicationChain {
	appchain.apps = append(appchain.apps, app.NewInstance())
	appchain.capabilities = append(appchain.capabilities, capability)
	return appchain
}

// ChainInteractiveApplication adds an interactive application to the chain. There can only be 1 interactive application.
func (appchain *ApplicationChain) ChainInteractiveApplication(app tapir.InteractiveApplication) *ApplicationChain {
	appchain.endapp = app
	return appchain
}

// NewInstance should always return a new instantiation of the application.
func (appchain *ApplicationChain) NewInstance() tapir.Application {
	applicationChain := new(ApplicationChain)
	for _, app := range appchain.apps {
		applicationChain.apps = append(applicationChain.apps, app.NewInstance())
	}
	applicationChain.capabilities = appchain.capabilities
	return applicationChain
}

// Init is run when the connection is first started.
func (appchain *ApplicationChain) Init(connection tapir.Connection) {
	appchain.TranscriptApp.Init(connection)
	for i, app := range appchain.apps {
		// propagate the transcript to the app
		app.PropagateTranscript(appchain.transcript)
		// apply the app to the connection
		connection.SetApp(app)
		// initialize the application given the connection
		app.Init(connection)
		// if we hit our guard then carry on, otherwise close...
		if !connection.HasCapability(appchain.capabilities[i]) {
			connection.Close()
			return
		}
	}
}

// Listen calls listen on the Interactive application
func (appchain *ApplicationChain) Listen() {
	if appchain.endapp != nil {
		appchain.endapp.Listen()
	}
}
